Linux系统调用
2018-03-19 13:19
127 查看
1 概述
相比Intel支持的快速系统调用指令sysenter/sysexit,AMD对应的是syscall/sysret,不过现在,Intel也兼容这两条指令。
测试环境:
Ubuntu 12.04
Ubuntu 16.04 64
系统调用号和返回结果
EAX指定要调用的函数(系统调用号)
EBX传递函数的第一个参数
ECX传递函数的第二个参数
EDX传递函数的第三个参数
返回值EAX
示例
#include <stdio.h>#include <sys/types.h>#include <unistd.h>#include <sys/syscall.h>
#define STRINGFY_(x) #x#define STRINGFY(x) STRINGFY_(x)
int main(){ pid_t pid;
asm volatile("movl $"STRINGFY(__NR_getpid)", %%eax\n" "int $0x80\n" : "=a"(pid)); printf("pid=%u\n", pid);
return 0;}
sys/syscall.h
/usr/include/i386-linux-gnu/asm/unistd_32.h
如:
#define __NR_getuid 24
#define __NR_getuid32 199
参数:
EAX指定要调用的函数(系统调用号)
EBX传递函数的第一个参数
ECX传递函数的第二个参数
EDX传递函数的第三个参数
ESI
EDI
EBP
返回值EAX
静态链接时,采用"call *_dl_sysinfo"指令;
动态链接时,采用"call *%gs:0x10"指令;
最终调用的是VDSO(linux-gate.so.1)中的__kernel_vsyscall函数;
__kernel_vsyscall函数包含sysenter指令;
syscall()函数也是类似的,根据静态/动态链接的不同分别采用的不同的指令,最终调用__kernel_vsyscall函数;
因此需要注意以下几点:
保存返回地址(sysenter指令的下一条指令)
保存ecx、edx寄存器(sysexit指令需要使用这两个寄存器);
asm("push %ecx\n");
asm("push %edx\n");
增加函数头
asm("push %ebp\n");
asm("mov %esp,%ebp\n");
#include <stdio.h>#include <sys/types.h>#include <unistd.h>#include <sys/syscall.h>
#define STRINGFY_(x) #x#define STRINGFY(x) STRINGFY_(x)
int main(){ pid_t pid;
//simulate __kernel_vsyscall asm("jmp label2\n"); asm("label1:\n");
asm("push %ecx\n"); asm("push %edx\n"); asm("push %ebp\n"); asm("mov %esp,%ebp\n");
asm("movl $"STRINGFY(__NR_getpid)", %eax\n"); asm("sysenter\n"); asm("label2:"); asm("call label1\n"); asm("": "=a"(pid)); printf("pid=%u\n", pid);
return 0;}
#define STRINGFY_(x) #x#define STRINGFY(x) STRINGFY_(x)
int main(){ pid_t pid;
asm("movl $"STRINGFY(__NR_getpid)", %eax\n"); asm("call *%gs:0x10\n"); asm("": "=a"(pid)); printf("pid=%u\n", pid);
return 0;}
sys/syscall.h
/usr/include/x86_64-linux-gnu/asm/unistd_64.h
参数(man syscall):
参数寄存器:
静态链接时,直接调用syscall指令;
动态链接时,调用libc的系统调用代码,调用syscall指令;
syscall()函数也是类似的,最终调用syscall指令;
GDB调试
值0x66是__NR_getuid在x64上的值,即把__NR_getuid放到eax寄存器后,不再是执行指令int 0x80,而是执行指令syscall。
GDB调试
传统的32位系统调用int 0x80
Intel的sysenter/sysexit
AMD的syscall/sysret
2. 传统Int 0x80系统调用
系统调用号:EAX
参数:EBX、ECX、EDX、ESI、EDI、EBP
返回值:EAX
3. 32位系统调用sysenter
系统调用号:EAX
参数:EBX、ECX、EDX、ESI、EDI、EBP
返回值:EAX
静态链接时,采用"call *_dl_sysinfo"指令;
动态链接时,采用"call *%gs:0x10"指令;
最终调用的是VDSO(linux-gate.so.1)中的__kernel_vsyscall函数;
__kernel_vsyscall函数包含sysenter指令;
syscall()函数也是类似的,根据静态/动态链接的不同分别采用的不同的指令,最终调用__kernel_vsyscall函数;
4. 64位系统调用syscall
系统调用号:RAX
参数:RDI、RSI、RDX、R10、R8、R9
返回值:RAX
静态链接时,直接调用syscall指令;
动态链接时,调用libc的系统调用代码,调用syscall指令;
syscall()函数也是类似的,最终调用syscall指令;
2. Linux 2.6 对新型 CPU 快速系统调用的支持。https://www.ibm.com/developerworks/cn/linux/kernel/l-k26ncpu/index.html。
相比Intel支持的快速系统调用指令sysenter/sysexit,AMD对应的是syscall/sysret,不过现在,Intel也兼容这两条指令。
测试环境:
Ubuntu 12.04
Ubuntu 16.04 64
2 传统系统调用int 0x80
只用于32位系统,64位系统上不起作用;系统调用号和返回结果
EAX指定要调用的函数(系统调用号)
EBX传递函数的第一个参数
ECX传递函数的第二个参数
EDX传递函数的第三个参数
返回值EAX
示例
#include <stdio.h>#include <sys/types.h>#include <unistd.h>#include <sys/syscall.h>
#define STRINGFY_(x) #x#define STRINGFY(x) STRINGFY_(x)
int main(){ pid_t pid;
asm volatile("movl $"STRINGFY(__NR_getpid)", %%eax\n" "int $0x80\n" : "=a"(pid)); printf("pid=%u\n", pid);
return 0;}
3 32位系统调用sysenter
3.1 系统调用
系统调用号:sys/syscall.h
/usr/include/i386-linux-gnu/asm/unistd_32.h
如:
#define __NR_getuid 24
#define __NR_getuid32 199
参数:
EAX指定要调用的函数(系统调用号)
EBX传递函数的第一个参数
ECX传递函数的第二个参数
EDX传递函数的第三个参数
ESI
EDI
EBP
返回值EAX
静态链接时,采用"call *_dl_sysinfo"指令;
动态链接时,采用"call *%gs:0x10"指令;
最终调用的是VDSO(linux-gate.so.1)中的__kernel_vsyscall函数;
__kernel_vsyscall函数包含sysenter指令;
syscall()函数也是类似的,根据静态/动态链接的不同分别采用的不同的指令,最终调用__kernel_vsyscall函数;
3.2 静态链接实例
millionsky@ubuntu-12:~/tmp$ gcc getuid_glibc.c -static millionsky@ubuntu-12:~/tmp$ gdb ./a.out (gdb) b main Breakpoint 1 at 0x8048ee3 (gdb) r Starting program: /home/millionsky/tmp/a.out Breakpoint 1, 0x08048ee3 in main () (gdb) disass Dump of assembler code for function main: 0x08048ee0 <+0>: push %ebp 0x08048ee1 <+1>: mov %esp,%ebp => 0x08048ee3 <+3>: and $0xfffffff0,%esp 0x08048ee6 <+6>: sub $0x10,%esp 0x08048ee9 <+9>: call 0x8053c00 <getuid> 4000 0x08048eee <+14>: mov $0x80c6088,%edx 0x08048ef3 <+19>: mov %eax,0x4(%esp) 0x08048ef7 <+23>: mov %edx,(%esp) 0x08048efa <+26>: call 0x8049980 <printf> 0x08048eff <+31>: mov $0x0,%eax 0x08048f04 <+36>: leave 0x08048f05 <+37>: ret End of assembler dump. (gdb) disass 0x8053c00 Dump of assembler code for function getuid: 0x08053c00 <+0>: mov $0xc7,%eax //__NR_getuid32 0x08053c05 <+5>: call *0x80ef5a4 0x08053c0b <+11>: ret End of assembler dump. (gdb) x 0x80ef5a4 0x80ef5a4 <_dl_sysinfo>: 0xb7fff414 (gdb) disass 0xb7fff414 Dump of assembler code for function __kernel_vsyscall: 0xb7fff414 <+0>: push %ecx 0xb7fff415 <+1>: push %edx 0xb7fff416 <+2>: push %ebp 0xb7fff417 <+3>: mov %esp,%ebp 0xb7fff419 <+5>: sysenter 0xb7fff41b <+7>: nop 0xb7fff41c <+8>: nop 0xb7fff41d <+9>: nop 0xb7fff41e <+10>: nop 0xb7fff41f <+11>: nop 0xb7fff420 <+12>: nop 0xb7fff421 <+13>: nop 0xb7fff422 <+14>: int $0x80 0xb7fff424 <+16>: pop %ebp 0xb7fff425 <+17>: pop %edx 0xb7fff426 <+18>: pop %ecx 0xb7fff427 <+19>: ret End of assembler dump. |
3.3 动态链接实例
millionsky@ubuntu-12:~/tmp$ gcc getuid_glibc.c millionsky@ubuntu-12:~/tmp$ gdb ./a.out (gdb) b main Breakpoint 1 at 0x8048417 (gdb) r Starting program: /home/millionsky/tmp/a.out Breakpoint 1, 0x08048417 in main () (gdb) b getuid Breakpoint 2 at 0xb7ed9c30 (gdb) c Continuing. Breakpoint 2, 0xb7ed9c30 in getuid () from /lib/i386-linux-gnu/libc.so.6 (gdb) disass Dump of assembler code for function getuid: => 0xb7ed9c30 <+0>: mov $0xc7,%eax 0xb7ed9c35 <+5>: call *%gs:0x10 0xb7ed9c3c <+12>: ret End of assembler dump. (gdb) si 0xb7ed9c35 in getuid () from /lib/i386-linux-gnu/libc.so.6 (gdb) 0xb7fdd414 in __kernel_vsyscall () (gdb) disass Dump of assembler code for function __kernel_vsyscall: => 0xb7fdd414 <+0>: push %ecx 0xb7fdd415 <+1>: push %edx 0xb7fdd416 <+2>: push %ebp 0xb7fdd417 <+3>: mov %esp,%ebp 0xb7fdd419 <+5>: sysenter 0xb7fdd41b <+7>: nop 0xb7fdd41c <+8>: nop 0xb7fdd41d <+9>: nop 0xb7fdd41e <+10>: nop 0xb7fdd41f <+11>: nop 0xb7fdd420 <+12>: nop 0xb7fdd421 <+13>: nop 0xb7fdd422 <+14>: int $0x80 0xb7fdd424 <+16>: pop %ebp 0xb7fdd425 <+17>: pop %edx 0xb7fdd426 <+18>: pop %ecx 0xb7fdd427 <+19>: ret End of assembler dump. |
3.4 汇编使用
3.4.1 模拟__kernel_vsyscall
直接执行sysenter指令,执行完成后,内核sysexit会跳转到__kernel_vsyscall的后半部分继续执行:Dump of assembler code for function __kernel_vsyscall: 0xb7fff414 <+0>: push %ecx 0xb7fff415 <+1>: push %edx 0xb7fff416 <+2>: push %ebp 0xb7fff417 <+3>: mov %esp,%ebp 0xb7fff419 <+5>: sysenter 0xb7fff41b <+7>: nop 0xb7fff41c <+8>: nop 0xb7fff41d <+9>: nop 0xb7fff41e <+10>: nop 0xb7fff41f <+11>: nop 0xb7fff420 <+12>: nop 0xb7fff421 <+13>: nop 0xb7fff422 <+14>: int $0x80 0xb7fff424 <+16>: pop %ebp //跳转到这里 0xb7fff425 <+17>: pop %edx 0xb7fff426 <+18>: pop %ecx 0xb7fff427 <+19>: ret |
保存返回地址(sysenter指令的下一条指令)
保存ecx、edx寄存器(sysexit指令需要使用这两个寄存器);
asm("push %ecx\n");
asm("push %edx\n");
增加函数头
asm("push %ebp\n");
asm("mov %esp,%ebp\n");
#include <stdio.h>#include <sys/types.h>#include <unistd.h>#include <sys/syscall.h>
#define STRINGFY_(x) #x#define STRINGFY(x) STRINGFY_(x)
int main(){ pid_t pid;
//simulate __kernel_vsyscall asm("jmp label2\n"); asm("label1:\n");
asm("push %ecx\n"); asm("push %edx\n"); asm("push %ebp\n"); asm("mov %esp,%ebp\n");
asm("movl $"STRINGFY(__NR_getpid)", %eax\n"); asm("sysenter\n"); asm("label2:"); asm("call label1\n"); asm("": "=a"(pid)); printf("pid=%u\n", pid);
return 0;}
3.4.2调用__kernel_vsyscall
#include <stdio.h>#include <sys/types.h>#include <unistd.h>#include <sys/syscall.h>#define STRINGFY_(x) #x#define STRINGFY(x) STRINGFY_(x)
int main(){ pid_t pid;
asm("movl $"STRINGFY(__NR_getpid)", %eax\n"); asm("call *%gs:0x10\n"); asm("": "=a"(pid)); printf("pid=%u\n", pid);
return 0;}
4 64位系统调用syscall
4.1 系统调用
系统调用号:sys/syscall.h
/usr/include/x86_64-linux-gnu/asm/unistd_64.h
参数(man syscall):
参数寄存器:
静态链接时,直接调用syscall指令;
动态链接时,调用libc的系统调用代码,调用syscall指令;
syscall()函数也是类似的,最终调用syscall指令;
4.2 静态链接实例
millionsky@ubuntu-16:~/tmp/VDSO$ cat getuid_glibc.c /** * filename: getuid_glibc.c */ #include <stdio.h> #include <unistd.h> #include <sys/types.h> int main(int argc, char *argv[]) { printf("uid:%d\n", getuid()); return 0; } millionsky@ubuntu-16:~/tmp/VDSO$ gcc getuid_glibc.c -o getuid_glibc -static |
GDB调试
millionsky@ubuntu-16:~/tmp/VDSO$ gdb ./getuid_glibc -q Reading symbols from ./getuid_glibc...(no debugging symbols found)...done. (gdb) tb __getuid Temporary breakpoint 1 at 0x43e8e0 (gdb) r Starting program: /home/millionsky/tmp/VDSO/getuid_glibc Temporary breakpoint 1, 0x000000000043e8e0 in getuid () (gdb) disass Dump of assembler code for function getuid: => 0x000000000043e8e0 <+0>: mov $0x66,%eax 0x000000000043e8e5 <+5>: syscall 0x000000000043e8e7 <+7>: retq End of assembler dump. |
4.3 动态链接实例
millionsky@ubuntu-16:~/tmp/SROP$ gcc getuid_glibc.c millionsky@ubuntu-16:~/tmp/SROP$ gdb ./a.out (gdb) b main Breakpoint 1 at 0x40056a (gdb) r Starting program: /home/millionsky/tmp/SROP/a.out Breakpoint 1, 0x000000000040056a in main () (gdb) b __getuid Breakpoint 2 at 0x7ffff7ada240: file ../sysdeps/unix/syscall-template.S, line 65. (gdb) c Continuing. Breakpoint 2, getuid () at ../sysdeps/unix/syscall-template.S:65 65 ../sysdeps/unix/syscall-template.S: 没有那个文件或目录. (gdb) disass Dump of assembler code for function getuid: => 0x00007ffff7ada240 <+0>: mov $0x66,%eax 0x00007ffff7ada245 <+5>: syscall 0x00007ffff7ada247 <+7>: retq End of assembler dump. |
4.4 syscall函数实例
millionsky@ubuntu-16:~/tmp/VDSO$ cat getuid_syscall.c /** * filename: getuid_syscall.c */ #include <stdio.h> #define _GNU_SOURCE #include <unistd.h> #include <sys/syscall.h> int main(int argc, char *argv[]) { printf("uid:%ld\n", syscall(__NR_getuid)); return 0; } millionsky@ubuntu-16:~/tmp/VDSO$ gcc getuid_syscall.c -o getuid_syscall -static |
GDB调试
millionsky@ubuntu-16:~/tmp/VDSO$ gdb ./getuid_syscall -q Reading symbols from ./getuid_syscall...(no debugging symbols found)...done. (gdb) b main Breakpoint 1 at 0x4009b2 (gdb) r Starting program: /home/millionsky/tmp/VDSO/getuid_syscall Breakpoint 1, 0x00000000004009b2 in main () (gdb) disass Dump of assembler code for function main: ... 0x00000000004009bd <+15>: mov $0x66,%edi 0x00000000004009c2 <+20>: mov $0x0,%eax 0x00000000004009c7 <+25>: callq 0x43fc70 <syscall> ... End of assembler dump. (gdb) disass 0x43fc70 Dump of assembler code for function syscall: 0x000000000043fc70 <+0>: mov %rdi,%rax 0x000000000043fc73 <+3>: mov %rsi,%rdi 0x000000000043fc76 <+6>: mov %rdx,%rsi 0x000000000043fc79 <+9>: mov %rcx,%rdx 0x000000000043fc7c <+12>: mov %r8,%r10 0x000000000043fc7f <+15>: mov %r9,%r8 0x000000000043fc82 <+18>: mov 0x8(%rsp),%r9 0x000000000043fc87 <+23>: syscall ... End of assembler dump. |
5 结论
1. 系统调用指令:传统的32位系统调用int 0x80
Intel的sysenter/sysexit
AMD的syscall/sysret
2. 传统Int 0x80系统调用
系统调用号:EAX
参数:EBX、ECX、EDX、ESI、EDI、EBP
返回值:EAX
3. 32位系统调用sysenter
系统调用号:EAX
参数:EBX、ECX、EDX、ESI、EDI、EBP
返回值:EAX
静态链接时,采用"call *_dl_sysinfo"指令;
动态链接时,采用"call *%gs:0x10"指令;
最终调用的是VDSO(linux-gate.so.1)中的__kernel_vsyscall函数;
__kernel_vsyscall函数包含sysenter指令;
syscall()函数也是类似的,根据静态/动态链接的不同分别采用的不同的指令,最终调用__kernel_vsyscall函数;
4. 64位系统调用syscall
系统调用号:RAX
参数:RDI、RSI、RDX、R10、R8、R9
返回值:RAX
静态链接时,直接调用syscall指令;
动态链接时,调用libc的系统调用代码,调用syscall指令;
syscall()函数也是类似的,最终调用syscall指令;
7 参考文章
1. 64位Linux下的系统调用。http://www.lenky.info/archives/2013/02/2199。2. Linux 2.6 对新型 CPU 快速系统调用的支持。https://www.ibm.com/developerworks/cn/linux/kernel/l-k26ncpu/index.html。
相关文章推荐
- Android 6.0 JNI原理分析 和 Linux系统调用(syscall)原理
- linux文件的系统调用
- Linux系统调用列表
- Linux文件编程之【系统调用】——write()
- linux下64位汇编的系统调用(1)
- linux下64位汇编的系统调用(2)
- Linux系统调用的数量变化
- 在 Linux 下用户空间与内核空间数据交换的方式,第 1 部分: 内核启动参数、模块参数与sysfs、sysctl、系统调用和netlink
- linux下64位汇编的系统调用(5)
- 走马观花: Linux 系统调用 open 七日游(六)
- Linux内核分析4:扒开系统调用三层皮
- Linux系统调用--msgsnd/msgrcv函数详解
- linux中waitpid系统调用
- Linux系统调用--getrusage函数详解
- 常见linux系统调用
- Linux系统调用--fcntl函数详解
- linux下的系统调用函数到内核函数的追踪
- 常用的Linux系统调用命令
- Java调用linux系统shell执行命令
- unix/linux中的dup()系统调用 --对上篇dup() dup2()例子的解释