将由字符串强制转换成的12-16字节数据转换为8字节数据

因为又在这里栽跟头了所以写一篇博客来记录一下orz

题目通过printf泄露地址的情况

例如:

1
2
3
4
5
6
#include<stdio.h>
int main(){
int a;
printf("%p",&a);
return 0;
}

0

可以看到输出的数据是0xe也就是14个字节

我们都知道一个64位程序的地址最多也就8个字节,为什么这个地址是14个字节呢?

原因是printf在输出的时候是将这个6个字节的地址转换成的字符串再加上0x进行输出的

知道原因过后我们的目标就很明确了:将这个14个字节的字符串转换为6个字节的数据

首先是要对获取到的数据进行处理:

我们需要用到python中的decode()方法将字节数据转换为字符串再使用lstrip('0x')去掉字符串左边的‘0x’

再使用bytes.fromhex()方法将十六进制字符串转换为字节数据

但是还没完,我们知道在大部分机器上数据是以小端序的顺序储存的,也就是我们要把我们得到的字节再进行反序操作

即使用[::-1]

完整操作如下:

1
2
3
4
raw_bytes = r(14)#接收数据
hex_str = raw_bytes.decode().lstrip('0x')#.rstrip('\x00') 如果结尾有多的'\x00'还需要.rstrip('\x00')来进行去除
hex_data = bytes.fromhex(hex_str) #转换为字节
reversed_bytes = hex_data[::-1] #反序

然后就可以使用u64(reversed_bytes.ljust(8,b’\x00’))对其处理在写入payload中

有师傅说可以直接用

1
int(p.recvline()[:-1],16)

直接一步进行处理,我自己还是见识太少了,另外师傅tql orz

puts的情况

构造ROP链来使puts函数泄露libc地址应该是最常见的情况了,puts函数也是输出的字符串,为什么就可以直接通过u64(p.recv(6).ljust(8,b’\x00’))来获得libc地址呢?

在构造payload输出的时候虽然输出的是字符串,但这个字符串本质是直接根据字节数据通过诸如ascii转换而来的,也就是,输出的数据仍是原来的字节,而不是像printf那样输出的是将每四位数据对应的字符将这个字符作为一个单独的字节输出