针对 Linux 环境下 gdb 动态调试获取的局部变量地址与直接运行程序时不一致问题的解决方案

基础的缓冲区溢出实践通常需要确定运行状态下程序中的某些局部变量的地址,如需要确定输入缓冲区的起始地址从而获得注入缓冲区中的机器指令的起始地址等。在 Linux 环境下,可通过 gdb 对程序进行动态调试,从而获得程序运行状态下的信息( 关闭 ALSR 机制 ),基础的 gdb 操作可参见笔者的文章Linux下编辑、编译、调试命令总结——gcc和gdb描述。使用 gdb 可以方便的获取程序动态运行状态下的信息,但通过 gdb 动态调试获取的诸如缓冲区的起始地址等信息可能与程序实际运行时的信息并不相同,从而影响缓冲区溢出实践的效果。

问题描述

  通过 gdb 运行的程序的内存布局较之程序单独运行时的内存布局可能略有不同.通过以下实例程序进行简单的验证。

  #include<stdio.h>

  int main(int argc , char *argv[] , char *envp[])
  {
      char buff[8];      printf("address of buff : %p \n", buff );            return 0;
  }

  关闭 ALSR 机制后,通过 gcc -m32 -o hello -g -fno-stack-protector hello.c 编译生成可执行文件 hello。直接运行可执行程序,获得缓冲区的起始地址为 0xffffcfa8.

  

  使用 gdb 对 hello 文件动态调试,获得缓冲区的起始地址为 0xffffcf88.可以看到两者地址之间存在32个字节的差距。   

  

  正常程序运行时,会将环境变量字符串数组和命令行参数字符串数组存放在栈顶,而程序使用的局部变量等数据则位于这些字符串数组之后。环境变量字符串数组记录了诸如当前用户名、终端类型、搜索路径等环境信息。程序直接运行时,程序进程继承的是运行其的 shell 的环境变量,而程序通过 gdb 运行时,程序进程继承的是 gdb 的环境变量,这两者存在不同,从而会造成位于栈上的局部变量的地址发生改变。用户可在 gdb 中运行 show environment 命令获得环境变量参数。

    show environment    //在 gdb 中获得当前程序运行的环境变量

  

  较之程序直接运行,位于栈顶的环境变量主要有以下变化:

  (1) _ 环境变量的内容发生改变,在程序直接运行时,_ 变量存放的是程序的执行路径,而通过 gdb 运行程序时,_ 变量存放的是 gdb 的执行路径。

  使用 gdb 运行程序时,_ 环境变量的值为 /usr/bin/gdb.而在程序直接运行时,_ 环境变量的值应为程序的执行路径,如./hello.

  

  (2) 通过 gdb 运行的调试程序继承了 gdb 的环境变量,其中包含新加入的环境变量 LINES 和 COLUMNS

  

  (3) 位于栈上的参数列表也可能不同,当用户通过 ./hello 直接在shell 中运行程序时,位于参数数组的第一项 argv[0] 内容为"./hello" ,而用户通过 gdb 运行 hello 程序时,程序的参数列表的第一项 argv[0] 的值为该程序的绝对路径"/home/yh/hello",这也会造成程序运行时局部变量地址的差异。建议终端环境下使用绝对路径运行程序,避免该差异。

  

功能支持

  为了解决上述由于环境变量不同所产生的程序局部变量地址的差异,需要在使用 gdb 运行程序之前进行一定的操作,gdb 为用户调试提供了以下功能支持。

  (1) gdb 提供命令对传递给调试进程的环境变量进行操作和修改。

    show paths                //显示可执行文件的搜索路径(即为PATH变量的内容)
    show environment [ varname ]    //显示启动调试程序时 gdb 传递给它的环境变量的值,不指定具体的变量名时,则会显示所有的环境变量,可使用 env 作为 environment 的简写
    set environment varname [= value]    //设置传递给调试程序的环境变量varname的值为value,不指定value时,值默认为NULL
    unset environment varname            //设置不会被传递给调试程序的环境变量

  用户可以通过 unset env LINES 和 unset env COLUMNS 命令使得调试程序的栈上不会新增额外的环境变量 LINES 和 COLUMNS。

  (2) Linux 平台提供了标准 wrapper 程序 env 对最终传递给程序的环境变量进行设置。env 的详细用法可通过 man env 进行查看,其简单的参数如下。

    env [options] [-] [ name=value ] [command [args] ...]    //通过 command 指定由 env 启动的程序,不指定 command 时,会将当前 env 指定的环境输出,如直接运行 env ,输出的为当前的所有环境变量
