• Linux基础(03)gdb调试


    1. 安装GDB增强工具 (gef)

      * GDB的版本大于7.7

      * wget -q -O- https://github.com/hugsy/gef/raw/master/scripts/gef.sh | sh

      * 确保网络连通 并且成功更新ubuntu (更新source.list 使用apt-get update)

    2. GDB安装插件(在root权限安装,用户权限使用不了需要在用户权限下安装)

      git clone https://github.com/gatieme/GdbPlugins.git ~/GdbPlugins  (安装gdb的Python脚本插件)

      切换gdb模式 :

      echo "source ~/GdbPlugins/peda/peda.py" > ~/.gdbinit    (默认打开gdb插件是pada)  倾向于破解和逆向

      echo "source ~/GdbPlugins/gef/gef.py" > ~/.gdbinit      倾向于debug 逆向

      echo "source ~/GdbPlugins/gdbinit/gdbinit" > ~/.gdbinit    个人定制

    3.Linux程序发布流程

      * 确定程序是否存在符号表

        readelf -s test-1

      * 生成符号表

        objcopy --only-keep-debug test-1 test-1.symbol

      * 生成发布程序

        objcopy --strip-debug test-1 test-release

      * 使用符号表进行程序debug

        gdb -q --symbol=test-1.symbol --exec=test-release

    4.gdb语法的使用

      set listsize num 设置 l 的显示行数

      list/l (code/num) 查看某个函数代码或者查看某行代码. 查看其它文件的代码 l xxx.c:(code/num)

      break/b num 打断点 条件断点 b num if var== 也可以断到某个函数上b func

      continue/c 跳到下一个断点

      disable num 使断点失效 enable num 使断点重新有效

      run/r 运行程序

    调试已运行的程序

      info breakpoints 查看断点 delete/d num 删除断点列表的某个断点

      print/p code 调试时打印某个变量的值 也可以选择打印的方式 如: p/d code 打印十进制的结果 p/s 打印字符串的结果

      p var=100 修改内存

      step/s 执行下一句代码有函数进入函数

      next/n 执行下一句代码有函数也跳过

      until/u 跳出当前循环

      finish 退出当前函数

      info locals 查看当前函数中的全部局部变量

      bt 查看函数调用栈 如: 进入某个函数时bt一下可以知道哪个函数调用了当前进入的函数 或者 可以查看处在哪个函数中

      info frame 打印当前栈里所以的信息

      x &code 查看某个变量的内存地址

      #观察点

        watch var/add 观察内存是否被改变如果改变则自动断点

        gdb就近原则 如果有变量重名 想查看任意函数内 的变量 watch func::var

        info watchpoints 查看观察点列表

        rwatch var/add 观察内存是否被读取如果被读取则断点

      #设置捕捉点

        catch event/throw

        throw 抛出一个C++的异常 catch throw

        catch 捕捉一个C++的异常 catch catch

        exec 调用系统调用exev时停止 catch exec

        exec 在一个进程中启动另外一个程序

        fork 调用系统调用fork时停止 catch fork

        load/load libname 载入动态链接库时 catch load / catch load libname

        unload

      #搜索源代码

        search var/func 内存搜索 只有调用了函数或存在于内存才能搜索到

        reverse-search var/func 全文搜索 不占用内存也能搜索只要在代码段

      #查看内存

        x/nfu

          x 是 examine 的缩写

          n表示要显示的内存单元的个数

          f表示显示方式, 可取如下值
            x 按十六进制格式显示变量。
            d 按十进制格式显示变量。
            u 按十进制格式显示无符号整型。
            o 按八进制格式显示变量。
            t 按二进制格式显示变量。
            a 按十六进制格式显示变量。
            i 指令地址格式
            c 按字符格式显示变量。
            f 按浮点数格式显示变量。

          u表示一个地址单元的长度
            b表示单字节,
            h表示双字节,
            w表示四字节,
            g表示八字节

       十六进制两位数一个字节

          如:x/3uh 0x80494a4 往后的三个无符号的十六进制只显示两个字节的地址

          x/3uh 0x80494a4 1 0 16

          x/3xh 0x80494a4 0x0001 0x0000 0x0010

       查看数组地址 p*arr@len

      #插入命令

        在触发某些断点是执行插入的命令并不是真的在源代码上插入

        conmades bnum

        ...

        ...

        end

    一般都是没有gef等脚本时使用的

     ##############################################################################

    内存分段的概念:  https://blog.csdn.net/zhy557/article/details/80832268

    code: 代码域 , 程序的执行代码(所有的函数)都存放在这里

    RO_data: 只读数据域 , 存放只读常量 ,如const和字符串

    RW_data 可读写数据域: 存放初始化为 非0值 的可读写变量

     ZI_data: 和RW_data的区别在于存放的都是初始化为 0 的变量

     RO: 包括code和RO_data

     当程序存储在ROM中时,所占用的大小为Code + RO_data + RW_data 。
    当程序执行时, RW_data和 ZI_data在RAM中,RO_data和code视cpu架构(51、arm、x86)不同处于ROM或者RAM中。其中ZI_data对应了BSS段,RW_data对应数据段,code对应代码段, RO_data对应数据段。(RAM:随机存储内存  ; ROM只读内存)

    ##############   程序进程中的分区

    相关概念:堆(heap),栈(stack),BSS段,数据段(data),代码段(code /text),全局静态区,文字常量区,程序代码区。
    BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。
    数据段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。
    代码段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。
    堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。
    栈(stack):栈又称堆栈,用户存放程序临时创建的局部变量。在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的后进先出特点,所以栈特别方便用来保存/恢复调用现场。

     

    全局静态区,文字常量区,程序代码区是从内存地址分配的角度来描述的。
    全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。

    文字常量区—常量字符串就是放在这里的。
    程序代码区—存放函数体的二进制代码。

    ################################################################################

    5.gdb多进程多线程调试

     1 int main()
     2 
     3 {
     4     pid_t pid = fork();//创建子进程
     5     if(pid == -1)
     6     {
     7         perror("fork error");
     8         return -1;
     9     }
    10     else if(pid == 0)//child    
    11     {
    12         printf("i am a child:my pid is %d,my father is%d
    ",getpid(),getppid());
    13     }
    14     else//father
    15     {
    16         printf("i am a father:my pid is %d
    ",getpid());
    17         wait(NULL);//等待子进程
    18     }
    19     return 0;
    20 }
    View Code

    ##################进程间是完全独立互不影响的但可以通信

      fork创建一个完全和父进程一样的子进程,两个进程并行执行,执行先后随机看CPU的调度

         父进程的fork返回值是子进程的pid号

         子进程的fork返回值是0

         wait阻塞函数 等待子进程退出后 父进程才退出

      ##确定gdb中的进程跟踪模式

         show/set follow-fork-mode parent/child 跟踪的父子进程间的切换

         detach-on-fork

         show detach-on-fork和跟踪的进程有关,只负责on和off , 跟踪父进程则负责父进程的on/off , 跟踪子进程则负责子进程的on/off

          parent       on         只调试父进程,子进程正常运行

          child       on         只调试子进程,父进程正常运行

          parent       off         同时调试两个进程,子进程暂停在fork位置

          child        off         同时调试两个进程,父进程暂停在fork位置

        shell ps -ef | grep num/programName 查看指定pid或程序名的进程 shell ps -A查看所有进程

      ##进程间的切换 在detach off 的时候使用切换调试的进程

        info inferiors   查看调试的进程

        inferiors num   切换调试的进程

      #进程的管理

       add-inferior [-copies n] 复制一个进程 -copies + 进程列表num

       [-exec executable] 新开一个进程 + 程序的路径

       detach

       kill

       remove-inferior 删除进程

    ###########多线程

      show/set scheduler-locking off不锁定任何线程 on锁定其他线程只有当前线程执行 step只有被调试线程运行

      gdb版本过低不一定能锁定

      info thread 查看所有线程

      threads num 切换到指定线程

     

    1 int main()
    2 { 
    3     pthread_t tid1,tid2;
    4     pthread_create(&tid1,NULL,thread1,NULL);//创建线程1
    5     pthread_create(&tid2,NULL,thread2,NULL);//创建线程2
    6     pthread_join(tid1,NULL);//等待线程1
    7     pthread_join(tid2,NULL);//等待线程2
    8     return 0;
    9 }
    View Code

    切换到指定线程后调试,断点打在线程函数上不妨碍主线程main的执行所以会有三个线程

  • 相关阅读:
    ASP.NET MVC 重点教程一周年版 第七回 UrlHelper 【转】
    Pycharm集成Python编程环境IDE
    每天前进一厘米如何快速迎头赶上
    终于开博
    大家觉得我有理,可以赞扬一下,觉得无理,可以骂一下。
    好久没有见到有人谈论代码生成器了
    看了FxCop工具
    关于30岁的“青春饭”问题的看法。
    更改后缀为.dex文件为.odex文件 让你的程序瘦身 运行更稳定
    HTC Sensation亲测 修改gps.conf 教你显著提高GPS定位速度
  • 原文地址:https://www.cnblogs.com/yxnrh/p/11490902.html
Copyright © 2020-2023  润新知