一、IAT保护方式
原函数调用代码:
1
| 004018EE FF 15 0C 30 40 00 call ds:__imp__GetSystemTimeAsFileTime@4
|
经过IAT保护后:
1 2 3
| 00D118FD | 52 | push edx | 00D118FE | E8 66A33000 | call vmpfuck.vmp.101BC69 | 00D11903 | 3145 FC | xor dword ptr ss:[ebp-4],eax |
|
由于原字节大小为6,vmp保护后使用了call vmpSeg
使得字节为5,但由于要统一,因此添加了一个push reg指令。
跟进函数后,使用调试器的跟踪功能进行记录,得到代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 0101BC69 | 90 | nop | 0101BC6A | 99 | cdq | 0101BC6B | BA 49578563 | mov edx,63855749 | 0101BC70 | 0FCA | bswap edx | 0101BC72 | 5A | pop edx | ;弹出堆栈,抵消外层的push edx,同时获取返回到地址,即edx=0x00D11903 0101BC73 | E9 33B9EBFF | jmp vmpfuck.vmp.ED75AB | 00ED75AB | 871424 | xchg dword ptr ss:[esp],edx | ;重新讲返回到地址写入到栈顶 00ED75AE | E9 7C020F00 | jmp vmpfuck.vmp.FC782F | 00FC782F | 52 | push edx | 00FC7830 | BA 1715D100 | mov edx,vmpfuck.vmp.D11517 | 00FC7835 | 8B92 9FAE2B00 | mov edx,dword ptr ds:[edx+2BAE9F] | 00FC783B | 8D92 48604129 | lea edx,dword ptr ds:[edx+29416048] | ;GetSystemTimeAsFileTime函数地址 00FC7841 | E9 2657F1FF | jmp vmpfuck.vmp.EDCF6C | 00EDCF6C | 871424 | xchg dword ptr ss:[esp],edx | ;写入到栈顶 00EDCF6F | E9 172FE5FF | jmp vmpfuck.vmp.D2FE8B | 00D2FE8B | C3 | ret | ;调用api函数
|
二、修复方法
经过多次尝试发现,IAT函数的调用全部指向VMP0,且函数头部都为nop
指令。


因此可以直接暴力搜索.text
段的E8 ?? ?? ?? ??
,然后计算出call的地址是否属于vmp0
段,再判断函数头部是否为nop指令来进行修复。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| for (int n = 0; n < detail->x86.op_count; n++) { cs_x86_op* op = &(detail->x86.operands[n]); switch (op->type) { case X86_OP_IMM: { ULONG_PTR callAddr = op->imm; if (callAddr > segs[3].base && callAddr < segs[3].base + segs[3].size) { unsigned char nop=NULL; uc_mem_read(uc, callAddr,&nop,1); if (nop == 0x90) vec_iatList.push_back(insn->address);
} } default: break; } }
|
将符合条件的添加在vec数组中,然后进行unicorn模拟执行到ret。
由于模拟环境中没有api模块,因此会产生异常,通过检测异常前的指令是否为ret
来进行修复。

此时堆栈的值如下表:
addr |
value |
esp |
api函数地址 |
esp+4 |
正常返回到地址 |
因此读取栈顶值即可获取到api地址,然后再遍历模块中的IAT进行函数名获取。

然后修复esp
和eip
来使模拟器继续正常运行。

修复效果如下:

github:https://github.com/PlaneJun/vmp_fixiat