首页
社区
课程
招聘
[原创]从0开始手撕64位vmp3.5壳(能撕但只能撕一点点)
发表于: 1天前 447

[原创]从0开始手撕64位vmp3.5壳(能撕但只能撕一点点)

1天前
447

本文适合0基础的小白,对于老鸟来说此文太过简单。作者的水平也只能脱壳出几行汇编,由于作者对于VMP了解有限,本文主要介绍大致技术方案,文中如有原理性错误,还望轻喷。

从2021年开始似乎逆向圈就用手撕VMP来称呼大佬,当时刚入门逆向的我也想看看VMP是什么,结果就被一堆CALL和JMP劝退了。近期又搜索了很多VMP相关课程,但是能看到的就是老师们一顿操作猛如虎然后留下一个命令行窗口的demo.exe。看着几百行的demo 又或者看着手速极快的视频,对于每天只有闲散的一两个小时用来研究的我简直是折磨,刚看明白哪个寄存器是哪个,手一抖点错了又要从新开始,或者今天刚有点眉目,迫于生活工作压力要停下当前的学习,过两天再看又是一头雾水,等理清楚了VMP的寄存器和堆栈,今天的闲散时间又结束了。

于是我编写了 一个辅助程序DisassembleVmp,这次我的学习策略是只要略懂原理就上手敲自动化代码。毕竟有AI加持很快就弄出了exe。

上图是我开发的工具,开源地址(1034063174/DisassembleVmp: VMP分析工具

这个工具的主要功能已经标记出来

①这个是选择要连接的x64Dbg

②这是能执行各种lua工具

③这个是显示解析出的handle

④显示handle的汇编,其中红色的表示该汇编属于混淆

⑤ 这里是通过unicorn执行汇编获取出来的寄存器,其中有原始汇编执行结果,还有去混淆后的执行结果。这样就可以通过寄存器执行结果判断是否去混淆逻辑标记了有用的汇编。

⑥输出界面,输出lua的日志

⑦显示执行汇编获取出来的堆栈

⑧和⑥一样是现实日志的,区别是⑧是绑定Handle的,UI上切换handle看到的日志是不一样的。

上述只是大概介绍下ui的各个功能,后面还会详细介绍


由于是64位,没有安装插件不能内联汇编,因此单独创建asm文件,如上所示。这个demo很简单 只有push pop sub  add  mov,main函数调用这个就行

加壳工具选择3.5.1 我是拿之前网上下的源码编译的,当然这次逆向和vmp源码没有关系,只是恰好我电脑上有这个版本

加壳的时候选择pushtest函数,图中选择的另一个函数是我用来研究for循环的,目前还没有结果不用理睬。

如下图所示,加密的时候把各种保护都关掉,我们只研究虚拟化。

已知虚拟机加密函数,是把函数的入口替换掉,从而进入虚拟化。那么找到原函数的调用入口就能找到虚拟机的入口

加壳前的调用函数入口

加壳后也定位到这里  可以看到 加壳后call的位置是没变得(exe基地址+固定偏移),跟进去就是虚拟机入口了

5

下图就是虚拟机入口了   push  call 就是标志。

再往里面走几个jmp跳转 就是虚拟机初始化

虽然加壳的时候我们选择的是仅虚拟化,但实际上还是有混淆的,而且还有很多jmp之类的,分析起来十分麻烦。

如果不去混淆对于初学者来说十分折磨。因此先放下对于虚拟化的好奇心,开始老老实实去混淆。

去混淆大致方案:

汇编转换成为PCode 语义 也就是读取了什么寄存器,写入了什么寄存器

从上往下进行判断,举个最简单的例子 mov rax ,0x123;   mov rax ,0x456。 如果两行汇编之间没有任何读取rax的指令那么  mov rax ,0x123就是没用的,就是混淆。

将会遇到的问题:

对于去混淆来说,实际静态就可以,接下来我们讨论下将要遇到的几个问题

1.去混淆要写一些匹配逻辑,逻辑写的规则太严谨会导致去混淆不彻底,写的太松散,导致去混淆可能把真实代码也删掉。这就需要不断地测试,用眼睛看是不是把正常代码当混淆了,这工作量太大了。

2.去混淆应该怎么去除。代码里有各种跳转,是跟着跳转进去去混淆还是按汇编地址逐行去混淆?遇到jz 是跳转还是不跳转? 因此不判断出执行顺序就不会清楚下一行汇编会执行什么,就不容易高效去混淆。

解决方案:

先模拟执行,确定汇编执行流程并且记录下来所有寄存器还有常用的堆栈(比如RSP±0x100,记录太多了实在是存不下)这样去混淆就有正确的路径并且去混淆后再次模拟执行对比寄存器就能知道去混淆是否正确。当然这也有一些问题,我们后面讨论

核心:Unicorn 模拟执行,按需内存映射。

通过 IPC 从 x64dbg 读取目标进程的寄存器快照和内存页

初始化 Unicorn 引擎,写入寄存器和已有内存页

注册两个 hook:

cb_code:每条指令执行前记录地址、寄存器快照(16 个 GPR + RFLAGS)、栈快照(RSP 上下各若干 slot)

cb_unmapped:遇到未映射页时,通过 IPC 从 x64dbg 实时读取并映射(按需加载)

模拟执行直到达到步数上限,产出一条 TraceEntry[] 序列


核心:将 x86 指令翻译为 Ghidra Sleigh PCode 原子操作。

每条 x86 指令被翻译为若干 PCode 操作(RawOp),每个操作有:

opc:操作码(COPY, INT_ADD, LOAD, STORE, BRANCH, CBRANCH 等)

out:输出 varnode(space + offset + size)

ins[]:输入 varnode 列表

dead:是否被死代码消除标记

Varnode 地址空间:

register:CPU 寄存器(offset 对应 Ghidra 寄存器编号,如 rax=0x00, rcx=0x08, rsp=0x20)

const:立即数常量

unique:Sleigh 临时变量(指令内部中间结果)

ram:内存地址空间

示例:mov r11, [rsp+0x90] 翻译为:

核心:反向活跃性分析 + RuleVmpEarlyRemoval。

这是去混淆最关键的一步,直接对应 VmpHelper 的 ActionVmpHandlerDeadCode。


传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!

最后于 17小时前 被卑鄙兔编辑 ,原因:
收藏
免费 20
打赏
分享
最新回复 (12)
雪    币: 2577
活跃值: (3166)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
你是想笑死我吗,也不排版也能通过审核
1天前
0
雪    币: 25
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
盖了帽了,图片怎么发出来看不到
1天前
0
雪    币: 247
活跃值: (9538)
能力值: ( LV7,RANK:102 )
在线值:
发帖
回帖
粉丝
4
有了AI,感觉把控制流还原出来就够了,算法和逻辑什么的都交给AI去搞
1天前
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
666
19小时前
0
雪    币: 29
活跃值: (1110)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
6666666666
19小时前
0
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
感谢分享
18小时前
0
雪    币: 158
活跃值: (5576)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
感谢分享 
13小时前
0
雪    币: 0
活跃值: (4765)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
666
13小时前
0
雪    币: 20
活跃值: (570)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
66666666
12小时前
0
雪    币: 3518
活跃值: (7708)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
11
学习
7小时前
0
雪    币: 677
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12
这不狠狠学习?
3小时前
0
雪    币: 677
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
fjqisba 有了AI,感觉把控制流还原出来就够了,算法和逻辑什么的都交给AI去搞[em_003]
现在vmp最新版本的好像破解不了了
3小时前
0
游客
登录 | 注册 方可回帖
返回