如何在 Rust 中获取给定 CPU 寄存器的偏移量

How to get the offset of a given CPU register in Rust

作为练习,我一直在尝试使用 Rust 的 nix::sys::ptrace::ptrace(这与 C 的 ptrace 几乎相同)通过读取RAX/RBX/RCX/RDX 在每次系统调用时注册。我想要的输出与此类似:

Found syscall: 4
Arg1: 1
Arg2: Hello World!

从我在网上找到的示例来看,PTRACE_PEEKUSERPTRACE_PEEKDATA 的组合似乎最适合实现此目的(也许 PTRACE_GETREGS 也是?),但我我很难理解如何满足我需要传递给这些函数以使其工作的第三个参数。

ptrace 的手册页列出了这个演示正确用法的示例:

ptrace(PTRACE_PEEKTEXT/PEEKDATA/PEEKUSER, pid, addr, 0);

手册页只是说,"Read a word at the address addr in the tracee's memory." 但是 addr 应该是什么?我怎样才能确定正确的地址来满足这个论点?

我在网上找到的例子都是这样的:

ptrace(PTRACE_PEEKUSER, pid, sizeof(long)*ORIG_EAX, 0);

ptrace(PTRACE_PEEKUSER, pid, somenumber*RAX, 0)

如何find/calculate这些寄存器在运行时的偏移量? (在 Rust 中!)

It's been a long time since I poked Linux kernel (Mac user now). Buf if I remember it correctly then ...

内核headers

安装内核 headers,类似 sudo apt-get install linux-headers-$(uname -r)。假设您在 x86_64-linux-gnu(根据您的 rax 兴趣猜测)。

打开/usr/include/x86_64-linux-gnu/sys/reg.hheader:

...
#ifdef __x86_64__
/* Index into an array of 8 byte longs returned from ptrace for
   location of the users' stored general purpose registers.  */

# define R15    0
# define R14    1
# define R13    2
# define R12    3
# define RBP    4
# define RBX    5
# define R11    6
# define R10    7
# define R9     8
# define R8     9
# define RAX    10
...

评论说:

Index into an array of 8 byte longs returned from ptrace for location of the users' stored general purpose registers.

所有这些宏(RAXRCX、...)为特定寄存器定义了 indexes。因为每个都是 8 字节长(仅 x86_64),所以偏移量是 8 * $index。在 rax 寄存器的情况下,偏移量计算为 8 * RAX = 8 * 10 = 8080 是您应该在 ptrace 函数调用中用于 addr 参数的内容。这就是它的工作原理。请注意,它与其他架构等不同。

PTRACE_*

PTRACE_PEEKUSER - 用于寄存器和其他调试信息。

PTRACE_PEEKDATA - 用于程序数据和代码。

PTRACE_PEEKTEXT - man ptrace (Linux) 表示 - 将字数据复制到被跟踪者内存中的地址addr。至于PTRACE_PEEKTEXTPTRACE_PEEKDATA,这两个请求目前是等价的。那是因为Linux没有单独的文本和数据地址空间。

生锈 & PTRACE_PEEKUSER

nix 箱子提供 getregs function to read all of them. It returns libc user_regs_struct。仅 Linux 支持:

libc crate 也包含这些索引:

如果您只对一个寄存器感兴趣,您可以使用此索引来计算 ptrace function. Multiply it with 8 (#[cfg(target_arch = "x86_64")]) / 4 (#[cfg(target_arch = "x86")]) and use PTRACE_PEEKUSER to read it (see Request) 的偏移量/地址。

生锈 & PTRACE_PEEKDATA

阅读 What are the calling conventions for UNIX & Linux system calls on i386 and x86-64. In other words, you're interested in rdi, rsi, rdx, ... registers. The nix crate provides specialized read function, which internally calls ptrace 函数与 PTRACE_PEEKDATA

nix 箱子

ptrace 函数已弃用。文档说明:

Deprecated since 0.10.0: usages of ptrace() should be replaced with the specialized helper functions instead

您应该使用 getregsread 等专用函数。您可以找到它们的列表 in the documentation.