【发布时间】:2019-04-26 15:09:24
【问题描述】:
我正在尝试使用 ptrace 来跟踪由单独进程进行的所有系统调用,无论是 32 位 (IA-32) 还是 64 位 (x86-64)。我的跟踪器将在启用 IA-32 仿真的 64 位 x86 安装上运行,但理想情况下将能够跟踪 64 位和 32 位应用程序,包括 64 位应用程序是否分叉并执行 32 位进程.
问题在于,由于 32 位和 64 位系统调用号不同,我需要知道进程是 32 位还是 64 位才能确定它使用哪个系统调用,即使我有系统调用号。似乎有 imperfect methods,比如检查 /proc/<pid>/exec 或(如 strace 一样)寄存器结构的大小,但没有什么可靠的。
更复杂的是,64 位进程可以switch out of long mode 直接执行 32 位代码。他们也可以make 32-bit int $0x80 syscalls,当然,它使用 32 位系统调用号。我不“相信”我追踪的过程不使用这些技巧,所以我想正确地检测它们。而且我已经独立验证,至少在后一种情况下,ptrace 看到的是 32 位系统调用号和参数寄存器分配,而不是 64 位的。
我在内核源代码中四处寻找,发现arch/x86/include/asm/processor.h 中的TS_COMPAT 标志,每当64 位进程进行32 位系统调用时,它似乎是set。唯一的问题是我不知道如何从用户区访问这个标志,或者是否有可能。
我还考虑阅读%cs 并将其与$0x23 或$0x33 进行比较,灵感来自this method 在运行过程中切换位数。但这仅检测 32 位 进程,不一定是来自 64 位进程的 32 位 系统调用(使用 int $0x80 进行的调用)。它也很脆弱,因为它依赖于未记录的内核行为。
最后,我注意到 x86 架构的扩展功能启用寄存器 MSR 中有一点长模式。但是 ptrace 无法从 tracee 中读取 MSR,我觉得从我的 tracer 中读取它是不够的,因为我的 tracer 总是以长模式运行。
我很茫然。也许我可以尝试使用其中一种技巧——此时我倾向于%cs 或/proc/<pid>/exec 方法——但我想要一些能够真正区分32 位和64 位系统调用的持久性。 在 x86-64 下使用 ptrace 的进程如何检测到其被跟踪者进行了系统调用,如何可靠地确定该系统调用是使用 32 位 (int $0x80) 还是 64 位 (syscall) 进行的ABI? 用户进程是否有其他方法可以获取有关它被授权 ptrace 的另一个进程的信息?
【问题讨论】:
-
有一个提议的内核补丁可以添加一个PTRACE_GET_SYSCALL_INFO 请求来帮助解决这个问题。
-
@MarkPlotnick 这看起来很棒,感谢您指出!如果它被合并,我可以使用该请求作为旧解决方案的替代方案
-
更新:PTRACE_GET_SYSCALL_INFO 在 Linux 5.3 中。
标签: linux x86 x86-64 system-calls ptrace