options:
    -i             //忽略环境变量,即不传递环境变量给程序
    -u varname     //忽略 varname 指定的环境变量,即不将其传递给程序
    -              //作用与 -i 相同

  (3) gdb 可通过 wrapper 函数运行调试程序。当设置好 wrapper 程序后,gdb 会以 exec wrapper hello 的 shell 命令的形式启动调试程序 Hello,wrapper程序首先运行并最终启动调试进程,之后由 gdb 对调试进程进行控制。通过 wrapper 程序,即可控制传递给调试进程的环境变量。

    set exec-wrapper wrapper  //设置 wrapper 程序为 wrapper
    show exec-wrapper       //显示当前的 wrapper 程序
    unset exec-wrapper     //取消对 wrapper 程序的设置

  可以使用标准的 wrapper 程序 env 对传递给调试进程的环境变量进行控制。

解决方案

  借助上述功能,可以通过以下方案解决 gdb 运行调试程序时局部变量地址变化的问题。

  A.不传递环境变量数组给调试程序。

  (1)通过 env 运行 gdb ,并将传递给 gdb 的环境变量设置为空。此时 gdb 环境下所包含的环境变量仅为其新增加的 LINES 和 COLUMNS。

    env - gdb /home/yh/hello    //设置传递给 gdb 的环境变量为空,注意使用待调试程序的完整路径名

  此时,通过 show env 显示的 gdb 中环境变量的列表如下

      

  (2)通过 gdb 对环境变量的设置命令 unset env ,设置不将上述两个环境变量传递给调试程序,使得最终运行调试程序时,不会有环境变量被调试程序继承。

    unset env LINES
    unset env COLUMNS

  经过以上两步,通过 gdb 运行的调试程序 hello 的局部变量的地址与通过 env - /home/yh/hello 运行的 hello 程序的地址是一致的(两者的进程栈上均不包含环境变量数组,且 argv[0] 均为绝对路径地址)。当待调试的程序需要使用某些环境变量的值时,只需通过 env 程序指定所需的环境变量即可,而不引入不相同的环境变量如 _ .

  

  B.基于同样的原理,可以正常启动 gdb ,但对 wrapper 程序进行设置。

   gdb hello          //正常启动 gdb
   set exec-wrapper env -    //在 gdb 中设置 wrapper 程序为 env -
   r                        //开始调试,借助设置的 wrapper 程序的功能,hello程序不会继承 gdb 的任何环境变量

  此时通过 gdb 运行程序时调试程序不会继承 gdb 的环境变量,这样获得的程序局部变量的地址与通过 env - /home/yh/hello 的方式启动的 hello 程序的局部变量地址相同。

参考资料

  1.Debugging with GDB:Environment

  2.Debugging with GDB:  Starting

  3.Buffer overflow works in gdb but not without it- Stack Overflow

  4.How to predict address space layout differences between real and gdb-controlled executions? - StackExchange

原文地址:https://www.cnblogs.com/yhjoker/p/9161716.html

时间: 2024-11-08 18:58:45

针对 Linux 环境下 gdb 动态调试获取的局部变量地址与直接运行程序时不一致问题的解决方案的相关文章

linux 环境下 gdb 附加进程调试程序

1.找到程序的进程号 2.gdb 程序名 进程号 gdb lobby 48012 3.设置断点 以下摘自博文:http://www.cnblogs.com/TianFang/archive/2013/01/20/2868889.html 启动GDB后,首先就是要设置断点,程序中断后才能调试.在gdb中,断点通常有三种形式: 断点(BreakPoint): 在代码的指定位置中断,这个是我们用得最多的一种.设置断点的命令是break,它通常有如下方式: break <function>    在进

嵌入式arm linux环境中gdb+gdbserver调试

一.前言嵌入式Linux系统中,应用开发过程中,很多情况下,用户需要对一个应用程序进行反复调试,特别是复杂的程序.采用GDB方法调试,由于嵌入式系统资源有限性,一般不能直接在目标系统上进行调试,通常采用gdb+gdbserver的方式进行调试. Gdbserver在目标系统中运行,gdb则在宿主机上运行.目标系统必须包括gdbserver程序,宿主机也必须安装gdb程序.在此我们还不能直接采用linux发行版自带的gdb,需要交叉编译gdb和gdbserver. 二.编译gdb和gdbserve

