好友
阅读权限 20
听众
最后登录 1970-1-1
对于vmp虚拟机爆破,网上也有相关帖子,如果没有亲身去跟过vmp,那么估计看那些帖子是看不懂的,因为不知道别人为啥要这么做,所以还是要实践,版本是3.2,保护选虚拟,先研究这种情况,自己去写个简单demo去研究
#include <windows.h>
#include "VMProtectSDK.h"
#pragma comment(lib, "VMProtectSDK32.lib")
int fun1() {
return MessageBoxA(0,"yoyo",0,0);
}
int fun2() {
return MessageBoxA(0, "vmp", 0, 0);
}
int main() {
__asm {
mov eax,0x11111111
mov ebx,0x22222222
mov ecx,0x33333333
mov edx,0x44444444
mov ebp,0x55555555
mov esi,0x66666666
mov edi,0x77777777
}
VMProtectBegin("begin");
__asm {
mov eax,0x12345678
cmp eax,ebx
jz lab
call fun1
jmp lab2
lab:
call fun2
lab2:
}
VMProtectEnd();
}
可以看到是恒执行fun1的,那么目标就是爆破去执行fun2,将程序拖入x32dbg,到达main函数,可以看到下面的push 和jmp指令然后进入虚拟机
进入虚拟机之后无非还是保存寄存器,为虚拟寄存器和虚拟栈分配空间啥的,这里不再赘述,要明白的是mov eax,0x12345678 这个立即数是解密出来的,可以慢慢单步跟,随时注意寄存器的值,也可以跟踪下相关条件断点,
最后跟到相关指令处
可以看到此时eax的值是0x12345678,在005B78A5地址处的指令mov dword ptr ss:[ebp], eax 将立即数保存了,然后我们直接下硬断,f9执行
程序中断在这里 mov eax, dword ptr ss:[ebp] 将立即数赋值给eax,下面0x5B527D的指令mov dword ptr ss:[esp+edx*1], eax 又将立即数保存,继续下断,f9执行
此时程序中断在第一次下硬断的访问的地址 可以看到此时会取出原先的ebx:0x22222222,然后将这个立即数写入第一次的断点处,继续f9
又将立即数取出放入ecx,后面地址005C7905的指令mov dword ptr ss:[ebp], ecx 放入ebp所指向的内存,断在ebp所指向内存处,继续f9执行
再次中断后,将立即数0x12345678放入ecx中,下面地址005BD0D6的指令mov dword ptr ss:[ebp], ecx 将ecx保存ebp所指向内存处
接下来看一下堆栈,可以看到这三个立即数,接下来就是vmp的相关操作了,对这三个地址下断(其实只要对上一步ebp地址处下断就行,下面两个在之前已经下了),删除第一次下的断点 没啥用了
f9执行,看vmp是如何对这几个立即数进行操作的 也就是别人说的万用门 与非门 或非门啥的,相关指令
mov edx, dword ptr ss:[ebp]
mov ecx, dword ptr ss:[ebp+0x04]
not edx
not ecx
and edx, ecx
在执行两条mov指令之后 edx=ecx=0x12345678,执行完这几条指令相当于(not edx) and (not edx) = not edx 就是对立即数进行取反(结果=0xEDCBA987),取反后
会将edx存入内存中(不再给图,不然太多了,其实是我懒)删除不必要的断点,继续执行
看下面的操作,将not之后的结果取出放入eax,将原先的ebx值放入edx,然后add eax,edx 再次将eax(此时eax=0x0FEDCBA9),保存,后续只给关键操作的图 取出保存不再给出
后续的操作如下
mov eax, dword ptr ss:[ebp]
mov edx, dword ptr ss:[ebp+0x04]
not eax
not edx
or eax, edx
eax =edx = 0x0FEDCBA9 等价于 (not eax) or (not edx) = not eax = 0xF0123456 到这儿之后我再次下断跟之后已经没有意义了,我百思不得其解,没关系 我们有ai啊 看关键点
也就是说如果eax和ebx相等 那么在执行not (not eax + ebx) or (not (not eax + ebx))结果是0,此时or指令会影响标志位zf 后面vmp是根据这个zf位进行判断的,在执行这个or指令后,我们将zf置1,然后运行
可以看到已经成功执行fun2函数了,也就是说后续vmp是根据这个标志位进行决策 从而决定跳转的,继续研究,观察是如何处理的,重新断在or eax,edx这里,执行后将zf置1,继续跟踪
依旧是下面指令
mov eax, dword ptr ss:[ebp]
mov edx, dword ptr ss:[ebp+0x04]
not eax
not edx
or eax, edx
在执行mov eax, dword ptr ss:[ebp] eax=0xFFFFF7EA 这个值是一个特定的魔数(not eax = 0x815) mov edx, dword ptr ss:[ebp+0x04] edx=0x2c6,就是上面将zf置1后的eflags
执行完or eax, edx 之后,eax= 0xFFFFFD3D,继续跟踪
接下来跟踪到这儿,依旧是熟悉的指令
mov eax, dword ptr ss:[ebp]
mov edx, dword ptr ss:[ebp+0x04]
not eax
not edx
or eax, edx 执行完之后eax=0x2c2
之后进行了一个eax+5 也就是0x2c2+5=0x2c7 暂时看不懂有啥用(应该也是魔数?),依旧保存,继续跟
依旧是熟悉的代码
mov edx, dword ptr ss:[ebp]
mov ecx, dword ptr ss:[ebp+0x04]
not edx
not ecx
and edx, ecx 执行完and指令之后edx=0xFFFFFD38
接下来还是熟悉的代码
mov edx, dword ptr ss:[ebp] //edx =0xFFFFFFBF(魔数)
mov ecx, dword ptr ss:[ebp+0x04] //ecx = 0xFFFFFD38
not edx
not ecx
and edx, ecx 执行完and指令之后edx=0x40 二进制0100 0000 位6刚好对应eflags的zf位
然后开始右移判断zf位, 此时eax=0x40 cl=6 shr eax,cl之后eax=1 此时判断出zf的值
那么我们可以写出在右移之前的大致表达式
edx = 2c6
eax = 0xFFFFF7EA
(not 0xFFFFFFBF) and(not((not(((not((not eax) or (not edx))) or (not((not eax) or (not edx))))+5)) and (not(((not((not eax) or (not edx))) or (not((not eax) or (not edx))))+5))))
看起来非常的眼花缭乱 没事交给ai 等价于 not(0xFFFFFFBF) and ((not eax) or (not edx) + 5),再计算得到 0x40。
可以看出来vm使用大量的万用门来进行复杂的计算 相当的恶心人啊 再右移得到1,如果两个值不相等 那么右移之后结果就是0了.继续看后续操作
代码如下
mov eax, dword ptr ss:[ebp] //eax=0xffffffff
mov edx, dword ptr ss:[ebp+0x04] //edx=1 即右移的结果
add eax, edx //相加 如果是edx=1 那么add之后eax=0 继续跟,可以看到依旧是熟悉的代码 这里不在写出来了
继续跟,还是熟悉的配方
mov eax, dword ptr ss:[ebp] //eax=0x0xffffffff not 0之后的结果
mov edx, dword ptr ss:[ebp+0x04] //edx=00482F2B
not eax
not edx
or eax,edx //eax = 0xFFB7D0D4
这里不再给图 只给后续逻辑代码
mov eax, dword ptr ss:[ebp] //eax = 0xFFB7D0D4
mov edx, dword ptr ss:[ebp+0x04] //edx = 0xFFB7D0D4
not eax
not edx
or eax,edx //eax = 0x00482F2B
mov eax, dword ptr ss:[ebp] //eax=0
mov edx, dword ptr ss:[ebp+0x04] //edx= 0x00482F2B
add eax,edx //eax= 0x00482F2B
mov eax, dword ptr ss:[ebp] //eax=0x140000
mov edx, dword ptr ss:[ebp+0x04] //edx = 0x00482F2B
add eax, edx // eax = 0x005C2F2B 此时得到类似决策的opcode指针
为啥这么说
mov ecx, dword ptr ss:[ebp] // ecx = 0x005C2F2B
mov edi, ecx
mov ebx, edi //ebx也是解密key 贯穿整个vm虚拟机流程 重新赋值解密key
上面这个图是刚进入虚拟机时会走的分支 解密得到opcode尾指针 更新ebx key 然后继续执行后面的代码
此时我们可以看zf=0的情况 ecx = 0x005C2FBD 和上面ecx = 0x005C2F2B不一致 从而执行不同的分支 key不一样 opcode尾也不一致了 至此整个爆破流程大致分析结束,不足之处多多包涵
结语:今天刚好是25年最后一天,明天元旦 祝愿各位网友万事如意,工作顺利,身体健康,祝愿吾爱越办越好
免费评分
查看全部评分