【问题标题】:ptrace change syscall number arm64ptrace 更改系统调用号 arm64
【发布时间】:2020-08-27 16:18:21
【问题描述】:

我正在尝试使用 ptrace 在 linux arm64 上将调用从一个系统调用更改为另一个系统调用。

据我了解,系统调用号在 x8 中,读取系统调用的寄存器可以确认这一点。即时更改此号码并调用 SETREGSET 并调用旧系统调用而不是新系统调用。当我检查从系统调用返回的寄存器时,x8 设置为我给他的系统调用。

为同一个系统调用更改其他参数是可行的。

我看到一些使用 PTRACE_SET_SYSCALL 的地方,但我找不到很多关于它的信息,我尝试使用它,但它似乎在这个拱门中不受支持,它没有定义并且写入数字反而因为不存在而失败。

我做错了什么?为什么它不起作用?

这是代码,为了简单起见,我删除了打印和验证,对于这个例子,我只是试图停止写入系统调用:

ptrace(PTRACE_ATTACH, pid, NULL, NULL);

int status;
waitpid(pid, &status, 0);
while (ptrace(PTRACE_SYSCALL, pid, NULL, NULL) == 0)
{
    waitpid(pid, &status, 0);

    struct user_pt_regs regs;
    struct iovec io;
    io.iov_base = &regs;
    io.iov_len = sizeof(regs);

    ptrace(PTRACE_GETREGSET, pid, (void*)NT_PRSTATUS, &io);

    // reg[7] is 0 before syscall and 1 after
    if (regs.regs[7] == 0)
    {
        // Change write syscall
        if (regs.regs[8] == 64)
        {
            // Change the syscall to getpid (doesn't matter)
            regs.regs[8] = 172;

            ptrace(PTRACE_SETREGSET, pid, (void*)NT_PRSTATUS, &io);
        }
    }
}

至于tracee,一个简单的hello world with write with sleep;

char buf[] = "hello world\n";
while(1)
{
    write(1, buf, sizeof(buf));
    sleep(5);
}

虽然程序运行没有错误并打印正确的寄存器,但系统调用并没有改变,它继续打印 hello world

【问题讨论】:

    标签: c linux arm arm64 ptrace


    【解决方案1】:

    在 arm64 上,出于兼容性原因,内核将要执行的系统调用存储在单独的变量 pt_regs.syscallno 中,而 ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov) 确实更改了寄存器(参见内核源代码中 struct pt_regs 的定义:commit:eec4df2/arch/arm64/include/asm/ptrace.h:178 )。

    在 arm 或 arm64 上并且内核使用CONFIG_COMPAT=yes 编译时,系统调用号可以通过ptrace(PTRACE_SET_SYSCALL, pid, NULL, syscallno) 更改。

    对于没有CONFIG_COMPAT=yes(更常见)的 arm64 内核,您需要ptrace(PTRACE_SETREGSET, pid, NT_ARM_SYSTEM_CALL, &iov)

    例子:

    int syscallno;
    struct iovec iov = {
        .iov_base = &syscallno,
        .iov_len = sizeof (int),
    };
    ptrace(PTRACE_SETREGSET, traceePid, NT_ARM_SYSTEM_CALL, &iov);
    

    NT_ARM_SYSTEM_CALL 的注册集在此处定义:commit:eec4df2/arch/arm64/kernel/ptrace.c:1173

    使用SYS_writeSYS_chdir 等参数阻止系统调用的另一种方法是将参数设置为无效地址,这样它就会失败并显示EINVAL

    struct user_regs_struct regs;
    struct iovec iov = {
        .iov_base = &regs,
        .iov_len = sizeof (struct user_regs_struct),
    };
    ptrace(PTRACE_GETREGSET, traceePid, NT_PRSTATUS, &iov);
    regs.regs[0] = 123;
    regs.regs[1] = 456;
    regs.regs[3] = 789;
    ptrace(PTRACE_SETREGSET, traceePid, NT_PRSTATUS, &iov);
    

    在系统调用中注册用法:Chromium OS Docs

    另请参阅:Gist: SBell6hf / A ptrace-based syscall jailer that runs on arm64, x86_64 and i386

    【讨论】:

      【解决方案2】:

      由于某种原因,您似乎必须在寄存器 0 中设置系统调用号,而不是像 aarch64 ABI 所期望的那样设置 8

      // Change the syscall to getpid (doesn't matter)
      regs.regs[0] = 172;
      

      我不确定为什么会这样;可能 ptrace 在进入 syscall 函数时停止执行进程,然后将第一个参数(在寄存器 0 中)移动到寄存器 8 本身中的实际系统调用号参数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-08-14
        • 2011-06-09
        • 2012-11-18
        • 2011-07-26
        • 2012-11-05
        • 2017-01-03
        • 2012-04-03
        相关资源
        最近更新 更多