Pwn學習隨筆( 二 )

esp與剛開始不一樣,還沒有平衡堆棧,由上圖下面的add esp, 8用于平衡堆棧 , 這種方式就叫外平棧 。

Pwn學習隨筆

文章插圖

Pwn學習隨筆

文章插圖
要覆蓋返回地址,即ebp + 4,覆蓋了ebp之后還需要再覆蓋ebp + 4的位置,64位下為ebp + 8,如上圖所示401171即為返回地址
leave指令相當于這兩條指令:movl %ebp, %esp即令esp=ebppopl %ebp 即 ebp = M[esp],esp = esp + 4
裸函數示例
int __declspec(naked) plus(){__asm{//在函數調用之前會先push 1 push 2(傳參數)call后會執行push 返回地址//保留調用前的棧底push ebp//提升堆棧mov ebp,espsub esp,0x40//保留現場push ebxpush esipush edi//填充緩沖區主要用于存儲函數的局部變量mov eax,0xccccccccmov ecx,0x10// 之所以是10 是因為之前提升堆棧0x40 / 4 = 10棧一個格四個字節lea edi,dword ptr ds:[ebp-0x40]rep stosd //每次填充四個字節,重復16次//函數的核心功能ebp + 0x4為返回地址mov eax,dword ptr ds:[ebp+0x8]//把第一個參數給eaxebp+0x4為函數返回地址add eax,dword ptr ds:[ebp+0xc]//第二個參數 + eax -> eax//恢復現場pop edi// 取出棧頂給edi,然后esp+4pop esi// 取出棧頂給esi,然后esp+4pop ebx// 取出棧頂給ebx,然后esp+4//降低堆棧mov esp,ebppop ebp//恢復棧底,剛開始ebp保留過ret//相當于pop eip 把函數返回地址401171給eip然后 rsp + 4}}//裸函數,系統不會生成任何指令,調用時會出錯,會導致指令跳轉后回不來,往往需要自己寫入匯編指令Pwntools用法參考手冊
  • 連接:本地process()、遠程remote( , );對于remote函數可以接url并且指定端口
  • 數據處理:主要是對整數進行打包:p32、p64是打包為二進制 , u32、u64是解包為二進制
  • 設置目標系統架構及操作系統
    >>> context.arch= 'i386'>>> context.os= 'linux'>>> context.endian= 'little'>>> context.word_size = 32當然 , 你也可以一次性設置好這些變量:
    >>> asm('nop')'\x90'>>> context(arch='arm', os='linux', endian='big', word_size=32)>>> asm('nop')'\xe3 \xf0\x00'
  • IO模塊:這個比較容易跟zio搞混 , 記住zio是read、write , pwn是recv、send
send(data): 發送數據sendline(data) : 發送一行數據,相當于在末尾加\nrecv(numb=4096, timeout=default) : 給出接收字節數,timeout指定超時recvuntil(delims, drop=False) : 接收到delims的pattern(以下可以看作until的特例)recvline(keepends=True) : 接收到\n,keepends指定保留\nrecvall() : 接收到EOFrecvrepeat(timeout=default) : 接收到EOF或timeoutinteractive() : 與shell交互
  • ELF模塊:獲取基地址、獲取函數地址(基于符號)、獲取函數got地址、獲取函數plt地址
e = ELF('/bin/cat')>>> print hex(e.address)# 文件裝載的基地址0x400000>>> print hex(e.symbols['write']) # 函數地址0x401680>>> print hex(e.got['write']) # GOT表的地址0x60b070>>> print hex(e.plt['write']) # PLT的地址0x401680>>> print hex(e.search('/bin/sh').next())# 字符串/bin/sh的地址
  • 在編寫exp時,最常見的工作就是在整數之間轉換,而且轉換后,它們的表現形式就是一個字節序列,pwntools提供了打包函數 。
p32/p64: 打包一個整數 , 分別打包為32位或64位u32/u64: 解包一個字符串,得到整數# 比如將0xdeadbeef進行32位的打包,將會得到'\xef\xbe\xad\xde'(小端序)payload = p32(0xdeadbeef)#pack 32 bits numberpayload = p64(0xdeadbeef)#pack 64 bits number