概述
vmpwm即模拟虚拟机实现的程序,主要考察对程序的逆向能力
特征是模拟的栈结构,伪机器码,还有pc寄存器和sp寄存器
分析的重点就聚焦于找出栈和上述寄存器是如何进行实现的
因为又在这里栽跟头了所以写一篇博客来记录一下orz
例如:
1 | #include<stdio.h> |

可以看到输出的数据是0xe也就是14个字节
我们都知道一个64位程序的地址最多也就8个字节,为什么这个地址是14个字节呢?
原因是printf在输出的时候是将这个6个字节的地址转换成的字符串再加上0x进行输出的
知道原因过后我们的目标就很明确了:将这个14个字节的字符串转换为6个字节的数据
首先是要对获取到的数据进行处理:
我们需要用到python中的decode()方法将字节数据转换为字符串再使用lstrip('0x')去掉字符串左边的‘0x’
再使用bytes.fromhex()方法将十六进制字符串转换为字节数据
但是还没完,我们知道在大部分机器上数据是以小端序的顺序储存的,也就是我们要把我们得到的字节再进行反序操作
即使用[::-1]
完整操作如下:
1 | raw_bytes = r(14)#接收数据 |
然后就可以使用u64(reversed_bytes.ljust(8,b’\x00’))对其处理在写入payload中
有师傅说可以直接用
1 | int(p.recvline()[:-1],16) |
直接一步进行处理,我自己还是见识太少了,另外师傅tql orz
构造ROP链来使puts函数泄露libc地址应该是最常见的情况了,puts函数也是输出的字符串,为什么就可以直接通过u64(p.recv(6).ljust(8,b’\x00’))来获得libc地址呢?
在构造payload输出的时候虽然输出的是字符串,但这个字符串本质是直接根据字节数据通过诸如ascii转换而来的,也就是,输出的数据仍是原来的字节,而不是像printf那样输出的是将每四位数据对应的字符将这个字符作为一个单独的字节输出

C 库函数 int rand(void) 返回一个范围在 0 到 RAND_MAX 之间的伪随机数。
RAND_MAX 是一个常量,它的默认值在不同的实现中会有所不同,但是值至少是 32767。
C 库函数 void srand(unsigned int seed) 播种由函数 rand 使用的随机数发生器。
void srand(unsigned int seed)
参数seed是一个整形值,用于伪随机数生成算法播种。
有时随机数的种子seed会在栈上,可以通过栈溢出等方法对齐进行覆盖为已知值,再通过这个已知值获取对应的随机数序列
ctypes是Python内建的用于调用动态链接库函数的功能模块,通过调用该模块以及利用与题目相同的libc文件,来达到一个与题目中随机数生成方式相同的效果,实现撞库,这种方式下如果提前覆盖了指定种子,是更容易达成,如果直接设置时间种子撞库,可能存在时间对不上的情况,需要多测试几次。
使用方法:
1 | from ctype import * |
1 | #使用相同时间为种子 |