F979d3091912ea9fde873167878bd3c0
调试器如何对源程序进行修改一

0x0 序言

在上篇文章中,介绍了断点调试的基本原理,最终我们知道其实是通过修改了目标程序的“机器码”指令,使其改为对应的软中断指令 int 3 实现的。那么本文我们从代码层面来探究一下调试器是如何将这个指令修改的呢?

本文也会从一个代码追溯的角度来追寻实现,方便大家理解。

0x1 调试器如何修改目标程序指令

顺着上文中的代码,继续看 ProcessGDBRemote::EnableBreakpointSite 方法

  if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware) &&
      (!bp_site->HardwareRequired())) {
    // 关键是这行,注释也比较关键,说发送了一个 $Z0 的packet
    // Try to send off a software breakpoint packet ($Z0)
    uint8_t error_no = m_gdb_comm.SendGDBStoppointTypePacket(
        eBreakpointSoftware, true, addr, bp_op_size);
    if (error_no == 0) {
      // The breakpoint was placed successfully
      bp_site->SetEnabled(true);
      bp_site->SetType(BreakpointSite::eExternal);
      return error;
    }

上面列的代码的关键行继续往下走,看到了如下信息:

就是在 GDBRemoteCommunication::SendRawPacketNoLock 方法中写入了一个这样的信息: $Z0,10e869e9f,1#84 。 那么是往什么里面写入的呢?这个信息又代表了什么呢?

往下看,我们聚焦到了下面这个类:

size_t ConnectionFileDescriptor::Write(const void *src, size_t src_len,
                                       ConnectionStatus &status,
                                       Status *error_ptr) {
  .... 省略代码

  .... m_write_sp 是socket,支持Tcp, Udp, 和本地fd
  error = m_write_sp->Write(src, bytes_sent);

  .... 省略代码
}

0x2 Debugserver

上面我们代码定位到了一个暂时的结论:

lldb发送了一个 packet 给socket

那么是谁接收了这个packet信息呢? 先抛出结果吧,那就是debugserver。 下面我们从代码上来发现这个步骤。

继续从上小节中的 ConnectionFileDescriptor 这个类着手,我们在它的构造函数中打个断点,看下它是如何初始化的。打完断点,然后在lldb中执行一个 process attach 命令发现就可以断住,然后找到了调用它的代码(看代码中的文字说明):

    int sockets[2]; /* the pair of socket descriptors */
    .... 通过socketpair 创建双向通行管道
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) {
      error.SetErrorToErrno();
      return error;
    }
    int our_socket = sockets[0];
    int gdb_socket = sockets[1];
    .... 省略代码
    .... gdb_socket 作为参数传递给将要开启的 Debugserver进程
    communication_fd = gdb_socket;
    error = m_gdb_comm.StartDebugserverProcess(
        nullptr, GetTarget().GetPlatform().get(), debugserver_launch_info,
        nullptr, nullptr, communication_fd);

    .... 省略代码
    .... our_socket 传递给 ConnectionFileDescriptor
      m_gdb_comm.SetConnection(new ConnectionFileDescriptor(our_socket, true));

通过这段代码,我们发现其实就是通过socketpair 创建了一个双向通行管道,一个传递给 ConnectionFileDescriptor, 另一个传递给了一个新启动的 Debugserver 进程:

// 通过 -fd=6 来传递了fileDescriptor
$path_to_lldb_build/LLDB.framework/Resources/debugserver -fd=6 --native-regs --setsid

0x3 Packet

既然是发送给了 Debugserver, 那么我们就在Debugserver中找答案,发现 lldb工程中有个单独的project,就是Debugserver.

RNBRemote.cpp 中我们找到了关于所有与lldb通信的packet类型。
```
typedef enum {
invalid_packet = 0,
ack, // '+'
nack, // '-'
halt, // ^C (async halt)
use_extended_mode, // '!'
why_halted, // '?'
set_argv, // 'A'
set_bp, // 'B'
cont, // 'c'
continue_with_sig, // 'C'
detach, // 'D'
read_general_regs, // 'g'
write_general_regs, // 'G'
set_thread, // 'H'
step_inferior_one_cycle, // 'i'
signal_and_step_inf_one_cycle, // 'I'
kill, // 'k'
read_memory, // 'm'
write_memory, // 'M'
read_register, // 'p'
write_register, // 'P'
restart, // 'R'
single_step, // 's'
single_step_with_sig, // 'S'
search_mem_backwards, // 't'
thread_alive_p, // 'T'
vattach, // 'vAttach;pid'
vattachwait, // 'vAttachWait:XX...' where XX is one or more hex encoded
// process name ASCII bytes
vattachorwait, // 'vAttachOrWait:XX...' where XX is one or more hex encoded
// process name ASCII bytes
vattachname, // 'vAttachName:XX...' where XX is one or more hex encoded
// process name ASCII bytes
vcont, // 'vCont'
vcont_list_actions, // 'vCont?'
read_data_from_memory, // 'x'
write_data_to_memory, // 'X'
insert_mem_bp, // 'Z0'
remove_mem_bp, // 'z0'
insert_hardware_bp, // 'Z1'
remove_hardware_bp, // 'z1'
insert_write_watch_bp, // 'Z2'
remove_write_watch_bp, // 'z2'
insert_read_watch_bp, // 'Z3'
remove_read_watch_bp, // 'z3'
insert_access_watch_bp, // 'Z4'
remove_access_watch_bp, // 'z4'

query_monitor,                                 // 'qRcmd'
query_current_thread_id,                       // 'qC'
query_get_pid,                                 // 'qGetPid'
query_echo,                                    // 'qEcho'
query_thread_ids_first,                        // 'qfThreadInfo'
query_thread_ids_subsequent,                   // 'qsThreadInfo'
query_thread_extra_info,                       // 'qThreadExtraInfo'
query_thread_stop_info,                        // 'qThreadStopInfo'
query_image_offsets,                           // 'qOffsets'
query_symbol_lookup,                           // 'qSymbol'
query_launch_success,                          // 'qLaunchSuccess'
query_register_info,                           // 'qRegisterInfo'
query_shlib_notify_info_addr,                  // 'qShlibInfoAddr'
query_step_packet_supported,                   // 'qStepPacketSupported'
query_supported_features,                      // 'qSupported'
top Created with Sketch.