VDSO与vsyscall

概述

vsyscall和vDSO是用于加速某些系统调用的两种机制。

 

传统的int 0x80有点慢, Intel和AMD分别实现了sysenter/sysexit和syscall/ sysret, 即所谓的快速系统调用指令, 使用它们更快, 但是也带来了兼容性的问题. 于是Linux实现了vsyscall, 程序统一调用vsyscall, 具体的选择由内核来决定. 而vsyscall的实现就在VDSO中。

 

【vsyscall】

用来执行特定的系统调用,减少系统调用的开销。某些系统调用并不会向内核提交参数,而仅仅只是从内核里请求读取某个数据,例如gettimeofday(),内核在处理这部分系统调用时可以把系统当前时间写在一个固定的位置(由内核在每个时间中断里去完成这个更新动作),mmap映射到用户空间。这样会更快速,避免了传统系统调用模式INT 0x80/SYSCALL造成的内核空间和用户空间的上下文切换。

 

【vsyscall的局限】

分配的内存较小;

只允许4个系统调用;

Vsyscall页面在每个进程中是静态分配了相同的地址;

 

【vDSO】

提供和vsyscall相同的功能,同时解决了其局限。

vDSO是动态分配的,地址是随机的;

可以提供超过4个系统调用;

vDSO是glibc库提供的功能;

X64

测试环境Ubuntu 16.04

2.1 查看vsyscall/VDSO内存映射

millionsky@ubuntu-16:~/tmp/VDSO$ cat /proc/`pgrep cat | head -1`/maps | egrep 'vdso|vsyscall'

7ffdb57da000-7ffdb57dc000 r-xp 00000000 00:00 0                          [vdso]

ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

2.2 导出VDSO/vsyscall

2.2.1 Gdb导出vdso/vsyscall

millionsky@ubuntu-16:~/tmp/VDSO$ gdb -q /bin/ls

Reading symbols from /bin/ls...(no debugging symbols found)...done.

(gdb) tb __open

Function "__open" not defined.

Make breakpoint pending on future shared library load? (y or [n]) y

Temporary breakpoint 1 (__open) pending.

(gdb) r

Starting program: /bin/ls

 

Temporary breakpoint 1, open64 () at ../sysdeps/unix/syscall-template.S:84

84      ../sysdeps/unix/syscall-template.S: 没有那个文件或目录.

(gdb) info program

        Using the running image of child process 12012.

Program stopped at 0x7ffff7df2320.

It stopped at a breakpoint that has since been deleted.

Type "info stack" or "info registers" for more information.

(gdb) shell cat /proc/12012/maps | grep vdso

7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0                          [vdso]

(gdb) dump memory /tmp/linux-vdso.so.1 0x7ffff7ffa000 0x7ffff7ffc000

(gdb) q

 

同样的方法可以导出vsyscall

2.2.2 python导出vdso

#!/usr/bin/env python
from ctypes import *

for ln in open( '/proc/self/maps'):
if "[vdso]" in ln:
start, end = [ int(x, 16) for x in ln.split()[ 0].split( '-')]
CDLL( "libc.so.6").write( 1, c_void_p(start), end-start)
break

这里是Python调用的C库函数

2.2.3 C导出vdso

/* extract_vdso.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{
char buf[256], *mem;
const char *range_name;
FILE *rd, *wr;
long long start_addr, end_addr;

/* Open file for writing the vdso data to */
if (argc != 3)
{
fprintf(stderr,
"Usage: %s <file> <string>\n"
"\t<file>: File to write the vdso data to.\n"
"\t<string>: Name of the mapped in region, e.g. vdso\n",
argv[0]);

abort();
}

range_name = argv[2];

if (!(wr = fopen(argv[1], "w")))
{
perror("Error: fopen() - output file");
abort();
}

/* Get this process' memory layout */
if (!(rd = fopen("/proc/self/maps", "r")))
{
perror("Error: fopen() - /proc/self/maps");
abort();
}

/* Find the line in /proc/self/maps that contains
the substring [vdso] * */
while (fgets(buf, sizeof(buf), rd))
{
if (strstr(buf, range_name))
break;
}

fclose(rd);

/* Locate the end memory range for [vdso] */
end_addr = strtoll((strchr(buf, '-') + 1), NULL, 16);

/* Terminate the string so we can get the start
address really easily * */
*(strchr(buf, '-')) = '\0';
start_addr = strtoll(buf, NULL, 16);

fwrite((void*)start_addr, 1, end_addr-start_addr, wr);
fclose(wr);

printf("Start: %p\nEnd: %p\nBytes: %d\n",
(void *)start_addr, (void *)end_addr, (int)(end_addr -
start_addr));

