【文章标题】: WINDBG+IDA对某木马手脱nSPACK+LOADER部分分析
【文章作者】: 笨笨雄
【软件名称】: 灰鸽子
【下载地址】: 这东西,还是不放出来了
【使用工具】: WINDBG IDA LORDPE IMPORTREC FILEMON REGMON PEID VMWARE
【工具介绍】: WINDBG是微软出的调试器,R0下的,并且能双机调试。缺点是反汇编功能简单。IDA静态分析功能强大,缺点是动态调试功能简单。
【作者声明】: 论坛限制大,混个精华好办事,水平低,只好来新手区碰碰运气。刚啃完debugging tools for windows帮助文件里的debugger reference,写篇文章简单介绍WINDBG的使用。

----------------------------------------------------------------------

打开FILEMON跟REGMON,排除无关进程后运行木马LOADER。得知此木马通过在系统目录SYSTEM32下释放名为SERVER的文件并注册为服务作恶。

在已被感染的情况下,再次运行木马LOADER,可在FILEMON看到,先是确认SERVER的存在,然后设置文件属性,之后程序就退出了。

我猜在系统目录下建立一个假的SERVER,就对木马免疫了。(VMWARE这款虚拟机很方便还原快照,又回到无毒的环境了)

试了一下,失败了。假的SERVER被删了,然后重新建立SERVER。究竟是怎么分辨真假的?特征码识别?还是文件大小识别?为了找出对这一木马(特指这个文件,显然文件名可以随便改)的免疫方法,下面进行汇编级的分析。

----------------------------------脱壳--------------------------------

PEID查得nSPack 2.1 - 2.5 -> North Star/Liu Xing Ping [Overlay]

用WINDBG打开木马的LOADER

g $exentry    

来到入口点。g命令是运行。$exentry是伪寄存器,调试器载入程序之后$exentry=EP

ba w4 0012ffc0

设置内存断点,ba代表内存断点,W代表写入,4代表4个字节,这里用ESP定律找OEP

连续g几次之后,来到下面,Access violation(非法访问)

eax=00000087 ebx=7ffde030 ecx=00010102 edx=ffffffff esi=00000000 edi=00084d99
eip=13203208 esp=0012ffa4 ebp=0012fff0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000202
image13140000+0xc3208:
13203208 0000            add     byte ptr [eax],al          ds:0023:00000087=??

看来是ANTI-DEBUG。向上滚动命令行窗口,找到最后一次能正确中断的地方

eax=00000000 ebx=7ffdf000 ecx=00010101 edx=ffffffff esi=00000000 edi=00084d98
eip=13203107 esp=0012ffa4 ebp=0012fff0 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000206
image13140000+0xc3107:
13203107 e8fffeffff      call    image13140000+0xc300b (1320300b)

CRTL+SHIFT+F5重新开始,g $exentry来到入口点后ba e1 13203107

e代表运行,1代表字节,代表指令的长度(非操作数)。

g就可以直接断在13203107了,按F8跟进,ALT+7打开反汇编窗口

1320300b b818000000      mov     eax,18h
13203010 648b18          mov     ebx,dword ptr fs:[eax]
13203013 83c330          add     ebx,30h
13203016 c3              ret

[fs:[18h] + 2]是PEB.BeingDebugged 标志,即7ffdf002,1代表被调试,0代表没有被调试

f 7ffdf002 l1 0

f代表写内存,l1代表长度为1个字节,后面是需要写进内存的数据

ba w4 0012ffc0

g,断在1314cb1b。

1314cb1a 55              push    ebp
1314cb1b 8bec            mov     ebp,esp

这就是入口点了。

