其实壳本身并非关键,真正的难点在于虚拟机内部的校验机制。多亏了 SM 同学手下留情,勉强能让它运行起来,但实在没有精力去完整还原整个 VM。在 VirtualFree 的 retn 指令上按下 F4,直到 [ESP] 指向某个 EXE 镜像内的地址,然后按 F7 返回:
0040FA91 B8 BE180000 mov eax, 18BE
0040FA96 BA 00004000 mov edx, slv_unpa.00400000
0040FA9B 03C2 add eax, edx
0040FA9D - FFE0 jmp eax
在 jmp eax 处按下 F4 再按 F7,即可跳转到 OEP:
004018BE 68 EA1AF500 push 0F51AEA
004018C3 - E9 EF01B500 jmp 00F51AB7
004018C8 CC int3
004018C9 CC int3
结果却跳进了 VM 段,那片内存区域无法直接 Dump。按 Ctrl+F2 重新开始,观察内存分配的位置:在 VirtualAlloc 函数上下断点,持续按 Ctrl+F9,直到 EAX 等于 0x00F50000 时停下:
0040FBB6 6A 40 push 40
0040FBB8 68 00100000 push 1000
0040FBBD 50 push eax
0040FBBE 6A 00 push 0
0040FBC0 FF13 call [ebx] ; kernel32.VirtualAlloc
0040FBC2 8BD0 mov edx, eax
0040FBC4 8BFA mov edi, edx
0040FBC6 8B4E FC mov ecx, [esi-4]
0040FBC9 F3:A4 rep movsb
继续向下执行,ECX = 0x000151E9,原来是在将 unpackme.exe 复制为 2.exe,同时新增一个节:VAddr=0x0022000,VSize=0x20000。加载 2.exe 后,在 0x0040FBC2 处按下 F4,将 EAX 修改为 0x422000。然后按照上述方法走到 OEP,使用 LordPE 进行 Dump(OllyDump 我从未成功使用过),得到 dumped.exe。
接着用 ImpREC 修复 IAT,生成 3.exe。一运行就报非法操作,挂上 OD 调试:
00422CCA 8910 mov [eax], edx // eax=00140688
00422CCC E9 0D0E0000 jmp 00423ADE
往上看全是花指令(实际上都是 EB 开头的跳转),直接 Nop 掉:
00422CA4 33C0 xor eax, eax
00422CAC AC lodsb
00422CB1 8B1487 mov edx, [edi+eax*4]
00422CB7 33C0 xor eax, eax
00422CBD AC lodsb
00422CC1 8B0487 mov eax, [edi+eax*4]
00422CCA 8910 mov [eax], edx
00422CCC E9 0D0E0000 jmp 00423ADE
看起来只是一个虚拟机指令,但似乎丢失了一些关键信息。校验逻辑到底藏在何处?
思路一:GetFileSize 校验。估计 SM 同学不屑于使用这种低级手段,抱着侥幸心理试了一下,果然无效。
思路二:在某处设置了标记。之前 VirtualAlloc 分配的内存已被释放,VM 段也 Dump 下来了,还能在哪里?联想到 SM 同学一向喜欢在 PE Header 做手脚,于是去查看 PE Header。加载 2.exe 时,在 OEP 按 Alt+M,在 PE Header 上 F2 下断,果然触发了访问:[400110] == 1000。加载 3.exe 同样中断:[400110] == 18BE。
原来校验的是入口点,后续很可能当作常数参与了运算——因为所有 KBYS 都会将 0x1000 作为入口。解决方案有两种:一是编写一段入口代码将入口改为 0x1000;二是挪动入口。考虑到写保护操作不便,我选择了挪动。在 0x401000 处 Ctrl+R 找到两处引用,均改为 0x401005:
004011DA E8 21FEFFFF call 3.00401000
004011EC E8 0FFEFFFF call 3.00401000
修改此处代码:
00401000 > /E9 B9080000 jmp 4.004018BE
00401005 |68 EA9C4200 push 4.00429CEA
0040100A -|E9 A82A0200 jmp 4.00423AB7
保存到文件,最后用 LordPE 将入口点改为 0x1000,保存为 4.exe。嗯,图片显示出来了!但点击“She”之后程序没有自行退出,反而又弹出了自己。估计是虚拟机里有一段调用 0x1000 退出的代码,结果跳到了 0x18BE 又执行了 DialogParam。那么只能选择写保护方案。用 LordPE 添加一个输入函数 VirtualProtect,在 OEP 下方找一片空地编写代码,最后跳回 0x18BE:
004018C8 > B8 00004000 mov eax, 00400000
004018CD 0340 3C add eax, [eax+3C]
004018D0 8D78 28 lea edi, [eax+28]
004018D3 50 push eax
004018D4 54 push esp
004018D5 6A 04 push 4
004018D7 6A 04 push 4
004018D9 57 push edi
004018DA FF15 1E304400 call [44301E] ; kernel32.VirtualProtect
004018E0 58 pop eax
004018E1 B8 00100000 mov eax, 1000
004018E6 AB stosd
004018E7 ^ EB D5 jmp 004018BE
将入口点改为 0x18C8,搞定收工。
