[2023]VMP还原day4:简单计算vm block跳转地址 huoji VMP,NOVMP 2023-04-16 773 次浏览 0 次点赞 目前的进度: ![](https://key08.com/usr/uploads/2023/04/2759092668.png) 从 VMP还原day3:模式匹配寻找VIP/VSP和Flow Entry 中,简单说了一下FDJ,我们也已经介绍了虚拟机是如何初始化的.现在,是时候进入第一个handler了. 记得上一章节说的: > 当走完一个block后,会jmp %flow_reg + offset 到下一个block.如此往复 - 这个叫做 FDJ (fetch,decrypt,jump) FDJ有几个比较重要的信息是offset寄存器、offset寄存器的算术表达式(如mov,push,pop之类的)和base地址(在flow reg中). 这是一个展示: ![](https://key08.com/usr/uploads/2023/04/599660945.png) 暂且不论中间的虚拟机指令(如vpush、vpop等),我们先追踪到第一个handle的位置: 这个offset寄存器一定是sub VIP,4后的mov offset, [VIP] 如根据上一章,我们匹配到的VIP是RSI,那么如下图所示,offset寄存器是EAX: ![](https://key08.com/usr/uploads/2023/04/276263081.png) 之后追踪这个offset寄存器的运算表达式,用于后续解密 ![](https://key08.com/usr/uploads/2023/04/657636140.png) ![](https://key08.com/usr/uploads/2023/04/2142545906.png) ![](https://key08.com/usr/uploads/2023/04/3367192118.png) ![](https://key08.com/usr/uploads/2023/04/3416511670.png) 如上所示,只需要追踪eax的表达式(更直白一点,追踪寄存器写入),就能计算出offset寄存器的最终偏移: ```cpp match expression->0x00219739 : rol eax, 1 match expression->0x001FED26 : not eax match expression->0x001B8E68 : inc eax match expression->0x0023D755 : bswap eax ``` 追踪/解密部分代码可以参考novmp.已经有完整的代码了.这部分不建议自己造轮子 之后我们还需要匹配一个rolling key寄存器,rolling key reg顾名思义放着滚动的密钥的寄存器.直接匹配offset寄存器后面有没有被xor过就行,如第一张图片所示,rolling key的匹配点应该是xor eax, ebx 而寄存器就是ebx... rolling key的内容第一个内容是当前VIP地址(通过前文提到的匹配VIP的计算表达式获得) 没有问题后,我们就可以计算出第一个vmblock的地址,其中,表达式可以简单理解为一个混淆函数对这个寄存器做各种加法减法除法: 地址 = 表达式解密 (VIP地址(没有被表达式解密前) xor rollingkey) + base寄存器 ```cpp // 计算出下一个vmblock的rva同时更新ctx auto calc_next_vm_block_rva(lifter::tracker::_ctx* ctx) -> uint64_t { // 下一个函数地址是rolling key ^ VIP的值(要根据方向+4或者-4去读.因为这个时候已经是被-+4后了的) // auto next_handler = vm_block_vip_read(ctx, 4) ^ (uint32_t)ctx->vm_run_env->rolling_key; // 通过之前的表达式计算出下一个handle地址 next_handler = static_cast( ctx->vm_info.fetch_reg_expression->compute(next_handler)); // 更新rolling key ctx->vm_run_env->rolling_key ^= next_handler; // 模拟movsxd struct { int64_t sign : 32; } s; s.sign = next_handler; ctx->vm_info.basic_flow_entry += s.sign; // 返回下一个跳转 return ctx->vm_info.basic_flow_entry; } ``` 让我们对比一下: ![](https://key08.com/usr/uploads/2023/04/2253094494.png) 应该是没问题的.我们成功的计算出第一个block的地址了 注意: 老外博客里面,最后的跳转是jmp跳转: ![](https://key08.com/usr/uploads/2023/04/2575221259.png) 我这边是add offset_reg,base_reg后 push 的栈跳转: ![](https://key08.com/usr/uploads/2023/04/818327045.png) ![](https://key08.com/usr/uploads/2023/04/1500545414.png) 本文由 huoji 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。 点赞 0
还不快抢沙发