-
-
[原创] vmp3.9.4 手动还原cmp/jne下的handler序列
-
发表于: 2天前 1232
-
这几个月学习vmp以来一直没尝试过自己亲手还原vmp,所以就想着试试反正这几天也没课。花了一整天时间,从上午到晚上总算完成了,工作量虽然大,但也是一次很好的学习经历。
我还不知道要遵循什么命名规范,比如VR寄存器要不要带q\d\w\b后缀来表示使用到的虚拟寄存器的宽度,运算宽度是用字节数(1/2/4/8)来表示还是用位数(8/16/32/64)来表示,专属寄存器如指示字节码位置的是叫 vpc 还是 vip 等等,后面我觉得各自按自己的喜好来规定就可以了。
在这里,我把指示字节码位置的专属寄存器叫 vpc、指示虚拟堆栈的叫 vsp、指示当前handler位置的叫 vbase、指示滚动密钥的叫 vkey。用来寻址虚拟寄存器的就是 rsp,它就不用起什么名了,但真要取的话我就叫它 vreg。

r10d 是我们的输入,如果输入值为 3735928559(0xdeadbeef),rax 的值就会变成 0xdeadbeef。


随便提一下,根据我的经验,10% 的复杂性可以让一个复合 handler 有最多两个简单 handler,而 20% 就是最多三个。因为我样本量不够,也不能真的保证 10% 的复杂性就是最多两个简单 handler,但至少在最后的分析结果来看,10% 的复杂性里,一个复合 handler 就是最多只有两个简单 handler。
100% 复杂性的话,就是最多十个,下面是我分析的其他100%复杂性样本的 handler 序列,可以简单看一下:
加壳完成后,我们就用 x64dbg 对被vm的代码进行跟踪采集,输入正确的跟踪文件是 success.trace64,输入错误的跟踪文件是 failed.trace64


如果直接通过看跟踪来一个个分析 handler 的话,如此多的混淆代码会让人看的头晕眼花的,因此我们需要留下真正有意义的代码再进行分析
首先是从追踪里分割 handler:
然后就是关键代码了,我们主要靠这段代码的输出来做后续的工作:
代码的思想就是污染所有与vm虚拟机机制有关的代码,包括专属寄存器 (vpc、vsp、vkey、vbase),以及可能的特殊指令(比如 cpuid、rdtsc),污点规则(无论是对有意义代码的保留、还是对混淆代码的去除)到时候灵活定制即可,现在由这套代码输出出来的代码经过我最后的还原,我发现效果非常好,只有少数的handler还是把混淆代码给带进来的,但也无碍。
专属寄存器里不用单独污染 rsp,污染它的话会带出很多混淆指令,我们只对与 rsp 寻址有关的指令污染即可。比如 lea rdx, qword ptr ss:[rsp+0x20],我们只污染 rdx。
这套代码还不能灵活处理专属寄存器变更的情况,所以使用前还是得先自己去跟踪里手动追一下,比如在索引 0x35e9 之前的 (vsp, vpc, vkey, vbase) 是 (rsi, rbx, rdi, rbp),在 0x35e9 索引之后就变成了 (r8, rdi, r9, rbx)
那个 VRLIMIT 的值是 checkVsp 里进行 rep movsb 复制的总大小(也就是 ecx 寄存器的值,rep movsb 指令的复制大小是看 ecx ):

我们也得先手动在跟踪里找出这个值,通过这个值,我们就能确定真正的虚拟寄存器的界限是多少,因为 checkVsp 的作用是重新提升虚拟栈,它自然就需要通过复制数据来提前保护虚拟寄存器的安全,因此观察它复制了多少字节,就能知道虚拟寄存器的准确界限。在这次的vm样本里,我们的 VRLIMIT 是 0x100,(0x100 // 8) = 0x20 = 32,我们的虚拟寄存器区间就是 VR0 ~ VR31。
[培训]《冰与火的战歌:Windows内核攻防实战》!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。