debugger(一):打断点的实现以及案例分析

12 篇文章 0 订阅
7 篇文章 0 订阅

〇、前言

最近在学习 debugger 的实现原理,并按照博客实现,是一个很不错的小项目,这是地址。由于 macOS 的问题,系统调用并不完全相同,因此实现了两个版本分支,一个是 main 版本分支(macOS M1 silicon),另一个是 linux 版本分支(Ubuntu 20.04 x86),这是仓库地址。以下以及后都用 linux 版本代码阐述其原理。

一、断点创建

这很简单,主要是由 ptrace() 实现(debug工具都依赖于 ptrace() ):

#ifndef BREAKPOINT_HPP_
#define BREAKPOINT_HPP_
#include <stdint.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
class BreakPoint {
  pid_t m_pid;
  intptr_t m_addr;
  bool m_enabled;
  uint8_t m_saved_data; // 最低位的旧数据(1 字节),之后需要恢复

public:
  BreakPoint() {}
  BreakPoint(pid_t pid, intptr_t addr)
      : m_pid(pid), m_addr(addr), m_enabled(false), m_saved_data{} {}
  auto is_enabled() const -> bool { return m_enabled; }
  auto get_address() const -> intptr_t { return m_addr; }

  void enable() {
    auto data = ptrace(PTRACE_PEEKDATA, m_pid, m_addr, nullptr);
    m_saved_data = static_cast<uint8_t>(data & 0xff); // save bottom byte

    uint64_t int3 = 0xcc;
    uint64_t data_with_int3 = ((data & ~0xff) | int3); // set bottom byte to
                                                       // 0xcc
    ptrace(PTRACE_POKEDATA, m_pid, m_addr, data_with_int3);

    m_enabled = true;
  }
  void disable() {
    auto data = ptrace(PTRACE_PEEKDATA, m_pid, m_addr, nullptr);
    auto restored_data = ((data & ~0xff) | m_saved_data);
    ptrace(PTRACE_POKEDATA, m_pid, m_addr, restored_data);

    m_enabled = false;
  }
};
#endif

以上是 BreakPoint 类的定义。重点是关注 enable()disable() 两个方法,在这两个方法中,这段代码及其关键:

	auto data = ptrace(PTRACE_PEEKDATA, m_pid, m_addr, nullptr);
    m_saved_data = static_cast<uint8_t>(data & 0xff); // save bottom byte

    uint64_t int3 = 0xcc;
    uint64_t data_with_int3 = ((data & ~0xff) | int3); // set bottom byte to
                                                       // 0xcc
    ptrace(PTRACE_POKEDATA, m_pid, m_addr, data_with_int3);

这里先说明一下,int3x86 中的一个中断指令,只要我们把某个指令修改为 int3,那么它运行到这里就会停下来。另外,我们只是打个断点,又不想真正得越过这个指令(这个指令被越过不执行,谁都不知道会发生什么),所以后面得恢复这个执行,并重新执行它,这就是 disable(),我们先讨论 enable()

因为 int3 指令的代码为 0xcc,这很明显是一个 1 字节指令,只要我们在我们想打断的指令处,将操作码改为 0xcc,这个指令就会停下来(这里牵扯到字节序,因为指令第一个字节是低地址,因为我们需要将 int3 放在一个指令的最低处)。然后再将这个被篡改的指令放回到原处,就成功的打了一个断点。

至于 disable(),其实做的也是这样的事情,将原来的被替换的一个字节再恢复放回去:

	auto data = ptrace(PTRACE_PEEKDATA, m_pid, m_addr, nullptr);
    auto restored_data = ((data & ~0xff) | m_saved_data);
    ptrace(PTRACE_POKEDATA, m_pid, m_addr, restored_data);

    m_enabled = false;

以上都是很简单的东西,我们现在就可以检验这个事情了,对了以下是 debugger 类的定义:

#ifndef DEBUGGER_HPP_
#define DEBUGGER_HPP_

#include "../ext/linenoise/linenoise.h"
#include "breakpoint.hpp"
#include "helpers.hpp"
#include <cstddef>
#include <iostream>
#include <string>
#include <unordered_map>

