菜鳥脫殼之脫殼的基礎(chǔ)知識(shí):如何尋找OEP
這節(jié)我們來講講如何尋找一個(gè)程序的OEP,即Original Entry Point。一些PE加殼程序在被加密的程序上面加了一個(gè)區(qū)段(有的殼也會(huì)合并區(qū)段),當(dāng)外殼代碼執(zhí)行完畢以后,會(huì)跳到程序的本身的代碼來執(zhí)行,所以我們可以依靠跨區(qū)段的轉(zhuǎn)移指令來尋找程序的入口點(diǎn)。
我們來看看加殼之前的Delphi7.0的程序,用LordPE來打開Delphi7.0程序,我們看到程序的入口點(diǎn)是004C498:
看區(qū)段,沒有任何的新加的區(qū)塊:
我們來看看加了殼的程序的入口點(diǎn),加過殼的入口點(diǎn)為000629D0:
區(qū)段變?yōu)槿齻€(gè)了,很明顯,殼將原程序的區(qū)段給合并了:
加了殼后,首先,各個(gè)區(qū)段都要被系統(tǒng)映射到內(nèi)存中,因?yàn)楝F(xiàn)在的入口點(diǎn)是000629D0,是指向外殼部分的,外殼拿到了控制權(quán)以后,通過LoadLibrary、GetProcaddresss、GetModuleHandle等函數(shù)來獲得自身所需要的API的地址,來解密各個(gè)區(qū)段的信息,填充好IAT后,就要跳到程序的OEP了(Entry Point),此例是004C498,我們用Ollydbg載入,設(shè)置好各個(gè)選項(xiàng)(我是把暫停點(diǎn)停在了WinMain處了)。
Ollydbg暫停以后,加殼程序停在了004629D0處:
004629D0 > 60 pushad //保存現(xiàn)場(pushad 相當(dāng)于 push 所有的寄存器)
004629D1 BE 00F04300 mov esi, 0043F000 //把代碼段放到esi寄存器
004629D6 8DBE 0020FCFF lea edi, dword ptr [esi+FFFC2000] //得到基址
004629DC C787 9CC00400 7>mov dword ptr [edi+4C09C], 46CD167B//將第一個(gè)函數(shù)的地址放到[edi+ 4C09C]
004629E6 57 push edi //將基址壓棧
004629E7 83CD FF or ebp, FFFFFFFF //將0012FFC0與 FFFFFFFF或
004629EA EB 0E jmp short 004629FA
004629EC 90 nop
004629ED 90 nop
004629EE 90 nop
004629EF 90 nop
004629F0 8A06 mov al, byte ptr [esi] //取出0043F004的一個(gè)字節(jié)
004629F2 46 inc esi //指向下一個(gè)字節(jié)
004629F3 8807 mov byte ptr [edi], al //從00401000開始,開始還原代碼
004629F5 47 inc edi //指向下一個(gè)地址
004629F6 01DB add ebx, ebx //ebx + ebx,當(dāng)ebx不等于零的時(shí)候跳轉(zhuǎn),下面的adc如果為,就取出下一個(gè)地址,并放到ebx中
004629F8 75 07 jnz short 00462A01
004629FA 8B1E mov ebx, dword ptr [esi] //將0043F000放到ebx中
004629FC 83EE FC sub esi, -4 //0043F000加4
004629FF 11DB adc ebx, ebx //進(jìn)位加法器
00462A01 ^ 72 ED jb short 004629F0 // 向上跳轉(zhuǎn),ebx做為是否回跳的標(biāo)志,循環(huán)處理代碼
00462A03 B8 01000000 mov eax, 1 // eax = 1
00462A08 01DB add ebx, ebx // ebx依然作為循環(huán)的標(biāo)志
00462A0A 75 07 jnz short 00462A13
00462A0C 8B1E mov ebx, dword ptr [esi] //esi指向的地址放到ebx里面
00462A0E 83EE FC sub esi, -4 //esi + 4
00462A11 11DB adc ebx, ebx//進(jìn)位加法
00462A13 11C0 adc eax, eax //進(jìn)位加法
00462A15 01DB add ebx, ebx //ebx + ebx
00462A17 73 0B jnb short 00462A24
00462A19 75 28 jnz short 00462A43 //跳到下面
00462A1B 8B1E mov ebx, dword ptr [esi]
00462A1D 83EE FC sub esi, -4
00462A20 11DB adc ebx, ebx
00462A22 72 1F jb short 00462A43
00462A24 48 dec eax
00462A25 01DB add ebx, ebx
00462A27 75 07 jnz short 00462A30
00462A29 8B1E mov ebx, dword ptr [esi]
00462A2B 83EE FC sub esi, -4
00462A2E 11DB adc ebx, ebx
00462A30 11C0 adc eax, eax
00462A32 ^ EB D4 jmp short 00462A08
00462A34 01DB add ebx, ebx
00462A36 75 07 jnz short 00462A3F
00462A38 8B1E mov ebx, dword ptr [esi]
00462A3A 83EE FC sub esi, -4
00462A3D 11DB adc ebx, ebx
00462A3F 11C9 adc ecx, ecx
00462A41 EB 52 jmp short 00462A95
00462A43 31C9 xor ecx, ecx // 清零ecx
00462A45 83E8 03 sub eax, 3 // eax - 3
00462A48 72 11 jb short 00462A5B
00462A4A C1E0 08 shl eax, 8
00462A4D 8A06 mov al, byte ptr [esi]
00462A4F 46 inc esi
00462A50 83F0 FF xor eax, FFFFFFFF
00462A53 74 75 je short 00462ACA
00462A55 D1F8 sar eax, 1
00462A57 89C5 mov ebp, eax
00462A59 EB 0B jmp short 00462A66
00462A5B 01DB add ebx, ebx
00462A5D 75 07 jnz short 00462A66
00462A5F 8B1E mov ebx, dword ptr [esi]
00462A61 83EE FC sub esi, -4
00462A64 11DB adc ebx, ebx
00462A66 ^ 72 CC jb short 00462A34
00462A68 41 inc ecx
00462A69 01DB add ebx, ebx
00462A6B 75 07 jnz short 00462A74
00462A6D 8B1E mov ebx, dword ptr [esi]
00462A6F 83EE FC sub esi, -4
00462A72 11DB adc ebx, ebx
00462A74 ^ 72 BE jb short 00462A34
00462A76 01DB add ebx, ebx
00462A78 75 07 jnz short 00462A81
00462A7A 8B1E mov ebx, dword ptr [esi]
00462A7C 83EE FC sub esi, -4
00462A7F 11DB adc ebx, ebx
00462A81 11C9 adc ecx, ecx
00462A83 01DB add ebx, ebx
00462A85 ^ 73 EF jnb short 00462A76
00462A87 75 09 jnz short 00462A92
00462A89 8B1E mov ebx, dword ptr [esi]
00462A8B 83EE FC sub esi, -4
00462A8E 11DB adc ebx, ebx
00462A90 ^ 73 E4 jnb short 00462A76
00462A92 83C1 02 add ecx, 2
00462A95 81FD 00FBFFFF cmp ebp, -500 //迷惑指令
00462A9B 83D1 02 adc ecx, 2// 進(jìn)位加法
00462A9E 8D142F lea edx, dword ptr [edi+ebp] // edi + ebp的地址裝載到edx,即原來的代碼段的地址
00462AA1 83FD FC cmp ebp, -4 // 判斷跳轉(zhuǎn)標(biāo)志,EBP小于等于-4就跳
00462AA4 76 0E jbe short 00462AB4
00462AA6 8A02 mov al, byte ptr [edx] //取出代碼段的一字節(jié)
00462AA8 42 inc edx //指向下一個(gè)地址
00462AA9 8807 mov byte ptr [edi], al //取出的代碼放到edi里面
00462AAB 47 inc edi //指向下一個(gè)代碼
00462AAC 49 dec ecx //計(jì)數(shù)器
00462AAD ^ 75 F7 jnz short 00462AA6 //關(guān)于計(jì)數(shù)器(ecx)的跳轉(zhuǎn)
00462AAF ^ E9 42FFFFFF jmp 004629F6 //向上面跳,跳到add ebx,ebx
00462AB4 8B02 mov eax, dword ptr [edx] // 處理輸入表
00462AB6 83C2 04 add edx, 4 // edx + 4,指向下一個(gè)地址
00462AB9 8907 mov dword ptr [edi], eax //將代碼放到edi
00462ABB 83C7 04 add edi, 4// edi + 4, 存放代碼的地址
00462ABE 83E9 04 sub ecx, 4//ecx - 4
00462AC1 ^ 77 F1 ja short 00462AB4
00462AC3 01CF add edi, ecx // edi + ecx,指向接收代碼的地址的最后一個(gè)字節(jié)
00462AC5 ^ E9 2CFFFFFF jmp 004629F6 //跳到 add ebx,ebx
00462ACA 5E pop esi
00462ACB 89F7 mov edi, esi
00462ACD B9 81260000 mov ecx, 2681
00462AD2 8A07 mov al, byte ptr [edi] //指向我們原來代碼段的代碼,取出到AL里面
00462AD4 47 inc edi //指向下一個(gè)字節(jié)
00462AD5 2C E8 sub al, 0E8 //處理CALL
00462AD7 3C 01 cmp al, 1 //判斷al是否大于1
00462AD9 ^ 77 F7 ja short 00462AD2 //循環(huán),到下一個(gè)CALL的第一個(gè)字節(jié)為止
00462ADB 803F 14 cmp byte ptr [edi], 14
00462ADE ^ 75 F2 jnz short 00462AD2
00462AE0 8B07 mov eax, dword ptr [edi] //取出里面的地址,里面的地址是定位CALL的絕對地址要用到的
00462AE2 8A5F 04 mov bl, byte ptr [edi+4] //得到下條地址的開始字節(jié)放到AL里面,CALL絕對地址就是下條指令開始+剛才上面取出的那個(gè)數(shù)字
00462AE5 66:C1E8 08 shr ax, 8 // ax右移8位
00462AE9 C1C0 10 rol eax, 10 //eax算術(shù)左移 8位
00462AEC 86C4 xchg ah, al //交換內(nèi)容
00462AEE 29F8 sub eax, edi //eax - edi
00462AF0 80EB E8 sub bl, 0E8 //再減去E8
00462AF3 01F0 add eax, esi //eax + esi,其中 esi是代碼段開始的地方
00462AF5 8907 mov dword ptr [edi], eax //這里處理CALL的地址,算出CALL的偏移到EDI里面
00462AF7 83C7 05 add edi, 5 //edi + 5,指向call的后面
00462AFA 88D8 mov al, bl //bl的內(nèi)容放到al中
00462AFC ^ E2 D9 loopd short 00462AD7 //循環(huán)處理CALL,其中ecx作為計(jì)數(shù)器
00462AFE 8DBE 00F00500 lea edi, dword ptr [esi+5F000] //代碼段的起始地址 + 5F000
00462B04 8B07 mov eax, dword ptr [edi] //現(xiàn)在EDI指向我們的代碼的輸入表
00462B06 09C0 or eax, eax //eax 或 eax ,判斷eax是否為零
00462B08 74 3C je short 00462B46
00462B0A 8B5F 04 mov ebx, dword ptr [edi+4] //取得這個(gè)地址的數(shù)據(jù)放到ebx
00462B0D 8D8430 AC2D0600 lea eax, dword ptr [eax+esi+62DAC] // 取得外殼段的KERNEL32.DLL的地址放eax
00462B14 01F3 add ebx, esi //我們代碼段的起始地址加上剛才取出的那個(gè)數(shù)據(jù)
00462B16 50 push eax //kernel32.dll的地址
00462B17 83C7 08 add edi, 8 //edi + 8
00462B1A FF96 4C2E0600 call dword ptr [esi+62E4C] //裝載kernel32.dll
00462B20 95 xchg eax, ebp //交換數(shù)據(jù),即eax指向kernel32.dll的地址
00462B21 8A07 mov al, byte ptr [edi] //取得現(xiàn)在的EDI的地址指向的數(shù)據(jù)放到AL
00462B23 47 inc edi //指向下一個(gè)函
00462B24 08C0 or al, al //al 或 al,判斷al是否為零
00462B26 ^ 74 DC je short 00462B04
00462B28 89F9 mov ecx, edi //取出的函數(shù)的名字放到ecx里面
00462B2A 57 push edi //函數(shù)名字壓棧
00462B2B 48 dec eax //eax - 1
00462B2C F2:AE repne scas byte ptr es:[edi]
00462B2E 55 push ebp //kernel32.dll的基址
00462B2F FF96 502E0600 call dword ptr [esi+62E50] //外殼的GetProcaddress
00462B35 09C0 or eax, eax //eax或eax,得到函數(shù)的地址
00462B37 74 07 je short 00462B40
00462B39 8903 mov dword ptr [ebx], eax //處理輸入表
00462B3B 83C3 04 add ebx, 4 //ebx + 4,指向下一個(gè)輸入表的地址
00462B3E ^ EB E1 jmp short 00462B21
00462B40 FF96 602E0600 call dword ptr [esi+62E60]
00462B46 8BAE 542E0600 mov ebp, dword ptr [esi+62E54] //VirtualProtect的地址放到ebp
00462B4C 8DBE 00F0FFFF lea edi, dword ptr [esi-1000] //指向PE頭,即映像基址
00462B52 BB 00100000 mov ebx, 1000 //把1000放到ebx,即ebx = 1000
00462B57 50 push eax
00462B58 54 push esp
00462B59 6A 04 push 4
00462B5B 53 push ebx
00462B5C 57 push edi
00462B5D FFD5 call ebp //改變屬性
00462B5F 8D87 1F020000 lea eax, dword ptr [edi+21F] //現(xiàn)在eax指向PE頭中區(qū)段的偏移起始位置
00462B65 8020 7F and byte ptr [eax], 7F //改寫區(qū)段名字
00462B68 8060 28 7F and byte ptr [eax+28], 7F //改寫區(qū)塊屬性第一個(gè)區(qū)塊的屬性
00462B6C 58 pop eax
00462B6D 50 push eax
00462B6E 54 push esp
00462B6F 50 push eax
00462B70 53 push ebx
00462B71 57 push edi
00462B72 FFD5 call ebp
00462B74 58 pop eax
00462B75 61 popad //恢復(fù)現(xiàn)場
00462B76 8D4424 80 lea eax, dword ptr [esp-80]
00462B7A 6A 00 push 0
00462B7C 39C4 cmp esp, eax
00462B7E ^ 75 FA jnz short 00462B7A
00462B80 83EC 80 sub esp, -80
00462B83 ^ E9 109FFEFF jmp 0044CA98 //跨區(qū)段的轉(zhuǎn)移,跳到OEP
00462B88 A0 2B4600B0 mov al, byte ptr [B000462B]
00462B8D 2B46 00 sub eax, dword ptr [esi]
00462B90 9C pushfd
Delphi7.0的OEP:
0044CA98 55 push ebp
0044CA99 8BEC mov ebp, esp
0044CA9B 83C4 F0 add esp, -10
0044CA9E B8 B8C84400 mov eax, 0044C8B8
0044CAA3 E8 2091FBFF call 00405BC8
0044CAA8 A1 B8DF4400 mov eax, dword ptr [44DFB8]
0044CAAD 8B00 mov eax, dword ptr [eax]
0044CAAF E8 9CE6FFFF call 0044B150
這個(gè)方法很簡單,就是從殼的開始一直跟蹤,直到來到OEP,沒有什么技巧!大家應(yīng)該熟悉各個(gè)程序的OEP,并且熟練的掌握這種方法!