laomms 发表于 2008-4-24 18:46:11

PECompact 2.79脱壳及编写脱壳机

事先说明,这个不是自己写的代码,只是拿了别人的东西进行分析及修改。
一、脱壳
1、OD载入
00401258 > $  B8 EC794000   mov     eax, 004079EC                     ;注意这里移到EAX的值
0040125D   .  50            push    eax
0040125E   .  64:FF35 00000>push    dword ptr fs:
00401265   .  64:8925 00000>mov     dword ptr fs:, esp
0040126C   .  33C0          xor     eax, eax
0040126E   .  8908          mov     dword ptr , ecx
00401270   .  50            push    eax

2、CTRL+G到达004079EC
004079EC    B8 B6679CFF     mov     eax, FF9C67B6                    ;到这里
004079F1    8D88 5912A400   lea     ecx, dword ptr
004079F7    8941 01         mov     dword ptr , eax
004079FA    8B5424 04       mov     edx, dword ptr
004079FE    8B52 0C         mov     edx, dword ptr
00407A01    C602 E9         mov     byte ptr , 0E9
00407A04    83C2 05         add     edx, 5
00407A07    2BCA            sub     ecx, edx
00407A09    894A FC         mov     dword ptr , ecx
00407A0C    33C0            xor     eax, eax
00407A0E    C3              retn

3、搜索CALL EDI,并下断,断下后F7跟入
00407A8A    FFD7            call    edi                              ; 下断,F7跟入            
00407A8C    8985 FA12A400   mov     dword ptr , eax  
00407A92    8BF0            mov     esi, eax
00407A94    8B4B 14         mov     ecx, dword ptr
...
00407AAE    FFE0            jmp     eax                              ;注意,跳向OEP

4、F7跟入到这里,在call 000308F0处再跟入,这里对DLL进行处理
000308C9    57              push    edi                              ; edi是首个thunk地址
000308CA    51              push    ecx                              ; 这里压入DLL名称

0040228E  75 73 65 72 33 32 2E 64 6C 6C 00 00 0A 00 47 65  user32.dll....Ge
0040229E  74 4F 70 65 6E 46 69 6C 65 4E 61 6D 65 41 00 00  tOpenFileNameA..
004022AE  63 6F 6D 64 6C 67 33 32 2E 64 6C 6C 00 00 00 00  comdlg32.dll....

//从这里我们看可以看到分别压入三个DLL

000308CB    53              push    ebx
000308CC    E8 1F000000     call    000308F0                         ; 寄存器ECX中就是DLL的名称
000308D1    40              inc     eax
000308D2    75 08           jnz     short 000308DC
000308D4    48              dec     eax
000308D5    5E              pop     esi
000308D6    5F              pop     edi
000308D7    5B              pop     ebx
000308D8    C9              leave
000308D9    C2 0800         retn    8


5、F7跟入到这里,这里就是解压各DLL中所含的API,可以用F8单步跟踪循环等待解压
000309B1    40              inc     eax                              ; change_i.0040215E
000309B2    40              inc     eax
000309B3    50              push    eax                              ;压入API名称
000309B4    FF75 FC         push    dword ptr
000309B7    FF93 111EA400   call    dword ptr
000309BD    5A              pop     edx
000309BE    85C0            test    eax, eax
000309C0  ^ 0F84 6FFFFFFF   je      00030935
000309C6    8906            mov     dword ptr , eax        
000309C8    8902            mov     dword ptr , eax            
000309CA    83C2 04         add     edx, 4
000309CD    83C6 04         add     esi, 4
000309D0  ^ EB AC           jmp     short 0003097E
000309D2    33C0            xor     eax, eax
000309D4    5E              pop     esi
000309D5    5F              pop     edi
000309D6    5B              pop     ebx
000309D7    C9              leave
000309D8    C2 1000         retn    10

