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>

    推薦閱讀