一般指 CTF Pwn 里“虚拟机解释器类题目”:题目程序自己实现了一套自定义指令集/字节码和一个解释器(VM),把很多类似“汇编/CPU”的概念(寄存器、栈、内存段、指令执行循环等)用代码模拟出来,然后你要在这套 VM 的实现里找漏洞并利用
主要难度在逆向分析处
BUU
在main函数中。第一部分输入合适的pc(opcode的开始index),sp(栈顶,这里有一个sp+code_size的大小校验进行边界限制),code_size()记录执行的指令的条数

第二块中fetch会依次取出opcode
然后在execute进行匹配调用,主要是把opcode分为四块
1 | instr --> op | dst | op2 | op1 4B 32bit |

第三块向刚才malloc的地址comment进行一个读入,并最后free掉这个chunk

解题思路
通过改变commant的地址为free_hook-8的地址,并调用system后门函数
由于
0x30 –> reg[dst] = memory[reg[op1]] –> mov mem, reg
0x40 –> memory[reg[op1]] = reg[dst] –> mov reg, mem
两条opcode不会进行校验,存在整数溢出的漏洞,而且我们的memory是bss段上的,所以可以去附近查找一个libc地址,这里提取的是0x00007ffff7dd2700,并把它存储在reg[3]reg[2]中

1 | # copy stderr addr --> reg[3]reg[2] |
接着计算和free_hook-8之间的距离,并把它通过计算偏移计算free_hook-8的地址

最后把comment的值赋为free_hook-8的地址
1 | sl(gen_code(0x10, 4, 0, 8)) # reg[4] = 8 |

最后传入后门函数就好了
1 | ru("R2: ") |
因为最后是free(commant),对commant输入bin/sh+p64(system),参数放在commant上,system放在free_hook处,那么调用free(commant)时就会调用system(“bin/sh”)
exp
1 | from pwn import * |
参考了__lifanxin师傅的讲解
new_star GO?
文件的结构
1 | go |
发现是zip文件先
1 | ┌──(p0ach1l㉿ZZH)-[~/Desktop/pwning] |
发现解出来的是tar
1 | ┌──(p0ach1l㉿ZZH)-[~/Desktop/pwning] |


初始化放置指令集

规定opcode的对应操作
分析
整理得到指令数集(可以用ai分析)
1 | ============================== |
原order中的内容
1 | col: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 |
case '~': // 读入 char = getchar();中getchar()是从 stdin 读 1 个字节stdin / socket 是一个字节队列缓冲区:
send() 进去 300 个字节
↓
内核缓冲区保存这 300 个字节
↓
程序每次 getchar() 只取 1 个,所以可以从终端输入多个字符
~ : 25* - |其实是一个\n校验,\n的ascll码是10
如果是\n,方向向上下,不是\n,方向向上,然后<v回来继续执行
对此输入我们的payload,相当于把他们存入栈上了,最后因为sendline发送的\n结束这个循环(row=3,list=2)
1 | payload = b'a'*0xff + b'&' + b'c' + b'>' |
变成如下,但是此时index的数值1
1 | col: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 |
程序会在这R0打转,循环读入,我们让它读入255个字节覆盖到order
1 | payload=b'1\t'*0xff |
接着就是泄露got表地址,根据栈上的got表的偏移控制它的index
pack(payload)把一个Befunge 指令串(比如b'&&&g.&$$')当作 8 字节 little-endian 的整数,转成十进制字符串再加换行发给程序:
- 因为题目里
&指令是scanf("%ld",&v),一次读一个 long 压栈;- 栈元素是 qword(8 字节),所以把 8 个字符的指令“塞进一个 long”最方便;
payload.ljust(8, b'\x00')是不足 8 字节补 0。这允许你用数字输入,精确控制“栈上那 8 字节在解释器里会被当作什么指令/数据”。
泄露libc的opcode表
1 | &&&g.&$$ |

覆盖返回地址的opcode表
1 | &&&g.&$$ |
脚本
1 | from pwn import* |
看字符串 x/80cb $order+80
set $pie=
p/d (int)($pie + 0x50F0)
set $index =(int*)($pie + 0x50F0)
set $order = (char*)($pie + 0x4920)
set $stack = (char*)($pie + 0x4120)