随着循环,在内存地址中可以看到各API名称:
00401FFC  00000000
00402000  7632311E  comdlg32.GetOpenFileNameA
00402004  00000000
00402008  7C85F229  kernel32.EnumResourceNamesA
0040200C  7C81CDDA  kernel32.ExitProcess
00402010  7C80BE89  kernel32.FindResourceA
00402014  7C80ABDE  kernel32.FreeLibrary
00402018  7C80B6A1  kernel32.GetModuleHandleA
0040201C  7C86F959  kernel32.EndUpdateResourceA
00402020  7C809FB5  kernel32.LoadResource
00402024  7C80CC97  kernel32.SetHandleCount
00402028  7C80BC69  kernel32.SizeofResource
0040202C  7C86F6B8  kernel32.UpdateResourceA
00402030  7C80BDB6  kernel32.lstrlenA
00402034  7C801D4F  kernel32.LoadLibraryExA
00402038  7C86FC8B  kernel32.BeginUpdateResourceA
0040203C  00000000
00402040  77D259C9  user32.EndDialog
00402044  77D23DCE  user32.GetDlgItem
00402048  77D208CE  user32.LoadIconA
0040204C  77D2F383  user32.SendMessageA
00402050  77D3C93A  user32.SetDlgItemTextA
00402054  77D3B10C  user32.DialogBoxParamA
00402058  77D1BE71  user32.EnableWindow
0040205C  00000000



6、等API完全解压后,重新到CALL EDI的地方,向下找到JMP EAX,下断,F9段下后,F8就到OEP了
00401258 > $  6A 00         push    0                                ;OEP
0040125A   ?  E8 47000000   call    004012A6                       
0040125F   ?  A3 00304000   mov     dword ptr , eax
00401264   ?  6A 00         push    0
00401266   ?  68 DF104000   push    004010DF
0040126B   ?  6A 00         push    0
0040126D   ?  6A 65         push    65
0040126F   ?  FF35 00304000 push    dword ptr
00401275   .  E8 56000000   call    004012D0                       
0040127A   ?  6A 00         push    0
0040127C   ?  E8 13000000   call    00401294                        
00401281   ?  CC            int3


至此,可以在0040125处DUMP文件了,API已经解压,不用修复了。

二、写脱壳机
如何打开文件、如何获取原文件PE信息都不讲了,很多实例和代码。来看脱壳部分:
1、OD载入停在入口,汇编源码形式:

创建进程,修改页面属性为可写:
invoke CreateProcess,FilePath,0,0,0,FALSE,CREATE_SUSPENDED,0,0,addr startup,addr processInfo
invoke VirtualProtectEx,processInfo.hProcess,imagebase,1000h,PAGE_EXECUTE_READWRITE,addr Buffer
mov edi,imagebase                                           ;(imagebase哪里来,可以查看PEinf部分代码:push

.OptionalHeader.ImageBase,pop imagebase)
add edi,ep                                                  ;(原来入口EP哪里来,可以查看PEinf部分代码:push

.OptionalHeader.AddressOfEntryPoint,pop ep)
add edi,1                                                   ;就是到达入口第二行0040125D   .  50            push    eax
lea esi,Buffer                                              ;push eax

2、CTRL+G到达004079EC,
invoke ReadProcessMemory,processInfo.hProcess,edi,esi,4,0   ;读取 eax的值
mov edi,dword ptr                                      ;得到004079EC

3、搜索CALL EDI,并下断,断下后F7跟入,利用了Searchcode函数,是个自定义的从起始地址查找二进制代码的函数
invoke Searchcode,edi,0ffd7h                                ;搜索CALL EDI
mov call_edi,eax                                            ;得到call edi的地址   
mov BP1,ebx                                                                     ;设置为断点BP1
invoke ReadProcessMemory,processInfo.hProcess,ebx,addr BP1_data,2,0             ;设置BP1断点,这里利用修改入口为JMP循环的的设断方法
invoke WriteProcessMemory,processInfo.hProcess,ebx,CTEXT(0EBh,0FEh),2,0

invoke Searchcode,call_edi,0FFe0h                                               ;搜索jmp eax,顺便也下个断
invoke ReadProcessMemory,processInfo.hProcess,ebx,addr BP2_data,2,0            ;设置BP2断点
invoke WriteProcessMemory,processInfo.hProcess,ebx,CTEXT(0EBh,0FEh),2,0   

invoke ResumeThread,processInfo.hThread                                        ;放开运行
invoke Sleep,500                                                               ;过500毫秒

invoke SuspendThread,processInfo.hThread                                       ;挂起线程
mov .ContextFlags,CONTEXT_FULL                                            ;设置访问所有的寄存器                                    

  
invoke GetThreadContext,processInfo.hThread,edi                                ;得到中断线程的上下文信息
mov eax,.regEip                                                           ;相对地址移入EAX        

.if eax==BP1;                                                                  ;如果断在BP1,即call edi地址处              
                invoke WriteProcessMemory,processInfo.hProcess,BP1,addr BP1_data,2,0   ;清除BP1断点
                mov eax, .regEdi                                                  ;查看寄存器EDI的值
                mov StepintoAddr,eax                                                   ;将寄存器EDI的值存储           


