我在使用以下代码玩弄
很快就产生了一个问题:“在32位机和64位机上,libc32 的行为是否有所不同?它们有区别吗?”因此我查看了64位中的 libc32。 然而,libc 中
以下是代码,源自https://www.linuxjournal.com/article/6100。
这是我从32位可执行文件中使用gdb获取到的内容。正如您所看到的,它正在使用/lib/i386-linux-gnu/libc.so.6,并且execve系统调用号为11。
ptrace
,发现即使我使用了 -m32
选项编译,execve
的系统调用号却仍然为59。由于我正在64位机器上使用Ubuntu,这是可以理解的。很快就产生了一个问题:“在32位机和64位机上,libc32 的行为是否有所不同?它们有区别吗?”因此我查看了64位中的 libc32。 然而,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
这是我从32位可执行文件中使用gdb获取到的内容。正如您所看到的,它正在使用/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 $0xb,%eax
0xf7e875b6 <+22>: call *%gs:0x10
0xf7e875bd <+29>: pop %ebx
0xf7e875be <+30>: cmp $0xfffff001,%eax
0xf7e875c3 <+35>: jae 0xf7dd9000
0xf7e875c9 <+41>: ret
End of assembler dump.
sysenter
,而不是较慢的int $0x80
。请参阅https://blog.packagecloud.io/eng/2016/04/05/the-definitive-guide-to-linux-system-calls/以获取概述。 - Peter Cordesmov $0xb,%eax
传递EAX=11。我不知道为什么您的程序在这种情况下找到了EAX=59。我肯定也希望在那里看到11,即来自unistd_32.h
的__NR_execve
,特别是因为我们可以看到libc包装器正在传递它。与Windows WOW64不同,Linux上的32位用户空间直接调用内核,而不是远跳转到64位模式进行系统调用。因此,在64位系统上,32位用户空间库并不特殊;64位内核只是为32位进程提供了32位ABI。 - Peter Cordes