这是某游戏辅助软件的一个主要dll文件,由于想diy其中的某个功能,所以必须先脱壳。我学破解也有一段时间了,但正儿八经的脱壳还是第一次,不怕大伙笑话,我前后花在脱这个壳的时间加起来快半个月了,当前其中也包括很多学习的过程,下面我把脱壳的过程记录下来,希望对和我一样的新手有所帮助。
这是个10kb的dll文件,作者擅长asm,所以肯定是asm编写的。有antidebug,具体机理不知。
PEID检测结果:
普通扫描
PackMan v0.0.0.1 *
深度扫描
UPX 0.80 - 1.24 DLL -> Markus & Laszlo
核心扫描
UPX 0.80 - 1.24 DLL -> Markus & Laszlo
=============================
DIE核心探测也可以检测出压缩过,提示的是:
--> UPX 0.9x - 3.0
而首页的显示
Microsoft Visual C++ | C/C++
External Sign:Packman v0.0.0.1

初步判断是加了2层壳,PackMan v0.0.0.1和UPX 0.80 - 1.24,好,OD加载strongOD插件,选项全选(其实我也不清楚很多选项的作用,但能过反调试就行了)后载入dll,停在以下位置:

代码:
1000C098 >  60              PUSHAD

1000C099    E8 00000000     CALL k.1000C09E

1000C09E    58              POP EAX

1000C09F    8DA8 6AFFFFFF   LEA EBP,DWORD PTR DS:[EAX-96]

参考fly大侠的手脱packman的文章,入口地址特征和文章描述的一模一样。

BP GetModuleHandleA在它处理输入表之前断下,返回到程序领空,这时候用loadpe把程序内存完全dump出来
代码:
1000C184    56              PUSH ESI                   ;ESI=1000B000,输入表RVA=1000B000-10000000=B000

1000C185    8B36            MOV ESI,DWORD PTR DS:[ESI]

1000C187    0BF6            OR ESI,ESI

1000C189    75 02           JNZ SHORT k.1000C18D

1000C18B    8BF7            MOV ESI,EDI

....

1000C1B4    5E              POP ESI

1000C1B5    83C6 14         ADD ESI,14

1000C1B8    8B7E 10         MOV EDI,DWORD PTR DS:[ESI+10]

1000C1BB    85FF            TEST EDI,EDI

1000C1BD  ^ 75 BC           JNZ SHORT k.1000C17B   ;到这里输入表处理完毕

1000C1BF    8D8B 000000F0   LEA ECX,DWORD PTR DS:[EBX+F0000000];直接跳过循环执行到这里,开始处理重定位表,这时候ESI=1000B028,输入表大小为B028-B000=28

1000C1C5    B8 94B00000     MOV EAX,0B094 ;B094就是重定位表的RVA

1000C1CA    0BC0            OR EAX,EAX

1000C1CC    74 34           JE SHORT k.1000C202 ;重定位表处理完跳到1000C202

....

1000C1FE    85FF            TEST EDI,EDI

1000C200  ^ 75 D0           JNZ SHORT k.1000C1D2

1000C202    61              POPAD         ;重定位表处理完到这里,我们直接跳到这里看看重定位表有多大,ESI=1000B0A0,那么重定位表大小就是B0A0-B094=C(这么小是因为里面还有一层壳)

1000C203  ^ E9 90FEFFFF     JMP k.<ModuleEntryPoint> 

1000C098 >- E9 B3E1FFFF     JMP k.1000A250 ;OEP=1000A250

好,记住刚才那几个数值OEP=1000A250,iat=B000,大小28,reloc=B094,大小C
用pe编辑器修改刚才的到dump文件,得到脱掉第一层的文件,测试能正常运行。

peid扫描这个文件UPX 0.80 - 1.24 DLL -> Markus & Laszlo

说明还有一层UPX的壳,由于只有2个txt区段,说明这不是upx的标准区段,被作者修改过,自然不能用UPX -d直接脱壳,在这里我废了好大功夫,试了各种upx修复工具修复区段都没成功,脱壳机也试了不少还是不行,看来还是得手工脱,仔细研究了《加密与解密第三版》345页的脱壳过程,最终得到了完美的基本与原文件相同的脱壳文件。

UPX的壳oep很好找,直接往下拉拉就看到了去OEP的跳转。
代码:
1000A3A6   > \83C7 04       ADD EDI,4