4、F7跟入到push edi push ecx,在call 000308F0处再跟入,我们可以用搜索二进制的方法
invoke Searchcode,StepintoAddr,5751h                                    ;搜索push edi push ecx
add eax,3                                                               
mov ebx,eax                                                             ;到call 000308F0的地址
mov BP3,ebx                                                             ;将CALL设置为断点BP3
invoke ReadProcessMemory,processInfo.hProcess,ebx,addr BP3_data,2,0     ;下断
invoke WriteProcessMemory,processInfo.hProcess,ebx,CTEXT(0EBh,0FEh),2,0

000308C9    57              push    edi
000308CA    51              push    ecx                                 ; 这里压入准备加密的DLL名称
//从这里我们看可以看到共循环了三次,分别压入comdlg32.ll、kernel32.dll、user32.dll
000308CB    53              push    ebx
000308CC    E8 1F000000     call    000308F0                            ; 寄存器ECX中就是DLL的名称

.elseif eax==BP3                                                        ;如果断在BP3,即call    000308F0 处
;此时,寄存器ecx中是DLL名称,寄存器edi中首个thunk
mov eax,.regEcx                                                    ;取寄存器ECX的值,即DLL的名称地址,kernel32.dll等
sub eax,imagebase                                                      
mov .Name1,eax                                                     ;存储RVA
mov eax, .regEdi;                                                  ;edi中是FirstThunk
sub eax,imagebase                                                       ;得到FirstThunk的RVA
mov .FirstThunk,eax
add esi,sizeof IMAGE_IMPORT_DESCRIPTOR
inc dlls_imported                                                       ;循环,起先有个0赋值操作mov dlls_imported,0
invoke WriteProcessMemory,processInfo.hProcess,BP3,addr BP3_data,2,0    ;消除BP3断点

5、F7跟入到inc eax, inc eax
invoke Searchcode,StepintoAddr,4040h                                    ;在内存地址中搜索inc eax, inc eax
在000309C8处,我们可以打补丁,修正RVA地址,补丁代码为mov ,edx,编辑二进制代码为89 16
invoke Searchcode,StepintoAddr,8902h  
add eax,1                                                               ;找到下一行代码
mov PatchAddr,eax                                                       ;准备打补丁的地址PatchAddr                                    
invoke WriteProcessMemory,processInfo.hProcess,eax,CTEXT(16h),1,0       ;改一个字节为16,即将mov ,eax改为mov ,edx  

6、等API完全解压后,重新到CALL EDI的地方,向下找到JMP EAX,下断
.elseif eax==BP2                                                        ;如果断在BP2即jmp eax的地址,得到EAX为OEP
mov eax,.regEax   


完整代码看附件。




[ 本帖最后由 laomms 于 2008-4-24 19:02 编辑 ]

lOOp 发表于 2008-4-24 19:03:20

好喜欢啊
做个sofa。。。。

mrowl 发表于 2008-4-24 19:50:46

不错,学习了

clw 发表于 2008-4-24 19:55:56

不错,向LZ学习

Lancia 发表于 2008-4-24 20:15:02

楼主很棒,,学习了!:rose

CCHLord 发表于 2008-4-24 20:33:42

我也学习一下PECompact 脱壳机编程
还有现成的实验品
真不错
呵呵



[ 本帖最后由 CCHLord 于 2008-4-24 21:17 编辑 ]

ycs 发表于 2008-4-24 21:28:52

楼主高产。收藏一份。:rose

小子贼野 发表于 2008-4-24 21:30:13

这个是一定要学习的,顶一个

yyww 发表于 2008-4-24 21:41:16

:P 强人,学习一下编写

夜凉如水 发表于 2008-4-24 21:50:17

呵呵  测试目标1就脱不掉

卡秋莎 发表于 2008-4-24 22:01:37

学习ASM,学习楼主

LoveZhuYiF 发表于 2008-4-24 22:09:37

牛人,下来测试一下~~

elance 发表于 2008-4-24 22:09:49

学习脱壳机,

ttt0001 发表于 2008-4-24 22:16:11

不错,学习!谢谢楼主,楼主高啊!!!谢谢分享!

网游难民 发表于 2008-4-24 22:23:00

太强大了~~
学习~~:P
页: [1] 2 3 4 5 6 7 8 9
查看完整版本: PECompact 2.79脱壳及编写脱壳机