程序分析
本题目是一道典型的虚拟机题目,通过这道题目,我们可以学习一下VM pwn题的分析技巧和利用思路。
该题目程序和远程环境可以在buuoj上找到,下面老规矩先检查下程序信息,64位小端程序,没有开启栈保护。
做虚拟机的pwn题,我们可以分两大步骤进行,首先先分析一下这个程序是如何实现虚拟机功能的,也就是程序自身的大逻辑,然后我们再详细分析程序虚拟出来的指令,分析指令的过程可能会比较耗时,需要耐心。
程序主逻辑分析
我们首先来看一下程序的大逻辑,程序本身是没有去除符号表的,再结合自身的理解,可以将程序恢复如下面截图所示。先看main函数前半部分,comment是malloc出来的一块内存,然后该程序会要求我们输入pc值,sp值以及code size。这里的pc也就是程序指令索引,sp可以看成栈指针,code size也就是指令条数。之后会结合sp和code size对我们输入的数据做一个简单的判断。这里可以注意一下reg[]这个数组,该数组对应虚拟机的寄存器,可以看到sp和pc分别存储在reg[13]和reg[15]中。
我们再来看一下main函数的后半部分,如下截图所示,这里会根据我们输入的code size也就是指令条数来循环读取指令。然后令running = 1,启动虚拟机,fetch()会根据reg[15]也就是pc值来取指令,取完一条后自增一;execute执行指令功能,对应程序虚拟出来的指令。最后当虚拟机退出时,我们可以向comment写入内容,sendcomment实际功能是free掉这个堆块,之后程序结束。
虚拟机指令分析
接下来,我们进入第二步,分析出程序虚拟的指令。这里execute函数代码比较长,就不再一步步分析,我们可以看一下代码最开头的逻辑就知道如何分析了。
如下截图所示,结合前面读取指令使用的格式化符号%d
,以及这里的处理逻辑,我们可以分析出指令字长(instr)为32bit。进一步分析前面几行代码,我们可以推断出该指令为三操作数指令,取4字节最高位字节作为操作码,dst是第二个字节的低四位,op2是第三个字节的低四位,op1是最后一个字节的低四位。这里只取低四位,是因为后面的操作都是在reg寄存器中,所以只需要低四位即可。
利用上面的分析思路,这里我将分析出来的指令总结如下。
instr --> op | dst | op2 | op1 4B 32bit
op:
0x10 --> reg[dst] = op1
0x20 --> reg[dst] = (op1 == 0)
0x30 --> reg[dst] = memory[reg[op1]] --> mov mem, reg
0x40 --> memory[reg[op1]] = reg[dst] --> mov reg, mem
0x50 --> stack[sp++] = reg[dst] --> push reg
0x60 --> reg[dst] = stack[--sp] --> pop reg
0x70 --> reg[dst] = reg[op1] + reg[op2] --> add
0x80 --> reg[dst] = reg[op2] - reg[op1] --> sub
0x90 --> reg[dst]