快雪时晴 发表于 2005-8-14 21:33:37

【原创】ESP定律搞定KEN QQServer2.0 QQ挂机程序脱壳

KEN QQServer.exe2.0   QQ挂机服务端程序

今天QQ群上一个老弟传给我个据称是挂QQ的服务端程序,说不知是什么壳。我打肿脸充胖子,对于手动脱壳我完全是个生手。
同时冒着是木马/病毒的风险,硬接过来,还好,一切顺利。

   
server_.exe    为脱壳后
server__.exe    为脱壳后修复

参照pediy论坛《【原创】WinUpack 0.29 beta主程序脱壳》一文中cater提供的方法可以迅速找到OEP.


&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&——————
后来我自己根据ESP定律脱壳的过程:
用PeID093查不出是什么壳Nothing *,用STUD_PEv2.101看PE结构:
                        Press to close this window!
No| Name      | VSize      | VOffset    | RSize      | ROffset    | Charact.   |
01| .Upack    | 0000A000   | 00001000   | 000001CC   | 00000020   | E0000060   |
02| .rsrc   | 0000A000   | 0000B000   | 0000280D   | 00000200   | E0000060   |
一个Upack段,再看PE文件头:
MZKERNEL32.DLL...LoadLibraryA...GetProcAddress.[[email protected]..
上google查了下,初步确定是Dwing的(Win)Upack壳,一个纯压缩壳,版本未知。当然也不排除伪造壳
特征的可能。对于未知壳,我也不知用什么自动脱壳工具好,只好手工脱壳了。

用OLLYDBG110加载Server.exe,来到:
00401030 S> $- E9 5BC50000                  jmp Server.0040D590
00401035    .42 79 44 77 69 6E 67 40 00   ascii "ByDwing@",0
0040103E       00                           db 00
0040103F       00                           db 00
00401040    .50 45 00                     ascii "PE",0
注意此时的堆栈寄存器ESP
EAX 00000000
ECX 00000101
EDX FFFFFFFF
EBX 7FFDF000
ESP 0012FFC4
EBP 0012FFF0
ESI 00000000
EDI 00000000
EIP 00401030 Server.<ModuleEntryPoint>

运用ESP定律(一开始也不知道管不管用,反正试试看),在ESP+4处0012FFC0设置硬件读中断HR,
F9,中断于:
0040D5A4   97                xchg eax,edi                           ; Server.0041482C
0040D5A5   51                push ecx
0040D5A6   58                pop eax
0040D5A7   8D5485 5C         lea edx,dword ptr ss:
0040D5AB   FF16            call dword ptr ds:

edi=0041482C (Server.0041482C)
eax=00401000 (Server.00401000), ASCII "MZKERNEL32.DLL"

再F9两次,来到:
0040D738   85C0            test eax,eax
0040D73A   - 0F84 946EFFFF   je Server.004045D4                      ;其实,这里的长跳转地址Server.004045D4就是OEP了
0040D740   56                push esi
0040D741   97                xchg eax,edi                           ; Server.00404972,硬件读中断于此
0040D742   FF53 FC         call dword ptr ds:
0040D745   95                xchg eax,ebp
0040D746   AC                lods byte ptr ds:
0040D747   84C0            test al,al
0040D749   ^ 75 FB             jnz short Server.0040D746
0040D74B   3806            cmp byte ptr ds:,al
0040D74D   ^ 74 E7             je short Server.0040D736
0040D74F   8BC6            mov eax,esi
0040D751   79 05             jns short Server.0040D758
0040D753   46                inc esi
0040D754   33C0            xor eax,eax
0040D756   66:AD             lods word ptr ds:
0040D758   50                push eax
0040D759   55                push ebp                                      ; 以后将不断硬中断于此(从kernel,ntdll返回主程序server领空)
0040D75A   FF13            call dword ptr ds:
0040D75C   AB                stos dword ptr es:
0040D75D   ^ EB E7             jmp short Server.0040D746              ; 这里跳回,是个循环

EAX 00408030 Server.00408030
ECX 00000000
EDX 0040DF0C Server.0040DF0C
EBX 0040D7E1 <&KERNEL32.GetProcAddress>
ESP 0012FFC0
EBP 0040D810 Server.0040D810
ESI 0040A004 ASCII "KERNEL32.DLL"
EDI 00404972 Server.00404972
EIP 0040D741 Server.0040D741

又F9,来到:
77E80228   53    push ebx                               ; <&KERNEL32.GetProcAddress>


连续2次F9,重回Server领空:
0040D759   55    push ebp                               ; KERNEL32.77E60000

EAX 0040A011 ASCII "lstrcpyA"
ECX 0012FFE0
EDX 00130608
EBX 0040D7E1 <&KERNEL32.GetProcAddress>
ESP 0012FFC0
EBP 77E60000 KERNEL32.77E60000
ESI 0040A011 ASCII "lstrcpyA"
EDI 00408030 Server.00408030
EIP 0040D759 Server.0040D759

又F9连续5次,又回到了0040D759:
代码和上次一样,只是部分寄存器有变化
EAX 0040A01A ASCII "lstrlenA"
ECX 0012FFE0
EDX 77FCE748 ntdll.77FCE748
EBX 0040D7E1 <&KERNEL32.GetProcAddress>
ESP 0012FFC0
EBP 77E60000 KERNEL32.77E60000
ESI 0040A01A ASCII "lstrlenA"
EDI 00408034 Server.00408034
EIP 0040D759 Server.0040D759

重复5次F9,同样又中断于0040D759:
EAX 0040A023 ASCII "GetTickCount"
ECX 0012FFE0
EDX 77FCE748 ntdll.77FCE748
EBX 0040D7E1 <&KERNEL32.GetProcAddress>
ESP 0012FFC0
EBP 77E60000 KERNEL32.77E60000
ESI 0040A023 ASCII "GetTickCount"
EDI 00408038 Server.00408038
EIP 0040D759 Server.0040D759

看到了么,是解析不同的函数地址,看来这里是个循环,不信可以再F9试试:
5次F9后看堆栈,EIP还是在0040D759:
0012FFC0    0040A030ASCII "CreateThread"
再5次F9,
0012FFC0    0040A03DASCII "OpenThread"
....
....


现在取消硬件读中断,用F8一步步来跟这个循环:
0040D759   55                push ebp
0040D75A   FF13            call dword ptr ds:
0040D75C   AB                stos dword ptr es:
0040D75D   ^ EB E7             jmp short Server.0040D746        ; 往回跳


0040D746   AC                lods byte ptr ds:                ; 循环开始,循环指向不同的输入函数名
0040D747   84C0            test al,al
0040D749   ^ 75 FB             jnz short Server.0040D746
0040D74B   3806            cmp byte ptr ds:,al
0040D74D   ^ 74 E7             je short Server.0040D736                ; 循环体内只有这一处跳转跳出循环体,回车来到0040D736处查看代码
0040D74F   8BC6            mov eax,esi
0040D751   79 05             jns short Server.0040D758
0040D753   46                inc esi
0040D754   33C0            xor eax,eax
0040D756   66:AD             lods word ptr ds:
0040D758   50                push eax
0040D759   55                push ebp
0040D75A   FF13            call dword ptr ds:
0040D75C   AB                stos dword ptr es:
0040D75D   ^ EB E7             jmp short Server.0040D746        ;往回跳到循环开始处


来到:
0040D736   46                inc esi
0040D737   AD                lods dword ptr ds:                ;Load doubleword at address DS:(E)SI into EAX

EAX 004080D8 Server.004080D8
ECX 0012FFE0
EDX 77FCE748 ntdll.77FCE748
EBX 0040D7E1 <&KERNEL32.GetProcAddress>
ESP 0012FFC4
EBP 77E60000 KERNEL32.77E60000
ESI 0040A19C ASCII "USER32.DLL"

0040D738   85C0            test eax,eax                        ; 注意EAX!=0,表示还有其他导入库的输入函数需要解析地址,现在是USER32.DLL了
0040D73A   - 0F84 946EFFFF   je Server.004045D4                ; 那么我们假定EAX=0,则说明所有的输入函数都已解析完毕,而且这是个长跳转"—",
                                                                ; 因此我们直接在004045D4下断F2,--〉光明之巅。
0040D740   56                push esi
0040D741   97                xchg eax,edi
0040D742   FF53 FC         call dword ptr ds:
0040D745   95                xchg eax,ebp
0040D746   AC                lods byte ptr ds:
0040D747   84C0            test al,al
0040D749   ^ 75 FB             jnz short Server.0040D746
0040D74B   3806            cmp byte ptr ds:,al
0040D74D   ^ 74 E7             je short Server.0040D736
0040D74F   8BC6            mov eax,esi
0040D751   79 05             jns short Server.0040D758
0040D753   46                inc esi
0040D754   33C0            xor eax,eax
0040D756   66:AD             lods word ptr ds:
0040D758   50                push eax
0040D759   55                push ebp
0040D75A   FF13            call dword ptr ds:
0040D75C   AB                stos dword ptr es:
0040D75D   ^ EB E7             jmp short Server.0040D746


在004045D4下断F2时,由于程序还没解压完毕,OD会提示在数据中设置断点,不管它,点确定。
F9运行,中断于:
004045D4       68            db 68                                  ;CHAR 'h'
004045D5       F0            db F0
004045D6       45            db 45                                  ;CHAR 'E'
004045D7       40            db 40                                  ;CHAR '@'
004045D8       00            db 00
004045D9       68            db 68                                  ;CHAR 'h'
004045DA       00            db 00
似乎不像哦,怎么回事?别急,在CPU窗口中鼠标右键--〉分析--〉分析代码:
004045D4    .68 F0454000   push Server.004045F0                        ; 这里就是OEP了,用OD插件OLLYDUMP到Server_.exe。
004045D9    .68 00104000   push Server.00401000
004045DE    .6A 00         push 0                                 ; /pModule = NULL
004045E0    .FF15 90804000   call dword ptr ds:             ; \GetModuleHandleA
004045E6    .A3 E4784000   mov dword ptr ds:,eax
004045EB    .^ E9 B4FFFFFF   jmp Server.004045A4
004045F0    .50            push eax
004045F1    .50            push eax
004045F2    .FF35 94804000   push dword ptr ds:             ;KERNEL32.ExitProcess
004045F8    .^ E9 93FFFFFF   jmp Server.00404590
呵呵,代码出来了。


DUMP后,OD现在不要动,运行IMPORT REC v16Final,选Server.exe进程,OEP处输入000045D4,自动搜索IAT,
获取输入表,修复抓起文件,选前一步骤dump的Server_.exe文件,OKay,Server__.exe生成,完工。

运行Server__.exe无错误,正常运行。原Server.exe有10.2k,现在Server__.exe竟90k,WinUPack压缩率够大的呀。
反正脱壳后体积也不大,懒得优化了(其实是不大熟悉PE减肥:-))



[总结]
一般对于纯压缩壳,无异常SEH、anti和花指令等,用ESP定律很好用。

[问题]
我怎么才能知道这个Server.exe程序是用WinUpack哪个版本压缩的?在PE文件何偏移处。

yyjpcx 发表于 2005-8-14 21:36:39

janxin 发表于 2005-8-15 14:34:44

hyd009 发表于 2005-8-23 07:14:42

对于纯压缩壳,无异常SEH、anti和花指令等,用ESP定律通杀


兄弟把原程序传上来就好了,我也可以看看学习

快雪时晴 发表于 2005-8-24 00:26:37

Originally posted by hyd009 at 2005-8-23 07:14 AM:
对于纯压缩壳,无异常SEH、anti和花指令等,用ESP定律通杀


兄弟把原程序传上来就好了,我也可以看看学习

感谢兄弟阅读本文.

CorpUlent 发表于 2006-3-12 21:09:54

不对啊,我这里的ESP是0013FFC4,再说,我根本就下不了断点,求解释
页: [1]
查看完整版本: 【原创】ESP定律搞定KEN QQServer2.0 QQ挂机程序脱壳