class debugger {
  std::string m_prog_name;
  pid_t m_pid;
  std::unordered_map<std::intptr_t, BreakPoint> m_breakPoints; // 存储断点

public:
  // 这里不应该给默认参数,断言:传了正确的 prog_name,pid
  debugger(std::string prog_name, pid_t pid)
      : m_prog_name(prog_name), m_pid(pid) {}
  void run() {
    int wait_status;
    auto options = 0;
    waitpid(m_pid, &wait_status, options);
    char *line = nullptr;
    while ((line = linenoise("minidbg> ")) != nullptr) {
      handl_command(line);
      linenoiseHistoryAdd(line);
      linenoiseFree(line);
    }
  }
  // handlers
  void handl_command(const std::string &line) {
    auto args = split(line, ' ');
    auto command = args[0];
    if (is_prefix(command, "continue")) {
      continue_execution();

    } else if (is_prefix(command, "break")) { // break 地址
      std::string addr{args[1], 2};
      set_breakPoint(std::stol(addr, 0, 16));

    } else {
      std::cerr << "Unkown command\n";
    }
  }
  void continue_execution() {
    ptrace(PTRACE_CONT, m_pid, nullptr, nullptr);

    int wait_status;
    auto options = 0;
    waitpid(m_pid, &wait_status, options);
  }

  void set_breakPoint(std::intptr_t addr) {
    std::cout << "Set breakpoint at address 0x" << std::hex << addr
              << std::endl;
    BreakPoint bp{m_pid, addr};
    bp.enable();
    m_breakPoints[addr] = bp;
  }

  ~debugger() {}
};

#endif

二、检测

main() 就是 debuggermain() 了:

#include "../include/debugger.hpp"
#include <cstddef>
#include <iostream>
#include <unistd.h>
#include <sys/personality.h>
int main(int argc, char *argv[]) {
  if (argc < 2) {
    std::cerr << "Program paras are not right.";
    return -1;
  }
  auto proj = argv[1];
  auto pid = fork();
  if (pid == 0) {
    personality(ADDR_NO_RANDOMIZE); // 取消随机内存
    // child progress
    // debugged progress
    ptrace(PTRACE_TRACEME, 0, nullptr, nullptr);
    execl(proj, proj, nullptr);
  } else if (pid >= 1) {
    // parent progress
    // debugger progress

    std::cout << "Start debugging the progress: " << proj << ", pid = " << pid
              << ":\n";
    debugger dbg(proj, pid);
    dbg.run();
  }

  return 0;
}

被 debug 的进程放在子进程中,然后由父进程,也就是我们的 debugger process,由它进行调试。

我们先写一个被 debug 的程序,这个程序输出 hello,world.

#include <iostream>
int main() {
  std::cerr << "hello,world.\n";
  return 0;
}

编译后,我们要打断点进行测试,可以看到目前只能传入一个地址,这个地址还是 0x 开头的 16 进制地址,我们对于这个地址丝毫没有头绪,因为我们不知道 std::cerr << "hello,world.\n";这个语句对应的汇编代码的指令地址是什么。这个程序首先有一个程序结构,对这个不清楚的话,可以看看我之前写的文章,是关于 elf 的,可以参考 c++ 内存模型或者 c++内存管理的那几篇博客。

现在看看这个被调试的程序的文件结构:

objdump -d hw        

hw:     file format elf64-x86-64


Disassembly of section .init:

0000000000001000 <_init>:
    1000:       f3 0f 1e fa             endbr64 
    1004:       48 83 ec 08             sub    $0x8,%rsp
    1008:       48 8b 05 d9 2f 00 00    mov    0x2fd9(%rip),%rax        # 3fe8 <__gmon_start__>
    100f:       48 85 c0                test   %rax,%rax
    1012:       74 02                   je     1016 <_init+0x16>
    1014:       ff d0                   callq  *%rax
    1016:       48 83 c4 08             add    $0x8,%rsp
    101a:       c3                      retq   

Disassembly of section .plt:

