我意外之中做了这道题,事后才知道它来自哪里 (膜
周末。panda熊猫让扔过来一道,他说是pwn中分数最高的,由于时间太短,没能做出来,太遗憾。这里把当时思路记录一下。
从题目名字来看ezarch,arch架构 ?3个功能,设置内存,下断点,运行。后知后觉原来是个虚拟机。当时主要是先入为主了,一段段的去看代码。主要还是太急了,根据我之前分析php内核的虚拟机的结构,这个地方其实是有套路的,分析虚拟机最主要的两个目标是:
- 虚拟机内部用来描述整个执行过程的指令集。
- 单个指令对应的解释过程。
先看设置内存的过程。仔细分析定义内存的结构应该是下面这样的
1 | struct vm{ |
这个结构存储在bss上,这里有一个结构虚拟机栈的空间是和这个结构紧靠着的。然后设置eip和esp,esp相关的寄存器。这里的寄存器都是相应的偏移值。这就是整个虚拟机内存初始化过程。
还有一个下断点的功能,就是把需要断住的eip储存在上面的bp空间里面。
最后来看虚拟机的执行器。就是这个地方浪费了我不少时间,一个switch,早就应该想到这个switch对应的就是处理过程的选择。
执行器会以步长10字节为一个opline来处理,opline的结构还是和通常的opline结构一样,opcode和操作数,操作数一般为两个,这里是没有返回值操作数的,一般返回值储存在前面两个操作数里面一个。
如果你是抱着上述思想来分析,整个执行器的过程就明了
1 | 10字节的opline结构 |
再看switch里面对应的case,这里不用看完,第一个case相当于nop,第二个和第三个是加减指令。第四个是读写寄存器。就这几个case就够,下面的异或,并,且逻辑运算等就不用看了。
这里分布是比较有特点,vm栈空间是和控制vm的结构是紧靠在一起的。这里checksec一下,发现got表是可以写的,但是开了pie。如果我们能把栈移到got表上呢?这里对esp和ebp做了一个检查。
1 | if ( v1 >= v2 || *(_DWORD *)(a1 + 1116) >= *(_DWORD *)(a1 + 16) || v2 <= *(_DWORD *)(a1 + 1120) ) |
这里是什么意思呢,esp要小于栈的大小。ebp要小于mem的大小。mem使我们自定义的是可控的。
这里栈的大小是固定的为0x1000。如果我们设置的内存的大小大于栈的空间大小0x1000, 那么我们的ebp是可以指向超出栈的空间的。并且这里是开了pie的。所以要找一个地址来中转。这里vm的结构上有栈顶的指针,为了也能让esp偏移超出限制,这里先让ebp偏移指向vm->stack_size.改变栈的大小。下面就是写got的过程。
在加减的处理过程中,除了写固定的寄存器以外,也可以写[reg],所以这里我们需要改变vm->mem的指向,前面也说了vm上有栈顶的指针,栈顶为bss上的地址。
这里再让ebp指向vm->stack. 将vm->mem覆盖为vm->stack.后面的流程就明显了。覆盖malloc@got为one_gadget。
把思路记录一下。也是第一次看虚拟机的题目,不过相对来说这个虚拟机还是比较简单:slight_smile: 。