飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 2485|回复: 5

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

[复制链接]

该用户从未签到

发表于 2005-8-14 21:33:37 | 显示全部楼层 |阅读模式
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 [Esc] 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.[...ByDwing@...PE..
上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:[ebp+eax*4+5C]
0040D5AB     FF16              call dword ptr ds:[esi]

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:[ebx-4]
0040D745     95                xchg eax,ebp
0040D746     AC                lods byte ptr ds:[esi]
0040D747     84C0              test al,al
0040D749   ^ 75 FB             jnz short Server.0040D746
0040D74B     3806              cmp byte ptr ds:[esi],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:[esi]
0040D758     50                push eax
0040D759     55                push ebp                                      ; 以后将不断硬中断于此(从kernel,ntdll返回主程序server领空)
0040D75A     FF13              call dword ptr ds:[ebx]
0040D75C     AB                stos dword ptr es:[edi]
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    0040A030  ASCII "CreateThread"
再5次F9,
0012FFC0    0040A03D  ASCII "OpenThread"
....
....


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


0040D746     AC                lods byte ptr ds:[esi]                ; 循环开始,[esi]循环指向不同的输入函数名
0040D747     84C0              test al,al
0040D749   ^ 75 FB             jnz short Server.0040D746
0040D74B     3806              cmp byte ptr ds:[esi],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:[esi]
0040D758     50                push eax
0040D759     55                push ebp
0040D75A     FF13              call dword ptr ds:[ebx]
0040D75C     AB                stos dword ptr es:[edi]
0040D75D   ^ EB E7             jmp short Server.0040D746        ;往回跳到循环开始处


来到:
0040D736     46                inc esi
0040D737     AD                lods dword ptr ds:[esi]                ;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:[ebx-4]
0040D745     95                xchg eax,ebp
0040D746     AC                lods byte ptr ds:[esi]
0040D747     84C0              test al,al
0040D749   ^ 75 FB             jnz short Server.0040D746
0040D74B     3806              cmp byte ptr ds:[esi],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:[esi]
0040D758     50                push eax
0040D759     55                push ebp
0040D75A     FF13              call dword ptr ds:[ebx]
0040D75C     AB                stos dword ptr es:[edi]
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:[408090]             ; \GetModuleHandleA
004045E6    .  A3 E4784000     mov dword ptr ds:[4078E4],eax
004045EB    .^ E9 B4FFFFFF     jmp Server.004045A4
004045F0    .  50              push eax
004045F1    .  50              push eax
004045F2    .  FF35 94804000   push dword ptr ds:[408094]             ;  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文件何偏移处。
PYG19周年生日快乐!
yyjpcx 该用户已被删除
发表于 2005-8-14 21:36:39 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
PYG19周年生日快乐!
janxin 该用户已被删除
发表于 2005-8-15 14:34:44 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
PYG19周年生日快乐!

该用户从未签到

发表于 2005-8-23 07:14:42 | 显示全部楼层
对于纯压缩壳,无异常SEH、anti和花指令等,用ESP定律通杀


兄弟把原程序传上来就好了,我也可以看看学习
PYG19周年生日快乐!

该用户从未签到

 楼主| 发表于 2005-8-24 00:26:37 | 显示全部楼层
Originally posted by hyd009 at 2005-8-23 07:14 AM:
对于纯压缩壳,无异常SEH、anti和花指令等,用ESP定律通杀


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


感谢兄弟阅读本文.

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?加入我们

x
PYG19周年生日快乐!

该用户从未签到

发表于 2006-3-12 21:09:54 | 显示全部楼层
不对啊,我这里的ESP是0013FFC4,再说,我根本就下不了断点,求解释
PYG19周年生日快乐!
您需要登录后才可以回帖 登录 | 加入我们

本版积分规则

快速回复 返回顶部 返回列表