用LORDPE完整DUMP下来。运行IMPORTREC,修改入OEP为CB1A,自动查找IAT,结果没多少是可用的。右键菜单->高级命令->获取API调用。最小地址13140000,最大地址13150000。确定,现在可以看到很多无效的,也看到很多有效的,IAT似乎没加密。删掉无效的有点麻烦,没有删除的快捷键:(

按住SHIFT键选中所有无效的,右键菜单->删除指针,现在修复转存文件就OK了。

本来想介绍WINDBG本身的.dump命令,原文件就700K左右,可是DUMP下来居然有7M,不知道DUMP多了多少对于脱壳没意义的数据。用LORDPE DUMP下来的只有1M左右,经测试,至少可以顺利运行到感染你的机器了。

--------------------------------脱壳完毕------------------------------

关于下断的问题,我是直接查输入表,把所有跟文件操作有关的API都下了断。为了方便阅读,我把一些没意义的过程省略了。

用WINDBG打开已经脱壳的程序。g $exentry来到入口点
bp findfirstfilea下断,不分大小写,太好了,这在OLLYDBG可是不行的!
输入G,断在系统区域,ALT+6查看CALL STACK(调用栈)

00 0012fdf8 131463ce 00b91a1c 0012fe0c 00b91a1c kernel32!FindFirstFileA

CALL指令执行的时候会把返回地址压栈,CALL里面的第一条指令一般是PUSH EBP。所以堆栈段地址0012FXXX(XXX为任意数字)后一个数值便是返回地址了。在这个例子中131463ce 返回地址  00b91a1c 函数左边第一个参数

db 00b91a1c查看内存

00b91a1c  43 3a 5c 57 49 4e 4e 54-5c 73 79 73 74 65 6d 33  C:\WINNT\system3
00b91a2c  32 5c 73 65 72 76 72 65-00 00 00 00 16 00 00 00  2\servre........

一开始就断在正确的位置了,运气不错。用IDA打开程序,按G,输入131463ce,跳到下面sub_131463AE中。sub表示一个CALL

--------------------------sub_131463AE分析,可不看,直接跳到下一个分割线------------------------------

.nsp0:131463AE sub_131463AE    proc near               ; CODE XREF: sub_13146416+5p
.nsp0:131463AE
.nsp0:131463AE FileTime        = FILETIME ptr -14Ch
.nsp0:131463AE LocalFileTime   = _FILETIME ptr -0Ch
.nsp0:131463AE FatTime         = word ptr -4

高级语言中的函数变量在堆栈中存在,EBP为基址加上偏移访问堆栈获得变量,为了方便阅读IDA分别为偏移命名

.nsp0:131463AE                 push    ebp
.nsp0:131463AF                 mov     ebp, esp
.nsp0:131463B1                 add     esp, 0FFFFFEB4h 
.nsp0:131463B7                 push    ebx
.nsp0:131463B8                 mov     ebx, eax ;把EAX的值暂存EBX
.nsp0:131463BA                 lea     eax, [ebp+FileTime]
.nsp0:131463C0                 push    eax             ; lpFindFileData
.nsp0:131463C1                 mov     eax, ebx ;取回原来的值   
.nsp0:131463C3                 call    sub_1314414E  

用EAX作为传入传出参数,估计此CALL作用为对EAX的值修正,可不跟进

.nsp0:131463C8                 push    eax             ; lpFileName
.nsp0:131463C9                 call    FindFirstFileA_0 ;查找文件
.nsp0:131463C9
.nsp0:131463CE                 cmp     eax, 0FFFFFFFFh  ;比较是否为-1,此处返回-1表示操作失败
.nsp0:131463D1                 jz      short loc_13146407 ;跳到函数最后
.nsp0:131463D1
.nsp0:131463D3                 push    eax             ; hFindFile
.nsp0:131463D4                 call    FindClose_0  ;结束查找
.nsp0:131463D4
.nsp0:131463D9                 test    byte ptr [ebp+FileTime.dwLowDateTime], 10h
.nsp0:131463E0                 jnz     short loc_13146407 
.nsp0:131463E2                 lea     eax, [ebp+LocalFileTime]
.nsp0:131463E5                 push    eax             ; lpLocalFileTime
.nsp0:131463E6                 lea     eax, [ebp-138h]
.nsp0:131463EC                 push    eax             ; lpFileTime
.nsp0:131463ED                 call    FileTimeToLocalFileTime ;转换文件时间的API
.nsp0:131463ED
.nsp0:131463F2                 lea     eax, [ebp+FatTime]
.nsp0:131463F5                 push    eax             ; lpFatTime
.nsp0:131463F6                 lea     eax, [ebp-2]
.nsp0:131463F9                 push    eax             ; lpFatDate
.nsp0:131463FA                 lea     eax, [ebp+LocalFileTime]
.nsp0:131463FD                 push    eax             ; lpFileTime
.nsp0:131463FE                 call    FileTimeToDosDateTime ;同上
.nsp0:131463FE
.nsp0:13146403                 test    eax, eax
.nsp0:13146405                 jnz     short loc_1314640E
.nsp0:13146405
.nsp0:13146407
.nsp0:13146407 loc_13146407:                           ; CODE XREF: sub_131463AE+23j
.nsp0:13146407                                         ; sub_131463AE+32j
.nsp0:13146407                 mov     dword ptr [ebp+FatTime], 0FFFFFFFFh ;返回-1
.nsp0:13146407
.nsp0:1314640E
.nsp0:1314640E loc_1314640E:                           ; CODE XREF: sub_131463AE+57j
.nsp0:1314640E                 mov     eax, dword ptr [ebp+FatTime]
.nsp0:13146411                 pop     ebx
.nsp0:13146412                 mov     esp, ebp ;恢复堆栈
.nsp0:13146414                 pop     ebp
.nsp0:13146415                 retn
.nsp0:13146415
.nsp0:13146415 sub_131463AE    endp

 -----------------------------sub_131463AE分析完毕----------------------------------

sub_131463AE功能:
1 寻找C:\WINNT\system32\servre文件,成功则对文件时间作修正
2 成功EAX=FatTime,失败EAX=-1

现在我们需要知道哪里调用此CALL

返回WINDBG,使用gu回到程序代码,再使用一次gu跳出此CALL

经过前面的分析之后,也可以用dd ebp显示堆栈信息(dd跟db都是内存显示的命令,只是显示格式不同,根据需要选择命令)

0012ff58  0012ffc0 13146420 7ffdf000 1314cbc4
0012ff68  0012ff74 1314cc62 0012ffc0 0012ffb4
0012ff78  1314ce18 0012ffc0 000862c8 00000000

13146420就是返回地址了

IDA按G,输入13146420,来到sub_13146416

------------------sub_13146416分析,可不看,直接跳到下一个分割线----------------------

.nsp0:13146416 sub_13146416    proc near               ; CODE XREF: sub_1314B9BE+59p
.nsp0:13146416                                         ; sub_1314C6DE+C2p
.nsp0:13146416                                         ; sub_1314C6DE+FAp
.nsp0:13146416                                         ; .nsp0:1314CBBFp
.nsp0:13146416                                         ; .nsp0:1314CBE3p
.nsp0:13146416                                         ; .nsp0:1314CD57p ...
.nsp0:13146416                 push    ebx      
.nsp0:13146417                 mov     ebx, eax ;由sub_131463AE分析可知
.nsp0:13146419                 mov     eax, ebx ;这两条指令相当于NOP
.nsp0:1314641B                 call    sub_131463AE
.nsp0:1314641B
.nsp0:13146420                 inc     eax ;假如EAX返回-1,EAX为0
.nsp0:13146421                 setnz   al  ;假如不为0,al被置为1
.nsp0:13146424                 pop     ebx
.nsp0:13146425                 retn
.nsp0:13146425
.nsp0:13146425 sub_13146416    endp

 -----------------------------sub_13146416分析完毕----------------------------------

sub_13146416功能: 修正为成功EAX=1,失败EAX=0

经分析可知1314cbc4是更上一层的返回地址

IDA按G,输入1314cbc4,来到入口函数,sub_13146416是loc_1314CBAC内调用的。

.nsp0:1314CBAC loc_1314CBAC:                           ; CODE XREF: .nsp0:1314CB9Ej
.nsp0:1314CBAC                 xor     eax, eax ;EAX为0
.nsp0:1314CBAE                 push    ebp
.nsp0:1314CBAF                 push    offset s_SoiSjkSJI ; "閛i\xFF\xFF鐹k\xFF\xFF桉哱xFF\xFF?
.nsp0:1314CBB4                 push    dword ptr fs:[eax] ;保存上一个SEH处理函数
.nsp0:1314CBB7                 mov     fs:[eax], esp      ;指向SEH处理函数
.nsp0:1314CBBA                 mov     eax, dword_1314E964
.nsp0:1314CBBF                 call    sub_13146416    ;搜索SERVER
.nsp0:1314CBC4                 test    al, al
.nsp0:1314CBC6                 jz      short loc_1314CC00 ;搜索不到就跳
.nsp0:1314CBC8                 xor     edx, edx
.nsp0:1314CBCA                 mov     eax, dword_1314E964
.nsp0:1314CBCF                 call    sub_1314644A ;设置文件属性为普通
.nsp0:1314CBD4                 mov     eax, dword_1314E964
.nsp0:1314CBD9                 call    sub_13146472 ;删除文件
.nsp0:1314CBDE                 mov     eax, dword_1314E964
.nsp0:1314CBE3                 call    sub_13146416 ;再搜索SERVER
.nsp0:1314CBE8                 test    al, al
.nsp0:1314CBEA                 jz      short loc_1314CC00 ;搜索不到就跳
.nsp0:1314CBEC                 push    0
.nsp0:1314CBEE                 call    ExitProcess_0

注:sub_1314644A和sub_13146472都是直接调用SetFileAttributesA和DeleteFileA。没有什么好分析的

mov eax, dword_1314E964然后是CALL,出现次数频繁,现在看看那是什么

回到windbg,使用dd 1314E964

1314e964  00b91a1c 01010100 00000000 00b918ec
1314e974  00b918d8 00b91900 00000001 00000000
1314e984  00000000 00000000 00000000 00000000

还记得00b91a1c吗?findfirstfilea的第一个参数“C:\WINNT\system32\servre”的首个地址。

现在来看看loc_1314CC00,看到COPYFILEA

.nsp0:1314CC00 loc_1314CC00:                           ; CODE XREF: .nsp0:1314CBC6j
.nsp0:1314CC00                                         ; .nsp0:1314CBEAj
.nsp0:1314CC00                 lea     edx, [ebp-18h]
.nsp0:1314CC03                 mov     eax, dword_1314E964
.nsp0:1314CC08                 call    sub_131464DA
.nsp0:1314CC08
.nsp0:1314CC0D                 mov     edx, [ebp-18h]
.nsp0:1314CC10                 mov     eax, offset dword_1314E9C4
.nsp0:1314CC15                 call    sub_13143D2E
.nsp0:1314CC15
.nsp0:1314CC1A                 mov     eax, dword_1314E9C4
.nsp0:1314CC1F                 call    sub_1314B4A2
.nsp0:1314CC1F
.nsp0:1314CC24                 push    0
.nsp0:1314CC26                 mov     eax, dword_1314E964
.nsp0:1314CC2B                 call    sub_1314414E
.nsp0:1314CC2B
.nsp0:1314CC30                 push    eax
.nsp0:1314CC31                 lea     edx, [ebp-1Ch]
.nsp0:1314CC34                 xor     eax, eax
.nsp0:1314CC36                 call    sub_13142856
.nsp0:1314CC36
.nsp0:1314CC3B                 mov     eax, [ebp-1Ch]
.nsp0:1314CC3E                 call    sub_1314414E
.nsp0:1314CC3E
.nsp0:1314CC43                 push    eax
.nsp0:1314CC44                 call    CopyFileA

返回WINDBG,输入g 1314CC44来到call CopyFileA,F8跟进

查看CALL STACK

00 0012ff54 1314cc49 00b91a78 00b91a1c 00000000 kernel32!CopyFileA

db 00b91a78

00b91a78  44 3a 5c b9 a4 d7 f7 5c-75 6e 70 61 63 6b 2e 65  D:\....\unpack.e
00b91a88  78 65 00 00 00 00 00 00-00 00 00 00 00 00 00 00  xe..............

db 00b91a1c

00b91a1c  43 3a 5c 57 49 4e 4e 54-5c 73 79 73 74 65 6d 33  C:\WINNT\system3
00b91a2c  32 5c 73 65 72 76 72 65-00 00 00 00 16 00 00 00  2\servre........

windbg中文显示不太好...中文目录名显示不出来

总结:由分析可知,程序先检查SERVER是否存在,存在则尝试删除该文件,删除失败则退出,本来就不存在或者删除成功就把自己复制到系统目录并改名字。