return 0;
}

2.3 VDSO内容

VDSO中包含以下4个系统调用:

/usr/include/x86_64-linux-gnu/asm/unistd_64.h

#define __NR_gettimeofday 96 //0x60

#define __NR_time 201 //0xc9

#define __NR_clock_gettime 228 //0xE4

#define __NR_getcpu 309 //0x135

 

IDA查看

 

 

objdump查看

millionsky@ubuntu-16:~/tmp/VDSO$ objdump -d /tmp/linux-vdso.so.1 | grep -A5 ">:"        

0000000000000930 <__vdso_clock_gettime@@LINUX_2.6-0xc0>:

 930:   55                      push   %rbp

 931:   48 89 e5                mov    %rsp,%rbp

 934:   0f ae e8                lfence

 937:   0f 31                   rdtsc  

 939:   48 c1 e2 20             shl    $0x20,%rdx

--

00000000000009f0 <__vdso_clock_gettime@@LINUX_2.6>:

 9f0:   55                      push   %rbp

 9f1:   41 89 f9                mov    %edi,%r9d

 9f4:   48 89 e5                mov    %rsp,%rbp

 9f7:   53                      push   %rbx

 9f8:   48 8d 5d f4             lea    -0xc(%rbp),%rbx

--

0000000000000c30 <__vdso_gettimeofday@@LINUX_2.6>:

 c30:   55                      push   %rbp

 c31:   48 89 e5                mov    %rsp,%rbp

 c34:   53                      push   %rbx

 c35:   48 83 ec 10             sub    $0x10,%rsp

 c39:   48 85 ff                test   %rdi,%rdi

--

0000000000000d80 <__vdso_time@@LINUX_2.6>:

 d80:   55                      push   %rbp

 d81:   48 85 ff                test   %rdi,%rdi

 d84:   48 8b 05 1d d3 ff ff    mov    -0x2ce3(%rip),%rax        # ffffffffffffe0a8 <__vdso_getcpu@@LINUX_2.6+0xffffffffffffd308>

 d8b:   48 89 e5                mov    %rsp,%rbp

 d8e:   74 03                   je     d93 <__vdso_time@@LINUX_2.6+0x13>

--

0000000000000da0 <__vdso_getcpu@@LINUX_2.6>:

 da0:   55                      push   %rbp

 da1:   b8 7b 00 00 00          mov    $0x7b,%eax

 da6:   48 89 e5                mov    %rsp,%rbp

 da9:   0f 03 c0                lsl    %ax,%eax

 dac:   90                      nop

2.4 Vsyscall内容

vsyscall中包含3个系统调用:

/usr/include/x86_64-linux-gnu/asm/unistd_64.h

#define __NR_gettimeofday 96 //0x60

#define __NR_time 201 //0xc9

#define __NR_clock_gettime 228//0xE4

#define __NR_getcpu 309 //0x135

 

地址是固定的:

基地址:0xffffffffff600000

#define VSYSCALL_ADDR_vgettimeofday   0xffffffffff600000

#define VSYSCALL_ADDR_vtime           0xffffffffff600400

#define VSYSCALL_ADDR_vgetcpu          0xffffffffff600800

 

 

IDA查看内容

 

 

Objdump查看内容

millionsky@ubuntu-16:~/tmp/VDSO$ objdump -D -b binary -mi386:x86-64 /tmp/linux-vsyscall.so  | grep syscall -C 2

 

/tmp/linux-vsyscall.so:     文件格式 binary

 

 

--

0000000000000000 <.data>:

       0:       48 c7 c0 60 00 00 00    mov    $0x60,%rax

       7:       0f 05                   syscall

       9:       c3                      retq   

       a:       cc                      int3   

--

     3ff:       cc                      int3   

     400:       48 c7 c0 c9 00 00 00    mov    $0xc9,%rax

     407:       0f 05                   syscall

     409:       c3                      retq   

     40a:       cc                      int3   

--

     7ff:       cc                      int3   

     800:       48 c7 c0 35 01 00 00    mov    $0x135,%rax

     807:       0f 05                   syscall

     809:       c3                      retq   

     80a:       cc                      int3   

X86

测试环境Ubuntu 12.04

 

linux-vdso.so.1

老点的版本是linux-gate.so.1

3.1 查看VDSO内存映射

millionsky@ubuntu-12:~/tmp$ ldd /bin/cat

        linux-gate.so.1 =>  (0xb776a000)

        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75ad000)

        /lib/ld-linux.so.2 (0xb776b000)

millionsky@ubuntu-12:~/tmp$ cat /proc/self/maps | grep vdso

