pwn入门:详解gdb调试程序的常见命令

本文通过实例讲解了如何使用GDB调试器对存在溢出漏洞的程序进行调试,涉及pwn环境部署、gdb命令应用、溢出覆盖、大小端序影响及Python脚本编写,旨在帮助理解内存布局和程序执行过程。

目录

写在开头

一、pwn题目环境的部署

二、解题思路(不是重点) 

三、gdb的调试过程(重点) 

完整运行过程(run)  

调试程序(重点)

运行到程序的开始位置

设置断点

查看内存 

修改地址的值

单步运行/查看寄存器

四、python脚本的编写

总结与思考

写在开头

   近期又开始回头开始学pwn了,看了b站国资社畜大佬的视频,在大佬讲解的基础上做个总结,加入了我的一些思考。学pwn主要是要了解内存布局和程序的执行过程,感觉学习路线确实比较陡峭,今后我也会不定期更新这个系列。

      本文主要以对一个程序的调试过程为例,介绍gdb调试器的常见命令,并直观的展示所谓“溢出”覆盖的效果以及大小端序的问题。涉及的知识点包括:pwn题目环境的部署、gdb调试的常见命令、大小端序的影响、简单解pwn题目脚本的编写。特别说明,由于篇幅有限,本文不会过多介绍汇编指令以及程序运行时函数调用栈的变化过程。这些内容可能会在后面的博客中再做介绍,本文的重点是使用gdb对程序进行调试。本文需要的工具主要有gdb和pwntools,读者如果想跟着复现,只要有gdb和pwntools即可,安装过程详见:

pwn入门(1):kali配置相关环境(pwntools+gdb+peda)_gdb插件 peda-优快云博客

  备注:文末有我总结的gdb常见指令,有需要的读者可以直接看文末。 

一、pwn题目环境的部署

   这一部分属于调试过程的前置,想在本地部署一个pwn环境,用socat开启一个本地端口运行pwn题目(下文会细说)。当然读者如果仅仅了解gdb的话也可以不用考虑这么多,直接用p = process(./文件)即可。建议使用ubuntu或kali这样的系统部署环境、调试程序。

   首先我们要构建一个存在“溢出”漏洞的二进制文件。这里我们就用国资社畜大佬给出的question.c文件即可,其内容如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
char sh[]="/bin/sh";

int init_func(){
    setvbuf(stdin,0,2,0);
    setvbuf(stdout,0,2,0);
    setvbuf(stderr,0,2,0);
    return 0;
}

int func(char *cmd){
        system(sh);
        return 0;
}

int main(){
    init_func();
    volatile int (*fp)();
    fp=0;
    int a;
    puts("input:");
    gets(&a);  //gets没有对输入字符的长度做限制,存在溢出
    if(fp){
        fp();
    }
    return 0;
}

   然后用gcc把这个文件编译一下,生成存在漏洞的二进制文件,注意编译的过程要关闭pie保护,命令如下:

gcc question.c -no-pie -o question1

此处简要说明一下PIE保护(不是本文重点)。

1.PIE保护是编译时默认开启的。如果想关闭pie保护,需要手动添加参数-no-pie

2.PIE (position-independent executable) 是一种生成地址无关可执行程序的技术。是一种保护机制,如果程序开启了PIE保护的话,那么在每次加载程序时都会改变加载的基地址。当然这种机制不利于我们本地调试程序,所以在这里就关闭了。

   接下来为了增强仪式感,我们可以在当前目录新建一个文件flag,拿到这个flag相当于解题成功。

 然后用socat开启8888端口部署这个题目question1,当然你也可以使用任何未被占用的其他端口:

socat tcp-l:8888,fork exec:./question1,reuseaddr

  然后另起一个终端(ctrl+shift+t),试一试能不能用nc连接这个题目,如下图连接成功:

 随便输入一段字符串看看效果,好像没啥问题:

二、解题思路(不是重点) 

   如果从解题的角度思考,我们只能拿到一个二进制文件,是看不到源代码的。当然可以放到ida中分析,也可以用反汇编工具查看伪源代码,然后再慢慢分析。不过由于本文的重点是用gdb调试程序,因此这里就直接从上面的源代码简要分析触发漏洞的位置:

1.溢出点:代码中存在危险函数gets()。gets函数用于获取用户的输入,且不会对输入的字符数量做限制,因此有可能会溢出覆盖其他内存。

2.后门函数:存在后门函数func,这个函数可以获取shell,我们要想办法让这个函数执行。

