[2023]VMP还原day3:模式匹配寻找VIP/VSP和Flow Entry huoji VMP3 2023-02-20 672 次浏览 1 次点赞 在找到vmstub后.我们需要开始做一些基本的操作,获取一些基本的信息.在此之前,介绍一下我们的基本思路 -模式匹配 ###模式匹配 什么是模式匹配? 在vm block初始化之前,vmp需要构造一个虚拟环境, 这个环境包括VIP - 即x64的PC,VSP - 即X64的SP 等等 我们可以直接在汇编中找到: ![](https://key08.com/usr/uploads/2023/02/659761889.png) 但是这个过程中有很多花指令.这会干扰我们的操作,所以我们期望的是"匹配"特定的指令.从而读取一些相关的信息.比如图中的mov rsi,[rsp + %offset]的 rsi就是我们后续需要的VIP 模式匹配代码有很多,novmp啥的都是开源的,可以自己去看看.我这边利用capstone,匹配从给予的地址 到 下次无条件跳转. ###VIP 匹配第一次mov %reg,imm -从上面的case图中很明显看出来 vm entry第一次一定是vip初始化 代码如下,请注意我省略了很多,包括我们还需要记录push到栈里面的寄存器信息等: ```cpp const auto result = match_code( ctx, [&](cs_insn* instruction) { // 这种push一般是开始把寄存器放到栈上了 // push rcx // push rbx // .... // 然后会交换rsp成vip analysis::print_asm(instruction); // ins_id == MOV // if (instruction->id != X86_INS_MOV) return false; // .base == rsp && .index = INVALID // if (instruction->detail->x86.operands[1].mem.base != X86_REG_RSP || instruction->detail->x86.operands[1].mem.index != X86_REG_INVALID) return false; matched_reg = instruction->detail->x86.operands[0].reg; matched_offset = instruction->detail->x86.operands[1].mem.disp; return true; }); ``` ###VSP 同理,也可以很轻松的通过模式匹配的方法把VSP找出来,Vmp 3.x的VM的堆栈是 RBP ![](https://key08.com/usr/uploads/2023/02/2501965229.png) ###flow 所谓流是指,vmp将完整的代码通过jmp拆分成code block,与此同时配有一个滚动地址偏移(rolling offset key) -即必须执行前面的代码,才能执行下面的代码 ![](https://key08.com/usr/uploads/2023/02/3875145.png) 所以匹配到flow也是非常关键的 在vmp的vm stub里面,flow的特征是 lea %reg,[rip - 7] 其中 %reg是叫做 flow reg,用于当前基址 当走完一个block后,会jmp %flow_reg + offset 到下一个block.如此往复 - 这个叫做 FDJ (fetch,decrypt,jump) 因此可以简单匹配到他 ```cpp const auto is_matched = match_code( ctx, [&](cs_insn* instruction) { bool matched = false; do { if (instruction->id != X86_INS_LEA) { break; } if (instruction->detail->x86.operands[0].reg == X86_REG_RSP) { break; } // base是rip if (instruction->detail->x86.operands[1].mem.base != X86_REG_RIP) { break; } // index不是寄存器 if (instruction->detail->x86.operands[1].mem.index != X86_REG_INVALID) { break; } // 偏移 一定是自己的长度 .理论上得是-7 if (instruction->detail->x86.operands[1].mem.disp != -instruction->size) { break; } _ASSERT(-7 == -instruction->size); flow_entry = instruction->size + instruction->address + instruction->detail->x86.operands[1].mem.disp; vm_flow_reg = instruction->detail->x86.operands[0].reg; matched = true; printf("[!!!] matched flow reg at-> \n"); } while (false); analysis::print_asm(instruction); return matched; }); ``` ![](https://key08.com/usr/uploads/2023/02/1696730372.png) 待续... 本文由 huoji 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。 点赞 1
还不快抢沙发