0000000000001020 <.plt>:
    1020:       ff 35 82 2f 00 00       pushq  0x2f82(%rip)        # 3fa8 <_GLOBAL_OFFSET_TABLE_+0x8>
    1026:       f2 ff 25 83 2f 00 00    bnd jmpq *0x2f83(%rip)        # 3fb0 <_GLOBAL_OFFSET_TABLE_+0x10>
    102d:       0f 1f 00                nopl   (%rax)
    1030:       f3 0f 1e fa             endbr64 
    1034:       68 00 00 00 00          pushq  $0x0
    1039:       f2 e9 e1 ff ff ff       bnd jmpq 1020 <.plt>
    103f:       90                      nop
    1040:       f3 0f 1e fa             endbr64 
    1044:       68 01 00 00 00          pushq  $0x1
    1049:       f2 e9 d1 ff ff ff       bnd jmpq 1020 <.plt>
    104f:       90                      nop
    1050:       f3 0f 1e fa             endbr64 
    1054:       68 02 00 00 00          pushq  $0x2
    1059:       f2 e9 c1 ff ff ff       bnd jmpq 1020 <.plt>
    105f:       90                      nop

Disassembly of section .plt.got:

0000000000001060 <__cxa_finalize@plt>:
    1060:       f3 0f 1e fa             endbr64 
    1064:       f2 ff 25 65 2f 00 00    bnd jmpq *0x2f65(%rip)        # 3fd0 <__cxa_finalize@GLIBC_2.2.5>
    106b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

Disassembly of section .plt.sec:

0000000000001070 <__cxa_atexit@plt>:
    1070:       f3 0f 1e fa             endbr64 
    1074:       f2 ff 25 3d 2f 00 00    bnd jmpq *0x2f3d(%rip)        # 3fb8 <__cxa_atexit@GLIBC_2.2.5>
    107b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

0000000000001080 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>:
    1080:       f3 0f 1e fa             endbr64 
    1084:       f2 ff 25 35 2f 00 00    bnd jmpq *0x2f35(%rip)        # 3fc0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@GLIBCXX_3.4>
    108b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

0000000000001090 <_ZNSt8ios_base4InitC1Ev@plt>:
    1090:       f3 0f 1e fa             endbr64 
    1094:       f2 ff 25 2d 2f 00 00    bnd jmpq *0x2f2d(%rip)        # 3fc8 <_ZNSt8ios_base4InitC1Ev@GLIBCXX_3.4>
    109b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

Disassembly of section .text:

00000000000010a0 <_start>:
    10a0:       f3 0f 1e fa             endbr64 
    10a4:       31 ed                   xor    %ebp,%ebp
    10a6:       49 89 d1                mov    %rdx,%r9
    10a9:       5e                      pop    %rsi
    10aa:       48 89 e2                mov    %rsp,%rdx
    10ad:       48 83 e4 f0             and    $0xfffffffffffffff0,%rsp
    10b1:       50                      push   %rax
    10b2:       54                      push   %rsp
    10b3:       4c 8d 05 d6 01 00 00    lea    0x1d6(%rip),%r8        # 1290 <__libc_csu_fini>
    10ba:       48 8d 0d 5f 01 00 00    lea    0x15f(%rip),%rcx        # 1220 <__libc_csu_init>
    10c1:       48 8d 3d c1 00 00 00    lea    0xc1(%rip),%rdi        # 1189 <main>
    10c8:       ff 15 12 2f 00 00       callq  *0x2f12(%rip)        # 3fe0 <__libc_start_main@GLIBC_2.2.5>
    10ce:       f4                      hlt    
    10cf:       90                      nop

00000000000010d0 <deregister_tm_clones>:
    10d0:       48 8d 3d 39 2f 00 00    lea    0x2f39(%rip),%rdi        # 4010 <__TMC_END__>
    10d7:       48 8d 05 32 2f 00 00    lea    0x2f32(%rip),%rax        # 4010 <__TMC_END__>
    10de:       48 39 f8                cmp    %rdi,%rax
    10e1:       74 15                   je     10f8 <deregister_tm_clones+0x28>
    10e3:       48 8b 05 ee 2e 00 00    mov    0x2eee(%rip),%rax        # 3fd8 <_ITM_deregisterTMCloneTable>
    10ea:       48 85 c0                test   %rax,%rax
    10ed:       74 09                   je     10f8 <deregister_tm_clones+0x28>
    10ef:       ff e0                   jmpq   *%rax
    10f1:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)
    10f8:       c3                      retq   
    10f9:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)