3.函数指针fp,原始的

<think>嗯,用户这次明确提出了ROP入门题解法的需求。从引用内容看,用户应该是在学习PWN技术中的栈溢出和ROP攻击基础,而且提到的几个引用都涉及关键知识点:栈迁移绕过NX保护、异或指令绕过badchar、寄存器控制gadget查找、地址泄漏处理等。 用户可能刚接触二进制安全,需要从最基础的ROP链构造逻辑开始梳理。虽然引用里有不少术语,但用户特意强调"入门题解法",说明需要基础性引导。我注意到引用[3]提到pop rdi寄存器的查找方法,引用[4]强调地址转换,这些都是新手容易出错的点。 上次回答已经搭建了ROP攻击的框架,这次要更聚焦"入门实践"环节。把攻击流程拆解成:1)确认漏洞点→2)泄漏地址→3)寻找gadget→4)构造payload→5)绕过防护措施。特别要强调工具链使用,比如ROPgadget和pwntools的操作细节,因为引用[3][4]显示用户正在实践这部分。 用户提到的badchar绕过(引用[2])和栈迁移(引用[1])属于进阶技巧,但既然是入门需求,应该放在最后简要说明作为延伸。核心还是要让用户理解最基本的ret2libc攻击模型——毕竟所有ROP都是基于这个思想衍生的。 啊,想到个重点:必须提醒内存地址对齐问题。引用[4]的int(buf,16)转换容易出错,很多新手会忽略小端序处理和地址偏移计算,这会导致payload发送失败。应该在步骤里单独强调,最好配上示意图解释栈帧布局。 </think>以下是ROP入门题的解法详解,结合栈溢出漏洞利用的核心步骤: --- ### **ROP攻击基本原理** ROP(Return-Oriented Programming)是一种绕过内存不可执行(NX/DEP)防护的技术。核心思想是复用程序中已有的代码片段(gadget),通过控制栈指针(`rsp`)跳转执行多个gadget链,最终实现攻击目标[^1]。 --- ### **ROP入门题解法步骤** #### **1. 确定漏洞点** - 通过缓冲区溢出覆盖返回地址(ret),劫持程序控制流。 - 输入模式示例:`payload = b'A'*offset + p64(gadget_addr)` (`offset`为溢出点到ret的偏移,`p64`用于地址打包)[^4]。 #### **2. 泄漏关键地址** - **场景**:需获取函数真实地址(如`libc`基址)。 - **方法**: - 调用`puts@plt`泄漏`puts@got`地址: ```python pop_rdi = 0x400703 # 控制rdi寄存器的gadget payload = b'A'*offset + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) ``` - 接收输出并计算`libc`基址: `libc_base = leaked_puts - libc.symbols['puts']`[^3][^4]。 #### **3. 构造ROP链** - **查找gadget**:使用工具定位关键指令: ```bash ROPgadget --binary vuln_prog --only 'pop|ret' | grep 'rdi' # 输出示例: 0x400703: pop rdi; ret; ``` - **常用gadget类型**: - 控制参数寄存器:`pop rdi; ret`, `pop rsi; ret` - 内存操作:`mov [rax], rdx; ret` - 系统调用:`syscall; ret`[^2][^3]. #### **4. 绕过防护机制** - **Bad Characters处理**(如`b'i/c'`): - 使用异或指令避免敏感字符: ```asm xor_byte_gadget: # 示例gadget xor byte [rax], bl ret ``` - 分步异或写入目标值(如`shellcode`)[^2]。 - **栈空间不足时**:通过`栈迁移`转移到可控区域(如BSS段): ```python leave_ret = 0x400801 # leave; ret; gadget payload = b'A'*offset + p64(pop_rbp) + p64(new_stack) + p64(leave_ret) ``` #### **5. 最终执行** - 调用`system("/bin/sh")`或`execve`: ```python binsh_addr = 0x601080 # 写入"/bin/sh"的地址 payload += p64(pop_rdi) + p64(binsh_addr) + p64(system_addr) ``` --- ### **关键工具与技巧** 1. **动态调试**:`gdb-pwndbg`查看栈布局,`cyclic`计算偏移。 2. **地址转换**:接收泄漏地址时需转换:`leak = u64(p.recv(6).ljust(8, b'\x00'))`[^4]。 3. **Libc查询**:利用`LibcSearcher`匹配远程libc版本: ```python from libcsearcher import LibcSearcher libc = LibcSearcher("puts", leak_addr) ``` ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Boss_frank

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值