【问题标题】:Getting/setting registers for arm32 executable on aarch64在 aarch64 上获取/设置 arm32 可执行文件的寄存器
【发布时间】:2022-09-29 10:36:50
【问题描述】:

我的目标是使用ptrace 设置在 64 位处理器上运行 32 位 ARM 可执行文件的进程的程序计数器(和其他寄存器)。

如果这是一个 64 位可执行文件,那么我可以使用 struct user_pt_regspc 字段(在 asm/ptrace.h 中定义)。

对于 32 位可执行文件,我将使用哪种结构?我看到struct user_regs(在 sys/user.h 中)定义为

struct user_regs {
    unsigned long uregs[18];
};

我阅读了this question 的答案(尽管作者承认这是一个有根据的猜测),这表明使用struct user_regs 中的uregs[15]。但是,由于我实际上有一个 64 位系统,这仍然适用于我的情况吗?

    标签: c arm cpu-registers arm64 ptrace


    【解决方案1】:

    即使在 64 位处理器上运行,ptrace 也会交还有效的32 位寄存器。为了确认这一点,请考虑以下两个可执行文件(为简洁起见,我省略了头文件和错误检查):

    测试.c

    int main()
    {
        printf("PID = %li\n", (long)getpid());
        pause();
        return 0;
    }
    

    附件.c

    int main(int argc, char **argv)
    {
        unsigned int expected_size;
        pid_t pid;
        struct {
            uint32_t r[18];
        } regs;
        struct iovec iov;
    
        pid = atoi(argv[1]); // Yes, I know I should use strtol.  However, this suffices for this example.
        ptrace(PTRACE_ATTACH, pid, NULL, NULL);
    
        iov.iov_base = &regs;
        iov.iov_len = expected_size = sizeof(regs);
        ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov);
        if ( iov.iov_len != expected_size ) {
            fprintf(stderr, "Unexpected size (%zu instead of %u)\n", iov.iov_len, expected_size);
            // abort
        }
        printf("PC is 0x%x\n", regs.r[15]);
    
        ptrace(PTRACE_DETACH, pid, NULL, NULL);
        return 0;
    }
    

    我将第一个编译成 32 位可执行文件,第二个编译成 64 位可执行文件。

    从附件的输出中,我查找了/proc/<pid>/maps 中的地址,发现它位于libc 的可执行部分(对应于pause)。

    【讨论】:

    • 我想知道这对于未指定缓冲区长度的PTRACE_GETREGS 是如何工作的。如果调试器认为自己在调试一个32位的进程,并分配了合适的缓冲区,但该进程实际上是64位的,缓冲区会不会溢出?这听起来像是在特权跟踪器和恶意跟踪器(不是不寻常的组合)的情况下可以利用的东西。
    • 调试器不能从 ELF 标头中确定寄存器的大小吗?
    • 哪个二进制文件的 ELF 标头?跟踪器可能会看到它最初执行了一个 32 位二进制文​​件,但与此同时,被跟踪者可能已经执行了一些其他二进制文件。您还必须执行PTRACE_O_TRACEEXEC 并在命中时从/proc/XXX/exe 刷新标题,以确保始终知道当前位数。即便如此,我也不确定是否还有漏洞。
    • 好吧,您确实将iov.iov_len 设置为您期望的大小。
    • 对于PTRACE_GETREGSET,是的。我说的是PTRACE_GETREGS,它直接将指针指向缓冲区而不是struct iovec,因此无法指定缓冲区大小。
    最近更新 更多