巩固一下PIE的保护是什么?
PIE是让程序每次加载的基地址随机,但内部相对偏移固定
libc:动态加载器,Linux 程序里的 puts、printf、system 这些函数,不是写在程序本身里的,而是存在系统的 ** 动态库(libc.so)** 中。
程序运行时:
程序不知道 puts 在哪
只能先去动态加载器问路
问到地址后,存起来,下次直接用
这个问路的过程 = PLT Procedure Linkage Table(过程链接表)
这个存地址的地方 = GOT Global Offset Table(全局偏移表)
程序调用 puts → 跳到 puts@plt(问路牌)
↓
plt 查看 puts@got(地址本)
↓
如果 got 里有地址 → 直接跳过去执行
如果 got 为空 → 去系统找 puts,把地址写入 got
如果关闭PIE:程序加载基地址固定 0x400000, 可以直接在IDA里面找到puts@got本身的地址,而puts@got 指向的地址才是内存中put函数的真实运行地址,所以要把puts@got 指向的地址打印出来。
如果开启PIE:
1.泄露【程序基地址】:程序有 read 输入,有 puts 输出,有栈溢出,返回地址就是main函数的真实运行地址
例子:泄露得到 main 真实地址 = 0x561234560b28
IDA 里 main 偏移 = 0xb28
基地址 = 0x561234560b28 - 0xb28 = 0x561234560000
elf = ELF('./题目')
pie_base = leak_addr - elf.sym['main']
elf.address = pie_base #修正所有用elf调用的地址
2.计算出 puts@got 真实地址: puts@got 真实地址 = 程序基地址 + IDA里的 puts@got 偏移
3.用 puts@plt 去打印 puts@got:pop_rdi -> puts_got -> puts_pltexp中用两个recvline()的原因:
在你发送 payload 后,程序会回显:
\n
Ciphertext
put_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))详细解释
\x7f 是 64 位 libc 地址开头固定字节,64 位系统的 libc 地址几乎都是 0x7fxxxxxxxxxxxx 开头
p.recvuntil(b'\x7f')就是表示读取到地址开始了
例子:接收的数据:b'\nCiphertext\n\x00\x10\xd0\x86\x1c\x7f'
[-6:] 取最后 6 个字节:\x00\x10\xd0\x86\x1c\x7f
.ljust(8, b'\x00'):b'\x00\x10\xd0\x86\x1c\x7f\x00\x00'
64 位地址在网络传输 / 打印时,只会输出 6 个有效字节,高 2 个字节是 \x00,不会打印出来
...[-6:]表示截取最后 6 字节,就是真实地址的低 6 字节