0000000000001100 <register_tm_clones>:
    1100:       48 8d 3d 09 2f 00 00    lea    0x2f09(%rip),%rdi        # 4010 <__TMC_END__>
    1107:       48 8d 35 02 2f 00 00    lea    0x2f02(%rip),%rsi        # 4010 <__TMC_END__>
    110e:       48 29 fe                sub    %rdi,%rsi
    1111:       48 89 f0                mov    %rsi,%rax
    1114:       48 c1 ee 3f             shr    $0x3f,%rsi
    1118:       48 c1 f8 03             sar    $0x3,%rax
    111c:       48 01 c6                add    %rax,%rsi
    111f:       48 d1 fe                sar    %rsi
    1122:       74 14                   je     1138 <register_tm_clones+0x38>
    1124:       48 8b 05 c5 2e 00 00    mov    0x2ec5(%rip),%rax        # 3ff0 <_ITM_registerTMCloneTable>
    112b:       48 85 c0                test   %rax,%rax
    112e:       74 08                   je     1138 <register_tm_clones+0x38>
    1130:       ff e0                   jmpq   *%rax
    1132:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)
    1138:       c3                      retq   
    1139:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)

0000000000001140 <__do_global_dtors_aux>:
    1140:       f3 0f 1e fa             endbr64 
    1144:       80 3d e5 2f 00 00 00    cmpb   $0x0,0x2fe5(%rip)        # 4130 <completed.0>
    114b:       75 2b                   jne    1178 <__do_global_dtors_aux+0x38>
    114d:       55                      push   %rbp
    114e:       48 83 3d 7a 2e 00 00    cmpq   $0x0,0x2e7a(%rip)        # 3fd0 <__cxa_finalize@GLIBC_2.2.5>
    1155:       00 
    1156:       48 89 e5                mov    %rsp,%rbp
    1159:       74 0c                   je     1167 <__do_global_dtors_aux+0x27>
    115b:       48 8b 3d a6 2e 00 00    mov    0x2ea6(%rip),%rdi        # 4008 <__dso_handle>
    1162:       e8 f9 fe ff ff          callq  1060 <__cxa_finalize@plt>
    1167:       e8 64 ff ff ff          callq  10d0 <deregister_tm_clones>
    116c:       c6 05 bd 2f 00 00 01    movb   $0x1,0x2fbd(%rip)        # 4130 <completed.0>
    1173:       5d                      pop    %rbp
    1174:       c3                      retq   
    1175:       0f 1f 00                nopl   (%rax)
    1178:       c3                      retq   
    1179:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)

0000000000001180 <frame_dummy>:
    1180:       f3 0f 1e fa             endbr64 
    1184:       e9 77 ff ff ff          jmpq   1100 <register_tm_clones>

0000000000001189 <main>:
    1189:       f3 0f 1e fa             endbr64 
    118d:       55                      push   %rbp
    118e:       48 89 e5                mov    %rsp,%rbp
    1191:       48 8d 35 6d 0e 00 00    lea    0xe6d(%rip),%rsi        # 2005 <_ZStL19piecewise_construct+0x1>
    1198:       48 8d 3d 81 2e 00 00    lea    0x2e81(%rip),%rdi        # 4020 <_ZSt4cerr@@GLIBCXX_3.4>
    119f:       e8 dc fe ff ff          callq  1080 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
    11a4:       b8 00 00 00 00          mov    $0x0,%eax
    11a9:       5d                      pop    %rbp
    11aa:       c3                      retq   

00000000000011ab <_Z41__static_initialization_and_destruction_0ii>:
    11ab:       f3 0f 1e fa             endbr64 
    11af:       55                      push   %rbp
    11b0:       48 89 e5                mov    %rsp,%rbp
    11b3:       48 83 ec 10             sub    $0x10,%rsp
    11b7:       89 7d fc                mov    %edi,-0x4(%rbp)
    11ba:       89 75 f8                mov    %esi,-0x8(%rbp)
    11bd:       83 7d fc 01             cmpl   $0x1,-0x4(%rbp)
    11c1:       75 32                   jne    11f5 <_Z41__static_initialization_and_destruction_0ii+0x4a>
    11c3:       81 7d f8 ff ff 00 00    cmpl   $0xffff,-0x8(%rbp)
    11ca:       75 29                   jne    11f5 <_Z41__static_initialization_and_destruction_0ii+0x4a>
    11cc:       48 8d 3d 5e 2f 00 00    lea    0x2f5e(%rip),%rdi        # 4131 <_ZStL8__ioinit>
    11d3:       e8 b8 fe ff ff          callq  1090 <_ZNSt8ios_base4InitC1Ev@plt>
    11d8:       48 8d 15 29 2e 00 00    lea    0x2e29(%rip),%rdx        # 4008 <__dso_handle>
    11df:       48 8d 35 4b 2f 00 00    lea    0x2f4b(%rip),%rsi        # 4131 <_ZStL8__ioinit>
    11e6:       48 8b 05 0b 2e 00 00    mov    0x2e0b(%rip),%rax        # 3ff8 <_ZNSt8ios_base4InitD1Ev@GLIBCXX_3.4>
    11ed:       48 89 c7                mov    %rax,%rdi
    11f0:       e8 7b fe ff ff          callq  1070 <__cxa_atexit@plt>
    11f5:       90                      nop
    11f6:       c9                      leaveq 
    11f7:       c3                      retq   

