前言
好像距离上一次更新博客很久很久了(咕咕咕),今天是写一篇关于VMPWN的萌新入门学习笔记哈哈哈。
起因是上海大学生原题0解,痛定思痛,以后打过的比赛要及时复现才行233。
室友有陪女朋友的,有打游戏的,只有我孤独学习VMPwn~
VMPwn简介
定义
VMPwn指在程序中实现运算指令来模拟程序的运行(汇编类),或者在程序中自定义运算指令的程序(编译类)。
简单来说就是用程序、代码来实现模拟PC中某个部件。
题目特征
- 代码量大,仔细观察可以发现重复度高
- 存在模拟的寄存器(bp、sp、pc等等)、opcode存储区域、模拟的堆栈区域、模拟的输出存储区域
- 存在一个analyser分析器,用来分析用户输入的opcode
- 循环读取opcode并转化为伪汇编指令
核心点
逆向出程序的代码中每一部分对应的汇编代码,理清程序逻辑。目前大多数题目都是越界溢出,难点在如何逆向。
经典例题
- ciscn_2019_qual_virtual
- [OGeek2019 Final] OVM
- RCTF2020 VM
- 上海大学生 cpu_emulator
下面直接看例题。
[OGeek2019 Final] OVM
题目分析
首先是checksec:64位,除了没canary其他保护全开。

下面直接分析代码,注意这里部分变量为了好理解,笔者有重命名。
主函数代码:
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
这里经过不断调试,踩了个坑:直接输入0xFF000000是不会打印reg[0~15]的,注释处也标记了,按理说这里应该是给PC置大于1的值,然后我们opcode第一个元素写入0xFF000000来绕过,但是这里是给SP置大于1的值。
重头戏在execute(),也就是我们重点要逆向的地方,先来看题目代码:
1 | ssize_t __fastcall execute(int a1) |
首先这里又有个坑,byte3 = (a1 & 0xF0000u) >> 16;
这段代码拿python一跑就知道少了个F,应该是byte3 = (a1 & 0xFF0000u) >> 16;
这种代码算比较简单的,逐段分析就可以逆向出功能:
1 | 0x10: reg[byte3] = a1; |
这里显然是当opcode[i]的高字节等于0x30或者0x40时由于数组下标没有判断,造成数组越界读写漏洞。
解题思路
首先看回main函数,最下面允许用户输入数据到comment[0],然后free(comment[0]):
1 | comment[0] = malloc(0x8CuLL); //(注意)comment是指针数组,元素0的位置存了malloc返回的指针 |
由于数组越界读写我们可以读写内存,如果我们能覆盖__free_hook的地址为system,然后再在comment[0]指向的内存区域写入/bin/sh\00
,即可getshell。
这里比较巧妙,我们让comment[0]指向__free_hook-8,这样一来,comment[1]就会指向__free_hook的地址,那么我们最后输入的时候,输入/bin/sh\00+p64(system_addr)
即可覆盖并传参。
EXP
1 | #coding=utf-8 |
小结
这次复现力求搞懂每一个细节,每一步原理,用Ex师傅的话来说,不要为了刷题而刷题。
发生甚么事了?哦,断点了啊,那没事了。时间到了,该熄灯睡觉了,剩下的下次写~
冲冲冲!