为什么 ptrace 显示 32 位 execve 系统调用具有 EAX = 59,即 64 位调用号? 32 位系统调用如何在 x86-64 上工作?
Why does ptrace show a 32-bit execve system call having EAX = 59, the 64-bit call number? How do 32-bit system calls work on x86-64?
我正在 ptrace
使用以下代码。我发现 execve
的系统调用号是 59,即使我使用 -m32
选项进行编译。由于我在 64 位机器上使用 Ubuntu,因此可以理解。
很快,问题出现了:“libc32 在 32 位机器和 64 位机器上的行为是否不同?它们不同吗?”所以我检查了 libc32 在 64 位中有什么。但是,libc 的 execve
系统调用号是 11,这与 32 位系统的 execv
系统调用号相同。那么魔法在哪里发生呢?提前谢谢你。
这是代码。它起源于 https://www.linuxjournal.com/article/6100
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/user.h>
#include <stdio.h>
int main()
{
pid_t child;
long orig_eax;
child = fork();
if (child == 0) {
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execl("/bin/ls", "ls", NULL);
} else {
wait(NULL);
orig_eax = ptrace(PTRACE_PEEKUSER,
#ifdef __x86_64__
child, &((struct user_regs_struct *)0)->orig_rax,
#else
child, &((struct user_regs_struct *)0)->orig_eax,
#endif
NULL);
printf("The child made a "
"system call %ld\n", orig_eax);
ptrace (PTRACE_CONT, child, NULL, NULL);
}
return 0;
}
这是代码的结果
~/my-sandbox/ptrace$ file s1 && ./s1
s1: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=f84894c2f5373051682858937bf54a66f21cbeb4, for GNU/Linux 3.2.0, not stripped
The child made a system call 59
~/my-sandbox/ptrace$ file s2 && ./s2
s2: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=cac6a2bbeee164e27c11764c1b68f4ddd06405cf, for GNU/Linux 3.2.0, with debug_info, not stripped
The child made a system call 59
这是我使用 gdb 从 32 位可执行文件中得到的。如您所见,它使用的是 /lib/i386-linux-gnu/libc.so.6,execve 的系统调用号是 11.
>>> bt
#0 0xf7e875a0 in execve () from /lib/i386-linux-gnu/libc.so.6
#1 0xf7e8799f in execl () from /lib/i386-linux-gnu/libc.so.6
#2 0x565562a4 in main () at simple1.c:15
>>> disassemble
Dump of assembler code for function execve:
=> 0xf7e875a0 <+0>: endbr32
0xf7e875a4 <+4>: push %ebx
0xf7e875a5 <+5>: mov 0x10(%esp),%edx
0xf7e875a9 <+9>: mov 0xc(%esp),%ecx
0xf7e875ad <+13>: mov 0x8(%esp),%ebx
0xf7e875b1 <+17>: mov [=12=]xb,%eax
0xf7e875b6 <+22>: call *%gs:0x10
0xf7e875bd <+29>: pop %ebx
0xf7e875be <+30>: cmp [=12=]xfffff001,%eax
0xf7e875c3 <+35>: jae 0xf7dd9000
0xf7e875c9 <+41>: ret
End of assembler dump.
execve
很特别;它是唯一与 PTRACE_TRACEME
有特殊互动的人。 strace
的工作方式,其他系统调用 do 显示 32 位调用号。 (和现代 strace needs special help to know whether that's a 32-bit call number for int 0x80
/ sysenter
, or a 64-bit call number, since 64-bit processes can still invoke int 0x80
, although . This support ,PTRACE_GET_SYSCALL_INFO
)
你是对的,当内核被实际调用时,EAX 持有 11
,__NR_execve
来自 unistd_32.h
。它由 mov [=21=]xb,%eax
在 glibc 的 execve 包装器跳转到 VDSO 页面以通过此硬件支持的任何有效方法进入内核之前设置(通常 sysenter
。)
但是执行实际上并没有停止,直到它到达主要 execve
实现中检查 PTRACE_TRACEME
并引发 SIGTRAP
.
的一些代码
显然在此之前的某个时间,它会调用 void set_personality_64bit(void)
in arch/x86/kernel/process_64.c,其中包括
/* Pretend that this comes from a 64bit execve */
task_pt_regs(current)->orig_ax = __NR_execve;
我在内核源代码浏览器中通过 searching for __NR_execve
发现了这一点,并查看了 arch/x86 中最有可能的文件。我没有通过交叉引用来查找调用的位置;它存在的事实(以及合理的非混淆设计的假设)非常强烈地表明这就是你的谜团的答案。
我正在 ptrace
使用以下代码。我发现 execve
的系统调用号是 59,即使我使用 -m32
选项进行编译。由于我在 64 位机器上使用 Ubuntu,因此可以理解。
很快,问题出现了:“libc32 在 32 位机器和 64 位机器上的行为是否不同?它们不同吗?”所以我检查了 libc32 在 64 位中有什么。但是,libc 的 execve
系统调用号是 11,这与 32 位系统的 execv
系统调用号相同。那么魔法在哪里发生呢?提前谢谢你。
这是代码。它起源于 https://www.linuxjournal.com/article/6100
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/user.h>
#include <stdio.h>
int main()
{
pid_t child;
long orig_eax;
child = fork();
if (child == 0) {
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execl("/bin/ls", "ls", NULL);
} else {
wait(NULL);
orig_eax = ptrace(PTRACE_PEEKUSER,
#ifdef __x86_64__
child, &((struct user_regs_struct *)0)->orig_rax,
#else
child, &((struct user_regs_struct *)0)->orig_eax,
#endif
NULL);
printf("The child made a "
"system call %ld\n", orig_eax);
ptrace (PTRACE_CONT, child, NULL, NULL);
}
return 0;
}
这是代码的结果
~/my-sandbox/ptrace$ file s1 && ./s1
s1: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=f84894c2f5373051682858937bf54a66f21cbeb4, for GNU/Linux 3.2.0, not stripped
The child made a system call 59
~/my-sandbox/ptrace$ file s2 && ./s2
s2: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=cac6a2bbeee164e27c11764c1b68f4ddd06405cf, for GNU/Linux 3.2.0, with debug_info, not stripped
The child made a system call 59
这是我使用 gdb 从 32 位可执行文件中得到的。如您所见,它使用的是 /lib/i386-linux-gnu/libc.so.6,execve 的系统调用号是 11.
>>> bt
#0 0xf7e875a0 in execve () from /lib/i386-linux-gnu/libc.so.6
#1 0xf7e8799f in execl () from /lib/i386-linux-gnu/libc.so.6
#2 0x565562a4 in main () at simple1.c:15
>>> disassemble
Dump of assembler code for function execve:
=> 0xf7e875a0 <+0>: endbr32
0xf7e875a4 <+4>: push %ebx
0xf7e875a5 <+5>: mov 0x10(%esp),%edx
0xf7e875a9 <+9>: mov 0xc(%esp),%ecx
0xf7e875ad <+13>: mov 0x8(%esp),%ebx
0xf7e875b1 <+17>: mov [=12=]xb,%eax
0xf7e875b6 <+22>: call *%gs:0x10
0xf7e875bd <+29>: pop %ebx
0xf7e875be <+30>: cmp [=12=]xfffff001,%eax
0xf7e875c3 <+35>: jae 0xf7dd9000
0xf7e875c9 <+41>: ret
End of assembler dump.
execve
很特别;它是唯一与 PTRACE_TRACEME
有特殊互动的人。 strace
的工作方式,其他系统调用 do 显示 32 位调用号。 (和现代 strace needs special help to know whether that's a 32-bit call number for int 0x80
/ sysenter
, or a 64-bit call number, since 64-bit processes can still invoke int 0x80
, although PTRACE_GET_SYSCALL_INFO
)
你是对的,当内核被实际调用时,EAX 持有 11
,__NR_execve
来自 unistd_32.h
。它由 mov [=21=]xb,%eax
在 glibc 的 execve 包装器跳转到 VDSO 页面以通过此硬件支持的任何有效方法进入内核之前设置(通常 sysenter
。)
但是执行实际上并没有停止,直到它到达主要 execve
实现中检查 PTRACE_TRACEME
并引发 SIGTRAP
.
显然在此之前的某个时间,它会调用 void set_personality_64bit(void)
in arch/x86/kernel/process_64.c,其中包括
/* Pretend that this comes from a 64bit execve */
task_pt_regs(current)->orig_ax = __NR_execve;
我在内核源代码浏览器中通过 searching for __NR_execve
发现了这一点,并查看了 arch/x86 中最有可能的文件。我没有通过交叉引用来查找调用的位置;它存在的事实(以及合理的非混淆设计的假设)非常强烈地表明这就是你的谜团的答案。