00000000000011f8 <_GLOBAL__sub_I_main>:
    11f8:       f3 0f 1e fa             endbr64 
    11fc:       55                      push   %rbp
    11fd:       48 89 e5                mov    %rsp,%rbp
    1200:       be ff ff 00 00          mov    $0xffff,%esi
    1205:       bf 01 00 00 00          mov    $0x1,%edi
    120a:       e8 9c ff ff ff          callq  11ab <_Z41__static_initialization_and_destruction_0ii>
    120f:       5d                      pop    %rbp
    1210:       c3                      retq   
    1211:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
    1218:       00 00 00 
    121b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

0000000000001220 <__libc_csu_init>:
    1220:       f3 0f 1e fa             endbr64 
    1224:       41 57                   push   %r15
    1226:       4c 8d 3d 5b 2b 00 00    lea    0x2b5b(%rip),%r15        # 3d88 <__frame_dummy_init_array_entry>
    122d:       41 56                   push   %r14
    122f:       49 89 d6                mov    %rdx,%r14
    1232:       41 55                   push   %r13
    1234:       49 89 f5                mov    %rsi,%r13
    1237:       41 54                   push   %r12
    1239:       41 89 fc                mov    %edi,%r12d
    123c:       55                      push   %rbp
    123d:       48 8d 2d 54 2b 00 00    lea    0x2b54(%rip),%rbp        # 3d98 <__do_global_dtors_aux_fini_array_entry>
    1244:       53                      push   %rbx
    1245:       4c 29 fd                sub    %r15,%rbp
    1248:       48 83 ec 08             sub    $0x8,%rsp
    124c:       e8 af fd ff ff          callq  1000 <_init>
    1251:       48 c1 fd 03             sar    $0x3,%rbp
    1255:       74 1f                   je     1276 <__libc_csu_init+0x56>
    1257:       31 db                   xor    %ebx,%ebx
    1259:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)
    1260:       4c 89 f2                mov    %r14,%rdx
    1263:       4c 89 ee                mov    %r13,%rsi
    1266:       44 89 e7                mov    %r12d,%edi
    1269:       41 ff 14 df             callq  *(%r15,%rbx,8)
    126d:       48 83 c3 01             add    $0x1,%rbx
    1271:       48 39 dd                cmp    %rbx,%rbp
    1274:       75 ea                   jne    1260 <__libc_csu_init+0x40>
    1276:       48 83 c4 08             add    $0x8,%rsp
    127a:       5b                      pop    %rbx
    127b:       5d                      pop    %rbp
    127c:       41 5c                   pop    %r12
    127e:       41 5d                   pop    %r13
    1280:       41 5e                   pop    %r14
    1282:       41 5f                   pop    %r15
    1284:       c3                      retq   
    1285:       66 66 2e 0f 1f 84 00    data16 nopw %cs:0x0(%rax,%rax,1)
    128c:       00 00 00 00 

0000000000001290 <__libc_csu_fini>:
    1290:       f3 0f 1e fa             endbr64 
    1294:       c3                      retq   

Disassembly of section .fini:

0000000000001298 <_fini>:
    1298:       f3 0f 1e fa             endbr64 
    129c:       48 83 ec 08             sub    $0x8,%rsp
    12a0:       48 83 c4 08             add    $0x8,%rsp
    12a4:       c3                      retq   

可以看到,这个程序虽然只是输出 hello,world.,但依然很复杂,因为它要包含其它很多的基础资源或者子程序,我们只需要重点关注 main

