调试器如何查看另一个进程的内存?

How does a debugger peek into another process' memory?

本文关键字:内存 进程 另一个 何查看 调试器      更新时间:2023-10-16

当每个进程都有自己的私有内存空间,没有外部进程可以访问时,调试器如何访问进程的内存空间?

例如,我可以使用gdb -p <pid>
将gdb附加到正在运行的进程我可以通过gdb访问这个进程的所有内存。

gdb是如何做到这一点的?

我阅读了SO中的相关问题,似乎没有帖子回答这一点。

由于问题被标记为Linux和Unix,我将进一步阐述David Scwartz所说的,简而言之,"在操作系统中有一个API"。同样的基本原理也适用于Windows,但实际实现是不同的,尽管我怀疑操作系统内部的实现做了同样的事情,但没有真正的方法知道这一点,因为我们无法检查Windows的源代码(然而,人们可以了解操作系统和处理器是如何工作的,有点弄清楚必须发生什么!)

Linux有一个名为ptrace的函数,它允许一个进程(在检查权限之后)以各种方式检查另一个进程。这是一个调用,但第一个参数是"你想做什么"。以下是一些最基本的例子——还有几十个其他不太"常见"的操作:

  • PTRACE_ATTACH-连接到进程
  • PTRACE_PEEKTEXT-查看所附进程的代码内存(例如反汇编代码)
  • PTRACE_PEEKDATA-查看附加进程的数据内存(显示变量)
  • PTRACE_POKETEXT-写入进程代码内存
  • PTRACE_POKEDATA—写入进程的数据内存
  • PTRACE_GETREGS—复制当前寄存器值
  • PTRACE_SETREGS-更改当前寄存器值(例如,如果x恰好在寄存器中,则为set variable x = 7的调试命令)

在Linux中,由于内存"完全相同",PTRACE_PEEKTEXTPTRACE_PEEKDATA实际上是相同的功能,因此您可以在代码中为PTRACE_PEEKDATA提供地址,并在堆栈上为PTRACE_PEEKTEXT提供地址,它会非常乐意地为您复制回地址。区别在于操作系统/处理器组合,其中内存在DATA内存和CODE内存之间"拆分"。大多数现代操作系统和处理器都没有这种区别。CCD_ 16和CCD_。

因此,假设"调试器进程"使用:

long data = ptrace(PTRACE_PEEKDATA, pid, 0x12340128, NULL); 

当使用地址为0x12340128的PTRACE_PEEKDATA调用操作系统时,它将"查看"0x12340128[页面对齐,即0x12340000]处内存的相应内存映射,如果存在,它将被映射到内核中,然后数据从地址0x12340128]复制到本地内存中,内存未映射,复制的数据作为返回值返回。

手册将使用的启动说明为:

父级可以通过调用fork(2)并具有生成的子级执行PTRACE_TRACEME,然后(通常)执行(3)。或者,父进程可以开始跟踪现有进程使用PTRACE_ATTACH。

有关更多信息,请参阅man ptrace

当每个进程都有自己的私有内存空间时,没有外部进程可以访问。。。

这是错误的。具有正确权限并使用正确API的外部进程可以访问其他进程的内存。

对于linux调试,有一个系统调用ptrace,它可以控制系统上的另一个进程。事实上,如果你是流程的所有者,并且没有手动删除权限,那么你需要这样做的权限,这通常是给定的。

操作系统调用ptrace本身允许访问内存、程序计数器、寄存器以及几乎所有其他相关的读取和写入内容。

详见man ptrace

如果您对它在调试器中的工作方式感兴趣,请查看中的文件gdb-x.x.x/gdb/linux-nat.c。在那里,您可以找到用于访问要调试的其他进程的核心内容。