| 
注册时间2005-6-3
阅读权限30
最后登录1970-1-1UID1874 龙战于野 
 
 该用户从未签到 | 
 
| 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.[[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:[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文件何偏移处。
 | 
 |