b77c1000-b77c2000 r-xp 00000000 00:00 0          [vdso]

3.2 导出VDSO

见X64章节

3.3 VDSO内容

Objdump查看

millionsky@ubuntu-12:~/tmp$ objdump -d linux-gate.so.1

 

linux-gate.so.1:     file format elf32-i386

 

 

Disassembly of section .text:

 

ffffe400 <__kernel_sigreturn>:

ffffe400:       58                      pop    %eax

ffffe401:       b8 77 00 00 00          mov    $0x77,%eax

ffffe406:       cd 80                   int    $0x80

ffffe408:       90                      nop

ffffe409:       8d 76 00                lea    0x0(%esi),%esi

 

ffffe40c <__kernel_rt_sigreturn>:

ffffe40c:       b8 ad 00 00 00          mov    $0xad,%eax

ffffe411:       cd 80                   int    $0x80

ffffe413:       90                      nop

 

ffffe414 <__kernel_vsyscall>:

ffffe414:       51                      push   %ecx

ffffe415:       52                      push   %edx

ffffe416:       55                      push   %ebp

ffffe417:       89 e5                   mov    %esp,%ebp

ffffe419:       0f 34                   sysenter

ffffe41b:       90                      nop

ffffe41c:       90                      nop

ffffe41d:       90                      nop

ffffe41e:       90                      nop

ffffe41f:       90                      nop

ffffe420:       90                      nop

ffffe421:       90                      nop

ffffe422:       cd 80                   int    $0x80

ffffe424:       5d                      pop    %ebp

ffffe425:       5a                      pop    %edx

ffffe426:       59                      pop    %ecx

ffffe427:       c3                      ret    

附加向量

1. 附加向量位于环境指针数组的后面,环境字符串的前面;

2. 附加向量中的AT_SYSINFO_EHDR指示VDSO的地址;

3. 附加向量中的AT_SYSINFO指示__kernel_vsyscall的地址(32位系统);

4. 附加向量中的AT_RANDOM指示栈中16字节随机数的地址。在栈中位于附加向量的后面,环境字符串的前面。可以通过这个地址计算栈中数据的地址。

5. AT_PLATFORM指示平台字符串,可以通过这个地址计算栈中数据的地址。

VDSO的爆破

【32位】

https://www.anquanke.com/post/id/85810

Sigreturn Oriented Programming攻击简介

这篇文章描述了32位vdso的爆破

 

32位的VDSO只有1个字节是随机的

vdso_range = range(0xf7700000, 0xf7800000, 0x1000)

 

在Ubuntu 16.04 64上测试32位程序失败(已经调整了/bin/sh,bss,sigreturn_offset,syscall_offset)

l 失败的原因在于main函数的栈溢出不好搞。Main开始对ESP进行了取整,EBP-4的地方存储的是addr_of_argc。Main的结尾,ESP是通过EBP-4处保存的addr_of_argc进行恢复的;

l 将漏洞代码放入单独的函数中时,爆破成功

[+] Starting local process './srop_test': pid 25791

vdso_addr: 0xf777b000

[*] Process './srop_test' stopped with exit code -11 (SIGSEGV) (pid 25791)

nTry 169

[+] Starting local process './srop_test': pid 25794

vdso_addr: 0xf77a7000

[*] Switching to interactive mode

 

$ whoami

millionsky

$ ls

core  makefile    srop3.py  srop3_single_test.py    srop_test  srop_test.c

$ exit

[*] Got EOF while reading in interactive

 

 

【64位】

但是对x64来说,爆破vdso就比较难了。原来只有11bit是随记的,但我在我的linux上测试好像有22位是随机的了,爆破也就几小时而已(个人亲测),还是能爆出来的。关于64位的爆破,可参考Return to VDSO using ELF Auxiliary Vectors

尚未测试

参考文章

1. On vsyscalls and the vDSO。https://lwn.net/Articles/446528/

2. 64位Linux下的系统调用。http://www.lenky.info/archives/2013/02/2199。

3. vsyscalls and vDSO。https://0xax.gitbooks.io/linux-insides/content/SysCall/syscall-3.html。

4. Creating a vDSO: the Colonel's Other Chicken。https://www.linuxjournal.com/content/creating-vdso-colonels-other-chicken?page=0,2。

5. Return to VDSO using ELF Auxiliary Vectors。https://v0ids3curity.blogspot.jp/2014/12/return-to-vdso-using-elf-auxiliary.html。

6.Sigreturn Oriented Programming攻击简介。https://www.anquanke.com/post/id/85810




 

 

猜你喜欢

转载自blog.csdn.net/luozhaotian/article/details/79609077