Pwn学习随笔( 三 )

结合asm可以可以得到最终的pyaload
from pwn import *context(os='linux',arch='amd64')shellcode = asm(shellcraft.sh())或者from pwn import *shellcode = asm(shellcraft.amd64.linux.sh())

  • ROP链生成器
    elf = ELF('ropasaurusrex')rop = ROP(elf)rop.read(0, elf.bss(0x80))rop.dump()# ['0x0000:0x80482fc (read)',#'0x0004:0xdeadbeef',#'0x0008:0x0',#'0x000c:0x80496a8']str(rop)# '\xfc\x82\x04\x08\xef\xbe\xad\xde\x00\x00\x00\x00\xa8\x96\x04\x08'
  • ? 因为ROP对象实现了getattr的功能,可以直接通过func call的形式来添加函数,rop.read(0, elf.bss(0x80))实际相当于rop.call('read', (0,elf.bss(0x80))) 。通过多次添加函数调用,最后使用str将整个rop chain dump出来就可以了 。
    • call(resolvable, arguments=()) : 添加一个调用 , resolvable可以是一个符号,也可以是一个int型地址 , 注意后面的参数必须是元组否则会报错,即使只有一个参数也要写成元组的形式(在后面加上一个逗号)
    • chain() : 返回当前的字节序列,即payload
    • dump(): 直观地展示出当前的rop chain
    • raw() : 在rop chain中加上一个整数或字符串
    • search(move=0, regs=None, order=’size’) : 按特定条件搜索gadget
    • unresolve(value) : 给出一个地址 , 反解析出符号
    PLT和GOT
    • GOT(Global Offset Table)全局偏移表 。存储导入变量的地址
    • PLT(Procedure Linkage Table)程序链接表 。它有两个功能,要么在 .got.plt 节中拿到地址,并跳转 。要么当 .got.plt 没有所需地址的时,触发「链接器」去找到所需地址,与常见导入的函数有关 , 如 read 等函数 。
    • .got.plt,这个是 GOT 专门为 PLT 专门准备的节 。说白了,.got.plt 中的值是 GOT 的一部分 。它包含上述 PLT 表所需地址(已经找到的和需要去触发的),存储导入函数的地址
    • .plt.got,与动态链接有关系 。
    puts这样的函数都是定义在glibc动态库里的,只有当程序运行起来时才可以确定地址,而运行时重定位是无法修改.text段的地址的,只能将puts重定位到data段,那么got表怎么知道puts()函数的真实地址呢,链接器会额外生成一小段代码,如下所示
    .text...// 调用printf的call指令call printf_stub...printf_stub:mov rax, [printf函数的储存地址] // 获取printf重定位之后的地址jmp rax // 跳过去执行printf函数.data...printf函数的储存地址,这里储存printf函数重定位后的地址总体来说,动态链接每个函数需要两个东西:
    • 用来存放外部函数地址的数据段
    • 用来获取数据段记录的外部函数地址的代码
    对应有两个表 , 一个用来存放外部的函数地址的数据表称为全局偏移表(GOT, Global Offset Table),那个存放额外代码的表称为程序链接表(PLT,Procedure Link Table),plt 表不是查询表,而是一块代码 。这一块内容是与代码相关的
    Pwn学习随笔

    文章插图
    可执行文件里面保存的是 PLT 表的地址,对应 PLT 地址指向的是 GOT 的地址,GOT 表指向的就是 glibc 中的地址,那我们可以发现,在这里面想要通过 plt 表获取函数的地址,首先要保证 got 表已经获取了正确的地址,但是在一开始就进行所有函数的重定位是比较麻烦的 , 为此,linux 引入了延迟绑定机制
    延迟绑定只有动态库函数在被调用时 , 才会地址解析和重定位工作,为此可以使用类似这样的代码来实现
    //一开始没有重定位的时候将 printf@got 填成 lookup_printf 的地址void printf@plt(){address_good:jmp *printf@gotlookup_printf:调用重定位函数查找 printf 地址 , 并写到 printf@got goto address_good;//再返回去执行address_good}说明一下这段代码工作流程,一开始,printf@got 是 lookup_printf 函数的地址 , 这个函数用来寻找 printf() 的地址,然后写入 printf@got,lookup_printf 执行完成后会返回到 address_good , 这样再 jmp 的话就可以直接跳到printf 来执行了
    也就是说这样的机制的话如果不知道 printf 的地址,就去找一下,知道的话就直接去 jmp 执行 printf 了
    下面是一段plt表的示例
    Disassembly of section .plt:080482d0 <common@plt>: 80482d0: ff 35 04 a0 04 08pushl0x804a004 80482d6: ff 25 08 a0 04 08jmp*0x804a008跳转到_dl_runtime_resolve这个函数中查找运行时地址它是got表的第三项,所以可以看到该地址为08而puts对应的got表的地址为0c 80482dc: 00 00add%al,(%eax) ...080482e0 <puts@plt>: 80482e0: ff 25 0c a0 04 08jmp*0x804a00c从got表的第四项开始 80482e6: 68 00 00 00 00push$0x0这个 push进去的实际上就是在 got 表中的索引 80482eb: e9 e0 ff ff ffjmp80482d0 <_init+0x28>又跳到最上面的 公共 plt080482f0 <__libc_start_main@plt>: 80482f0: ff 25 10 a0 04 08jmp*0x804a010 80482f6: 68 08 00 00 00push$0x8每一个got表项对应的 push 的参数之间的间隔为8 80482fb: e9 d0 ff ff ffjmp80482d0 <_init+0x28>

    推荐阅读