1000A3A9   .  8D5E FC       LEA EBX,DWORD PTR DS:[ESI-4]

1000A3AC   >  31C0          XOR EAX,EAX                              ;  处理重定位表开始

1000A3AE   .  8A07          MOV AL,BYTE PTR DS:[EDI]

1000A3B0   .  47            INC EDI

1000A3B1   .  09C0          OR EAX,EAX                               ;  检测是否为0,否则结束重定位

1000A3B3   .  74 22         JE SHORT 脱掉第一.1000A3D7

1000A3B5   .  3C EF         CMP AL,0EF

1000A3B7   .  77 11         JA SHORT 脱掉第一.1000A3CA

1000A3B9   >  01C3          ADD EBX,EAX

1000A3BB   .  8B03          MOV EAX,DWORD PTR DS:[EBX]               ;  EBX指向需要重定位的数据,取出放到EAX

1000A3BD   .  86C4          XCHG AH,AL

1000A3BF   .  C1C0 10       ROL EAX,10

1000A3C2   .  86C4          XCHG AH,AL

1000A3C4   .  01F0          ADD EAX,ESI                              ;  ESI指向第一区段VA,这里是10001000

1000A3C6   .  8903          MOV DWORD PTR DS:[EBX],EAX               ;  重定位

1000A3C8   .^ EB E2         JMP SHORT 脱掉第一.1000A3AC

1000A3CA   >  24 0F         AND AL,0F

1000A3CC   .  C1E0 10       SHL EAX,10

1000A3CF   .  66:8B07       MOV AX,WORD PTR DS:[EDI]

1000A3D2   .  83C7 02       ADD EDI,2

1000A3D5   .^ EB E2         JMP SHORT 脱掉第一.1000A3B9                  ;  处理重定位表结束

1000A3D7   >  61            POPAD

1000A3D8   >^ E9 D18CFFFF   JMP 脱掉第一.100030AE                        ;  跳往OEP

....

100030AE    55              PUSH EBP          ;OEP

100030AF    8BEC            MOV EBP,ESP

100030B1    8B45 0C         MOV EAX,DWORD PTR SS:[EBP+C]

100030B4    83F8 01         CMP EAX,1

100030B7    75 14           JNZ SHORT 脱掉第一.100030CD

100030B9    60              PUSHAD

100030BA    E8 1E040000     CALL 脱掉第一.100034DD

到OEP我们把程序内存完整dump出来,保存为dump.dll,我们可以看看内存上下翻翻,UPX虽然破坏了重定位表却保存了原程序的文件头和区段信息,这些数据在我们dump出来的文件中都有。



整理文件头区段信息,把它还原那么应该是这样:




参考这些信息,我们现在重新构造这个dll文件。用winhex新建一个空白的文件,大小为5600h
按ctrl+B把dump文件的1000h开始到41CBh结束处的hex拷贝到新文件的400h处,这样text区段就准备好了。6000h到755Ah为data段,拷贝到新文件的3800处。93EDh到9584h为部分文件头,拷贝到40h(你也可以拷贝到50h,反正最终dos header里要设定)。输入表很简单,用ImportREC_fix或者自己手写都可以,写入到3600h,我偷懒是用ImportREC_fix修复的。
下面是dos header,其实dos header真的很简单,只要写入两个地方,一个是magic number,就是字符MZ,写在hex文件里是4D5A;还有最后一个file address of new exe header,就写刚才我们设好的40h,写在hex文件里就是40000000。其他都留空。
如下:
[


最后是最麻烦的重定位表,由于imagebase设定死了10000000所以不能用常规方法那样用Dll_LoadEx加载两个相同dll得到reloc table。还是要手工修复,我用论坛上有个大牛写的脚本,我得到的重定位表不对,翻开书静静的看了半小时,开始修复:

先在OD里写代码处理重定位的RVA:




一定要记得把内存1000A400处的数据改成1000A410,否则代码无法执行:


在100030B9处下断点,F9运行,我们看在我们指定的内存中出现了需要重定位的RVA:



从1000A410处拷贝生成的RVA序列二进制文件到winhex中,保存。然后用拽到reloRec中生成重定位表,写入刚才新建的文件8000h处。

忐忑不安的运行自己构建的文件,看来一切ok,有种苦尽甘来的感觉,希望与您分享。
上传的附件 k.rar