0000000000001189 <main>:
    1189:       f3 0f 1e fa             endbr64 
    118d:       55                      push   %rbp
    118e:       48 89 e5                mov    %rsp,%rbp
    1191:       48 8d 35 6d 0e 00 00    lea    0xe6d(%rip),%rsi        # 2005 <_ZStL19piecewise_construct+0x1>
    1198:       48 8d 3d 81 2e 00 00    lea    0x2e81(%rip),%rdi        # 4020 <_ZSt4cerr@@GLIBCXX_3.4>
    119f:       e8 dc fe ff ff          callq  1080 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
    11a4:       b8 00 00 00 00          mov    $0x0,%eax
    11a9:       5d                      pop    %rbp
    11aa:       c3                      retq   

可以看到,这个段是从0000000000001189开始的,需要关注的输出语句为:

    119f:       e8 dc fe ff ff          callq  1080 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>

这个地址 119f 就是我们需要打断点的地方,被我们找出来了,这个地址是定死的,它在运行的时候,需要加载到内存中。问题是,加载到哪里?

我们并不知道加载到哪里,换句话说,我们不知道段地址是什么,它不固定,这主要是为了程序数据安全考虑,采用了内存分布随机化,我们可以关掉内存分布随机化

if (pid == 0) {
    personality(ADDR_NO_RANDOMIZE); // 取消随机内存
    // child progress
    // debugged progress
    ptrace(PTRACE_TRACEME, 0, nullptr, nullptr);
    execl(proj, proj, nullptr);
    ...

这样它就固定了,我们可以这样查看它在运行的时候的 map,首先用我们程序进行调试:

./main hw
Start debugging the progress: hw, pid = 260915:
minidbg> 

可以看到,pid260915,另开一个 zsh,直接查看:

cat /proc/260915/maps
555555554000-555555555000 r--p 00000000 fc:01 698165                     /root/mydebugger/src/hw
555555555000-555555556000 r-xp 00001000 fc:01 698165                     /root/mydebugger/src/hw
555555556000-555555557000 r--p 00002000 fc:01 698165                     /root/mydebugger/src/hw
555555557000-555555559000 rw-p 00002000 fc:01 698165                     /root/mydebugger/src/hw
7ffff7fcb000-7ffff7fce000 r--p 00000000 00:00 0                          [vvar]
7ffff7fce000-7ffff7fcf000 r-xp 00000000 00:00 0                          [vdso]
7ffff7fcf000-7ffff7fd0000 r--p 00000000 fc:01 1185709                    /usr/lib/x86_64-linux-gnu/ld-2.31.so
7ffff7fd0000-7ffff7ff3000 r-xp 00001000 fc:01 1185709                    /usr/lib/x86_64-linux-gnu/ld-2.31.so
7ffff7ff3000-7ffff7ffb000 r--p 00024000 fc:01 1185709                    /usr/lib/x86_64-linux-gnu/ld-2.31.so
7ffff7ffc000-7ffff7ffe000 rw-p 0002c000 fc:01 1185709                    /usr/lib/x86_64-linux-gnu/ld-2.31.so
7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0 
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0                          [stack]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0

可以看到我们的可执行代码也就是 main 段在这里:

555555555000-555555556000 r-xp 00001000 fc:01 698165                     /root/mydebugger/src/hw

这以后都是固定的,虽然不安全,但是仅仅是为了演示,就没关系了。段的偏移地址为555555554000,因为我们需要打断点的地址为119f,因此:

基址 + 指令相对地址
= 555555554000 + 119f
= 55555555519f

可以预见的是,如果 break 0x55555555519f,之后执行,并不会打印出 hello,world,但是我们如果打到了下一条地址:0x5555555551a4,运行之后,就会理解打印出 hello,world。以下进行检测:

./main hw
Start debugging the progress: hw, pid = 261169:
minidbg> break 0x55555555519f
Set breakpoint at address 0x55555555519f
minidbg> continue
minidbg> 

我们换一个地址:

./main hw
Start debugging the progress: hw, pid = 261407:
minidbg> break 0x0x5555555551a4
Set breakpoint at address 0x5555555551a4
minidbg> continue
hello,world.
minidbg> 

可以看到,以下就打印出了hello,world。以上符合我们的预期,因此实验是成功的,另外不需要担心 pid 不一样,由于我们关闭了地址空间布局随机化(ASLR, Address Space Layout Randomization),段地址不会变,因此地址也是固定的。

  • 23
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值