pwndbg调试工具使用教程

熟悉常用的调试命令

C++ 进程内存空间分布

内存可以分为以下几段:

  • 文本段:包含实际要执行的代码(机器指令)和常量。它通常是共享的,多个实例之间共享文本段。文本段是不可修改的。

  • 初始化数据段:包含程序已经初始化的全局变量,.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)攻击中都可以使用该节。 另外,命令行参数也保持在该区域中。

pwndbg调试命令

程序的调试过程主要有:单步执行,跳入函数,跳出函数,设置断点,设置观察点,查看变量。

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,输出即为偏移量

IDA和OD快捷键

IDA

  • y修改变量为类型加值

  • n单纯修改变量

  • G跳转到指定地址

  • X查看交叉引用

  • shift+F12查找字符串

  • 数组和

  • 空格切换图形和文本视图

  • shift+e查看data段初始化的全局变量及静态变量

OD

  • 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)

二进制一些基础知识

代码测试:

#include <stdio.h>


int add(int x, int y)
{
        int sum;
        sum = x+y;
        return sum;
}

int main()
{
        int p = add(3, 4);
        printf("%d\n",p);
        return 0;
}
gcc -g -m32 learn_stack.c -o learn_stack_32
  • 栈从内存高地址向低地址生长,调用执行函数的时候:首先将被调用函数参数按照从右向左的顺序依次入栈,之后的流程为:

    • 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// 通过poprdi赋值,再通过ret指令跳转到我们希望的地方

  • ret:sp增加一个内存单元栈顶数据出栈赋值给ip寄存器

最后更新于