AT PPP拨号失败,linux环境下在如何调试?

首先PPP在options文件中可以放开debug & kedebug调试选项 其次Linux内核日记可以通过cat /var/log/syslog.1查看 下面是linux的相关操作步骤 cd /etc/ppp ls vi options change #debug to debug # Increase debugging level (same as -d).  If this option is given, pppd# will log the contents of all cont

ElasticHD Linux环境下安装

上一篇讲了ElasticHD windows环境下安装,这一篇继续说明ElasticHD Linux环境下安装,有了安装windows版本下错版本的经验,这次我们直接定位到程序下载页面,选择对应的系统版本,我这次安装的是Cetos 7 64位系统: 打开Xshell,连接到Linux系统中: 第一步:我们来下载程序压缩包,先打开下载地址页(上一篇中有地址),然后选择需要下载的版本,复制下载地址,然后用命令下载: wget https://github.com/360EntSecGroup-Sky

Linux环境下段错误的产生原因及调试方法小结(转)

最近在Linux环境下做C语言项目,由于是在一个原有项目基础之上进行二次开发,而且 项目工程庞大复杂,出现了不少问题,其中遇到最多.花费时间最长的问题就是著名的“段错误”(Segmentation Fault).借此机会系统学习了一下,这里对Linux环境下的段错误做个小结,方便以后同类问题的排查与解决. 1. 段错误是什么 一句话来说,段错误是指访问的内存超出了系统给这个程序所设定的内存空间,例如访问了不存在的内存地址.访问了系统保护的内存地址.访问了只读的内存地址等等情况.这里贴一个对于“段

Linux环境下段错误的产生原因及调试方法小结

最近在Linux环境下做C语言项目,由于是在一个原有项目基础之上进行二次开发,而且项目工程庞大复杂,出现了不少问题,其中遇到最多.花费时间最长的问题就是著名的“段错误”(Segmentation Fault).借此机会系统学习了一下,这里对Linux环境下的段错误做个小结,方便以后同类问题的排查与解决. 1. 段错误是什么 一句话来说,段错误是指访问的内存超出了系统给这个程序所设定的内存空间,例如访问了不存在的内存地址.访问了系统保护的内存地址.访问了只读的内存地址等等情况.这里贴一个对于“段错

【转】【调试技巧】Linux环境下段错误的产生原因及调试方法小结

本文转自:http://www.cnblogs.com/panfeng412/archive/2011/11/06/segmentation-fault-in-linux.html 最近在Linux环境下做C语言项目,由于是在一个原有项目基础之上进行二次开发,而且项目工程庞大复杂,出现了不少问题,其中遇到最多.花费时间最长的问题就是著名的“段错误”(Segmentation Fault).借此机会系统学习了一下,这里对Linux环境下的段错误做个小结,方便以后同类问题的排查与解决. 1. 段错误

Linux环境下段错误的产生原因及调试方法小结(转载)

转载自http://www.cnblogs.com/panfeng412/archive/2011/11/06/2237857.html 最近在Linux环境下做C语言项目,由于是在一个原有项目基础之上进行二次开发,而且项目工程庞大复杂,出现了不少问题,其中遇到最多.花费时间 最长的问题就是著名的“段错误”(Segmentation Fault).借此机会系统学习了一下,这里对Linux环境下的段错误做个小结,方便以后同类问题的排查与解决. 1. 段错误是什么 一句话来说,段错误是指访问的内存超

GDB远程调试(一)之DM8168下gdb远程调试环境的搭建

1.前言 gdb是较为流行和通用的linux环境调试工具,掌握它对于嵌入式开发工作来说非常必要,能够提高工作效率,快速发现和解决问题.最近有兴趣研究了下gdb使用,特总结如下以备以后查阅. 2.下载最新的gdb 下载地址:http://ftp.gnu.org/gnu/gdb/,选择目前最新的版本下载.我选择了gdb-7.7.tar.gz版本 3.编译gdb和gdbserver (1)解压到你的工作目录: [email protected]:~$ tar -xvf gdb-7.7.tar.gz (