【原创】深入浅出VMPIAT修复
本帖最后由 R-R, 于 2024-1-12 17:52 编辑<0>我看了现在大一部分帖子,在修复VMPIAT的方法上都是通过扫描代码段的pushreg call,call ret这种特征来进行的对应地址修复。我想也许应该有种更好的方法来修复VMP的IAT。
<1>我以VMP3.85的版本示例来演示另外一种修复的方法,这里因为我们只需要修复IAT,所以在资源加密上我选择了否。
<2>VMP3.8以下的版本,在API调用的时候一般是以lea reg,dword ptr ds:这种形式调用,但在3.85的版本上可以看到在API的调用上,
lea reg,dword ptr ds:,显然,在3.8的版本上API的调用也带来了一些变化。
但这并不影响,在图下API的调用上,会有reg+reg调用,这里我回退看了下两个寄存器的值
mov edi,0x8438AD1A
mov eax,dword ptr ds:
lea edi,dword ptr ds:
Eax是以内存的形式存储,edi的值则是以一种固定的形式计算。
<3>思路打开:那么现在可以推断出,VMP首先会调用自建的GetProcAddress来获取表里的API地址然后经过计算得出Reg的存储在内存的值,
在调用时就会取出这个内存中的值经过在vm代码里面的计算得出API地址,那么现在我们抛开一切不必要的因素找到重点:找到它计算后的内存值,和对应的API地址,然后我们构建一段汇编代码模拟vm的计算方式来计算出内存值填充进去。
<4>上面图中的eax值是从vmp段里获取的,对地址下硬件写入断点,运行几次在出现到内存值时如下图,Eax为计算后的内存值,ecx为写入值的内存地址,esp+10为对应的API地址。
<5>脚本构建思路,现在得到了计算后的值和对应的API地址,那么我们要获取到那个被减值,也就是用来计算内存值的那个固定值,
用脚本构建的话,获取到被减值,内存写入地址,API名,和对应的模块名,如下图,为了方便快捷,我选择了友好的易语言,当然脚本应该会更快,获取方法如下图。
<6>在就是修复了,有了这些信息,完全可以构建汇编来模拟vmp的计算方式。
为了快捷方便,我选择了友好的易语言来构建dll。
首先我们得到了计算值,计算值大部分会产生借位问题,这里只需要保留低八位即可,因为在lea的时候越位也会舍去高位,这也是vmp的一个精妙设计之一。
有了被计算值,有了模块名和对应的函数名,那么在写dll时就可以这样:通过函数名和模块名获取到对应的函数地址,然后通过函数地址减去固定的被减值,
得到新的内存值,然后写入对应的内存地址,最后绑定dll导出函数,在运行OEP之前写入内存值,这样无论在任意机器上我们就仍然可以保证vmp在vm里调用API地址时一直都是正确的API了。
dll代码如下图
<7>还有一点,vmp在对Getversion和GetversionEx特殊照顾,他是直接保留在内存里,在脱壳时可以在vm段里扫到这两个地址然后重建出来。
<8>我经过了几个版本的脱壳测试,这种修复方式还算是比较稳定高效的。
<9>题外话:在vmp脱壳时iat修复其实算的上是比较小的一个问题。
资源的修复,vmp的vm校验,这些比较难,当然,其实深入一点也不是很难。
在资源修复上补全PEB.heap,HeapCreate两段内存,找到vmphook的函数,然后照着挂钩上即可。
至于vm函数里的校验,补版本的低四位和cpuid即可,当然,我选择更好的方式就是找到cpuid的算法,然后用算法计算出hash写到校验内存里,这样所有的vm函数都能在脱壳时过掉,而不需要补所有的cpuid。
最后就是vmp的授权,vmp的授权在没加sdk时可以直接至0过掉,这个地方是比较简单的,最难的一点是vmp在vm函数时有个锁定到序列号,这个校验点除了Patch机器码和keygen之外,我发现了另外一直方法,只需要补全它的ProductCode即可。
VM保护导入表函数后, 会有一个字节额外的空间, 可能会被写入0xC3 然后VM的函数会随机跳过来, 所以加了V的. 修复IAT后大概率还是不能运行 感谢分享 学习了。。。。。。。。。。。。。。 感谢分享!!! 想楼主大佬学习 感谢分享 感谢大佬分享!! 感谢分享 感谢分享 each 和edi代表什么
页:
[1]
2