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 编辑 ] 好喜欢啊
做个sofa。。。。 不错,学习了 不错,向LZ学习 楼主很棒,,学习了!:rose 我也学习一下PECompact 脱壳机编程
还有现成的实验品
真不错
呵呵
[ 本帖最后由 CCHLord 于 2008-4-24 21:17 编辑 ] 楼主高产。收藏一份。:rose 这个是一定要学习的,顶一个 :P 强人,学习一下编写 呵呵 测试目标1就脱不掉 学习ASM,学习楼主 牛人,下来测试一下~~ 学习脱壳机, 不错,学习!谢谢楼主,楼主高啊!!!谢谢分享! 太强大了~~
学习~~:P