ptrace更改系统调用号arm64

3

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

据我所知,系统调用号码在x8中,读取系统调用的寄存器可以确认这一点。我更改了这个数字并调用SETREGSET, 旧的系统调用被调用而不是新的。在系统调用返回时检查寄存器,x8被设置为我所提供的系统调用,就像应该做的那样。

更改相同系统调用的其他参数是有效的。

我看到一些地方使用PTRACE_SET_SYSCALL,但我找不到太多相关信息,我尝试使用它,但似乎这个组合体系结构不支持它,它未定义,在写入数字失败因为不存在。

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

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

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);
        }
    }
}

对于被追踪的对象,一个简单的hello world程序,带有写和休眠操作;

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

尽管程序没有错误并且打印了正确的寄存器,但系统调用没有改变并且它仍然打印“hello world”。
2个回答

3
在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”的Regset在此处定义:commit:eec4df2/arch/arm64/kernel/ptrace.c:1173
另一种阻止具有参数的系统调用(如“SYS_write”或“SYS_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文档
另请参阅:Gist: SBell6hf /基于ptrace的系统调用监狱,在arm64、x86_64和i386上运行

0

由于某些原因,似乎需要将系统调用号设置为寄存器0,而不是从aarch64 ABI预期的8寄存器。

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接