pwndbg调试工具使用教程
熟悉常用的调试命令
最后更新于
熟悉常用的调试命令
最后更新于
内存可以分为以下几段:
文本段:包含实际要执行的代码(机器指令)和常量。它通常是共享的,多个实例之间共享文本段。文本段是不可修改的。
初始化数据段:包含程序已经初始化的全局变量,.data。
未初始化数据段:包含程序未初始化的全局变量,.bbs。该段中的变量在执行之前初始化为0或NULL。
栈:由系统管理,由高地址向低地址扩展。
堆:动态内存,由用户管理。通过malloc/alloc/realloc、new/new[]申请空间,通过free、delete/delete[]释放所申请的空间。由低地址向高地址扩展。
进程地址空间从低地址开始依次是代码段(Text)、数据段(Data)、BSS段、堆、内存映射段(mmap)、栈。
[ 注意:BSS段 和 data段的区别是 ,如果一个全局变量没有被初始化(或被初始化为0),那么他就存放在bss段;如果一个全局变量被初始化为非0,那么他就被存放在data段。]
在进程被载入内存中时,基本上被分裂成许多小的节(section)。我们比较关注的是6个主要的节:
(1) .text 节
(2).data 节
(3).bss 节
(4) 堆节
(5) 栈节
(6)环境/参数节 环境/参数节(environment/arguments section)用来存储系统环境变量的一份复制文件, 进程在运行时可能需要。
例如,运行中的进程,可以通过环境变量来访问路径、shell 名称、主机名等信息。 该节是可写的,因此在格式串(format string)和缓冲区溢出(buffer overflow)攻击中都可以使用该节。 另外,命令行参数也保持在该区域中。
程序的调试过程主要有:单步执行,跳入函数,跳出函数,设置断点,设置观察点,查看变量。
GDB主要可以做4大类事(加上一些其他的辅助工作),以帮助用户在程序运行过程中发现bug。
启动您的程序,并列出可能会影响它运行的一些信息
使您的程序在特定条件下停止下来
当程序停下来的时候,检查发生了什么
对程序做出相应的调整,这样您就能尝试纠正一个错误并继续发现其它错误
1.输入gdb或者gdb-multiarch program进行本地调试
2.如果是本地程序的话,可以输入entry或者start进入第一条指令
entry
如果是本地调试有main函数,则可以输入main直接进入main函数
main
3.如果调试的程序有源代码,则输入n执行的时候是按照源代码一行一行执行的;
如果没有源代码,则输入ni来按照汇编语言执行一条条指令
如果指令是调用了函数,则可以通过si进入调用内部
4.输入p 函数名可以打印出函数地址,然后使用nearpc 地址即可显示此函数的反汇编代码
p scanf
nearpc 0x7ffff7a777f0
p/x:以16进制显示变量值
p/d:10进制显示
p/o:8进制显示
5.下断点,如果有符号,可以直接使用b 函数名下断点,如果没有,则需要使用b *address的方式下断点
b read/b recv
b *0x40062b
通过
bl列出所有断点
bc清除断点
bd临时禁用断点
be启用断点
6.下完断点之后需要执行到断点,从头开始运行输入r命令,继续运行输入c
7.通过stack命令可以查看栈的数据
stack 0x20 [0x20为查看的长度]
8.输入context可以重新刷新一下之前的调试界面
9.可以通过hexdump查看栈的地址的内容
hexdump 0x7fffffffe3b8
x/s 0x7fffffffe3b8
10.通过nextcall可以直接进行下一次调用
11.vmmap可以查看内存布局
12.可以使用p $eax查看寄存器的值,也可以使用i r eax查看
13.如果使用的调试器为peda:
pattern create 200
pattern offset 0xaddr
14.如果遇到栈溢出需要查看偏移量:
cyclic 200生成200字节的长度数据
cyclic -l 覆盖的前四个字母:cyclic -l jaab,输出即为偏移量
y修改变量为类型加值
n单纯修改变量
G跳转到指定地址
X查看交叉引用
shift+F12查找字符串
数组和
空格切换图形和文本视图
shift+e查看data段初始化的全局变量及静态变量
F2下断点
F4运行到选中指令
F8步过
F7单步执行
数据窗口跟随
智能搜索可以查找的字符串更全
od的基址与ida可能不一样
在看Shark恒 破解教程 时,有很多吾友对教程中按键的含义不懂,我发一个常用的快捷键列表,希望对新手有点帮助。 打开一个新的可执行程序 (F3) 重新运行当前调试的程序 (Ctrl+F2) 当前调试的程序 (Alt+F2) 运行选定的程序进行调试 (F9) 暂时停止被调试程序的执行 (F12) 单步进入被调试程序的 Call 中 (F7) 步过被调试程序的 Call (F8) 跟入被调试程序的 Call 中 (Ctrl+F11) 跟踪时跳过被调试程序的 Call (Ctrl+F12) 执行直到返回 (Ctrl+F9) 显示记录窗口 (Alt+L) 显示模块窗口 (Alt+E) 显示内存窗口 (Alt+M) 显示 CPU 窗口 (Alt+C) 显示补丁窗口 (Ctrl+P) 显示呼叫堆栈 (Alt+K) 显示断点窗口 (Alt+B) 打开调试选项窗口 (Alt+O)
代码测试:
栈从内存高地址向低地址生长,调用执行函数的时候:首先将被调用函数参数按照从右向左的顺序依次入栈,之后的流程为:
1.将call指令的下一条地址入栈,即被调用函数的返回地址入栈;
2.main函数的栈底地址入栈,即ebp入栈;mov ebp,esp;使得ebp指向新的栈顶;
3.之后被调用函数的局部变量入栈,执行到leave ret指令的时候:mov esp,ebp;pop ebp;,相当于恢复ebp原来的值,esp指向原来的位置,ret直接将pop esp;mov eip,esp;
plt与got
PLT:CODE
GOT:ADDRESS
PROGRAM CALL --> PLT --> GOT -->LIBC
64位传参:rdi,rsi,rdx... rdi, rsi, rdx, rcx, r8, r9,栈
当参数少于7个时, 参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9
当参数为7个以上时, 前 6 个与前面一样, 但后面的依次从 “右向左” 放入栈中,即和32位汇编一样。
POP rdi; ret// 通过pop为rdi赋值,再通过ret指令跳转到我们希望的地方
ret:sp增加一个内存单元栈顶数据出栈赋值给ip寄存器