日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux的使用和认识实验,通过一个小实验认识Linux vDSO

發布時間:2023/12/10 linux 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux的使用和认识实验,通过一个小实验认识Linux vDSO 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這里不再解釋vDSO的概念,而直接談其意義:

vDSO類似一個信息公告板,用戶可以直取所需,而無需為此辦理任何手續。

vDSO相當于內核直接暴露出來的一個C庫,作為GLIBC的補充。

類似gettimeofday之類的調用,每次都陷入內核去拿一個時間戳,顯得有點昂貴了,不如內核把時間戳放在一個公共的可以暴露給任何用戶的地方,用戶自己去看就行了,這是vDSO的典型用例。

為了簡單化描述,我們關閉ASLR:

[root@localhost ~]# sysctl -w kernel.randomize_va_space=0

隨便打開一個ping程序,獲取其/proc/pid/smap中vdso的map區間:

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

Size: 8 kB

...

我們將其dd出來:

[root@localhost ~]# dd if=/proc/3688/mem of=./vsdo.dd obs=1 bs=1 skip=140737354113024 count=8192

隨后我們看看它是什么:

[root@localhost ~]# file ./vdso.dd

./vdso.dd: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=09be88363f7ca8b05e2cb54a82d16bec2e840186, stripped

那么,接下來可以objdump了,就像對待普通的動態鏈接庫一樣:

[root@localhost ~]# objdump -T vdso.dd

vdso.dd: 文件格式 elf64-x86-64

DYNAMIC SYMBOL TABLE:

ffffffffff700354 l d .eh_frame_hdr0000000000000000 .eh_frame_hdr

ffffffffff700700 w DF .text000000000000059d LINUX_2.6 clock_gettime

0000000000000000 g DO *ABS*0000000000000000 LINUX_2.6 LINUX_2.6

ffffffffff700ca0 g DF .text00000000000002d5 LINUX_2.6 __vdso_gettimeofday

ffffffffff700fa0 g DF .text000000000000003d LINUX_2.6 __vdso_getcpu

ffffffffff700ca0 w DF .text00000000000002d5 LINUX_2.6 gettimeofday

ffffffffff700f80 w DF .text0000000000000016 LINUX_2.6 time

ffffffffff700fa0 w DF .text000000000000003d LINUX_2.6 getcpu

ffffffffff700700 g DF .text000000000000059d LINUX_2.6 __vdso_clock_gettime

ffffffffff700f80 g DF .text0000000000000016 LINUX_2.6 __vdso_time

看看,看看,里面竟都是些什么東西,竟是一些時間公告函數啊,這意味著如果你想獲取時間,調這里的函數就好了,我們看看最簡單的time系統調用是如何來獲取時間的,下面是對待vdso.dd文件的objdump -D的結果:

ffffffffff700f80 <__vdso_time>:

ffffffffff700f80: 55 push %rbp

ffffffffff700f81: 48 85 ff test %rdi,%rdi

ffffffffff700f84: 48 8b 04 25 a8 f0 5f mov 0xffffffffff5ff0a8,%rax

ffffffffff700f8b: ff

ffffffffff700f8c: 48 89 e5 mov %rsp,%rbp

ffffffffff700f8f: 74 03 je ffffffffff700f94 <__vdso_time>

ffffffffff700f91: 48 89 07 mov %rax,(%rdi)

ffffffffff700f94: 5d pop %rbp

ffffffffff700f95: c3 retq

很顯然,并沒有調用任何系統調用,而是直接從地址0xffffffffff5ff0a8處拿到了時間,那么地址0xffffffffff5ff0a8一定就是內核映射到用戶態的時間公告板的位置了。

記住地址0xffffffffff5ff0a8,用戶態的分析到此告一段落,我們進入內核去看一看。

首先從/proc/kallsyms中查到vdso的位置:

ffffffff81941000 D vdso_start

ffffffff819424b0 D vdso_end

其次我們找到內核時間公告板vsyscall_gtod_data的位置:

ffffffff81a75080 D vsyscall_gtod_data

我們看一下該公告板的值:

crash> struct vsyscall_gtod_data.wall_time_sec ffffffff81a75080

wall_time_sec = 1600912854

crash> struct vsyscall_gtod_data.wall_time_sec ffffffff81a75080

wall_time_sec = 1600912856

crash> struct vsyscall_gtod_data.wall_time_sec ffffffff81a75080

wall_time_sec = 1600912857

顯然,公告板的wall_time_sec字段就是返回給time的值了。下面我們找到它的地址:

crash> struct vsyscall_gtod_data ffffffff81a75080 -o

struct vsyscall_gtod_data {

[ffffffff81a75080] seqcount_t seq;

struct {

int vclock_mode;

cycle_t cycle_last;

cycle_t mask;

u32 mult;

u32 shift;

[ffffffff81a75088] } clock;

[ffffffff81a750a8] time_t wall_time_sec;

[ffffffff81a750b0] u64 wall_time_snsec;

[ffffffff81a750b8] u64 monotonic_time_snsec;

[ffffffff81a750c0] time_t monotonic_time_sec;

[ffffffff81a750c8] struct timezone sys_tz;

[ffffffff81a750d0] struct timespec wall_time_coarse;

[ffffffff81a750e0] struct timespec monotonic_time_coarse;

}

嗯,就是0xffffffff81a750a8了。它就是映射到0xffffffffff5ff0a8暴露給用戶態的那個地址了。

我們接下來證實這一點:

修改掉映射地址,返回給time調用以0.

我們再看公告板:

crash> struct vsyscall_gtod_data ffffffff81a75080

...

sys_tz = {

tz_minuteswest = 0,

tz_dsttime = 0

},

我們把sys_tz映射出去怎樣,這個值是一直為0的,我們期望的就是time返回0.

為此,我們首先拿到sys_tz和wall_time_sec之間的偏移:

crash> eval ffffffff81a750c8-ffffffff81a750a8

hexadecimal: 20

decimal: 32

octal: 40

因此,我們只要把vdso的time函數代碼改掉即可:

ffffffffff700f84: 48 8b 04 25 a8 f0 5f mov 0xffffffffff5ff0a8,%rax

改為:

ffffffffff700f84: 48 8b 04 25 c8 f0 5f mov 0xffffffffff5ff0c8,%rax

即將time函數的第8個字節,0xa8改成0xc8即可:

通過模式匹配,可以拿到time函數在vdso頁面的偏移:

f80: 55 push rbp

f81: 48 85 ff test rdi,rdi

f84: 48 8b 04 25 a8 f0 5f mov rax,QWORD PTR ds:0xffffffffff5ff0a8

f8b: ff

f8c: 48 89 e5 mov rbp,rsp

f8f: 74 03 je 0xf94

f91: 48 89 07 mov QWORD PTR [rdi],rax

f94: 5d pop rbp

f95: c3 ret

即0xf80.

那么0xffffffff81941f80便是time函數其地址了:

unsigned char *addr = (unsigned char *)0xffffffff81941f80;

addr[8] = 0xc8;

在修改之前,我們先編程驗證:

#include

#include

typedef time_t (*time_func)(time_t *);

int main(int argc, char *argv[])

{

time_t tloc;

// 直接從地址拿值

unsigned long *p = (unsigned long *)0xffffffffff5ff0a8;

// 通過函數拿值

time_func func = (time_func)0x7ffff7ffaf80;

func(&tloc);

printf("%ld

", tloc);

printf("%lu

", *p);

}

預期的結果應該是兩種方式獲取的是同一個值:

[root@localhost ~]# ./a.out

1600923922

1600923922

[root@localhost ~]# ./a.out

1600923923

1600923923

[root@localhost ~]#

下面將內核頁面對應的指令修改之:

[root@localhost ~]# cat modtime.stp

#!/usr/local/bin/stap -g

function modtime(val:long)

%{

unsigned char *addr = (unsigned char *)0xffffffff81941f80;

unsigned char c = (unsigned char)STAP_ARG_val;

addr[8] = c;

%}

probe begin

{

modtime($1)

exit()

}

執行之:

[root@localhost ~]# ./modtime.stp 0xc8

[root@localhost ~]# ./a.out

0

1600924228

[root@localhost ~]# ./a.out

0

1600924229

[root@localhost ~]# ./modtime.stp 0xa8

[root@localhost ~]# ./a.out

1600924238

1600924238

[root@localhost ~]#

當修改了vdso頁面的指令后,所有調用time的進程都將異常,這是很顯然的:

top - 08:00:00 up 42 min, 3 users, load average: 0.00, 0.00, 0.00

Tasks: 114 total, 1 running, 113 sleeping, 0 stopped, 0 zombie

%Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st

KiB Mem : 0 total, 0 free, 0 used, 0 buff/cache

KiB Swap: 0 total, 0 free, 0 used. 0 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND

1 root 20 0 51696 3808 2492 S 0.0 inf 0:01.29 systemd

2 root 20 0 0 0 0 S 0.0 -nan 0:00.00 kthreadd

3 root 20 0 0 0 0 S 0.0 -nan 0:00.00 ksoftirqd/0

7 root rt 0 0 0 0 S 0.0 -nan 0:00.01 migration/0

8 root 20 0 0 0 0 S 0.0 -nan 0:00.00 rcu_bh

9 root 20 0 0 0 0 S 0.0 -nan 0:00.00 rcuob/0

10 root 20 0 0 0 0 S 0.0 -nan 0:00.00 rcuob/1

值得一提的是,在vdso之前,vsyscall機制也是類似,只是說它僅僅提供了一種map,而沒有抽象出動態鏈接的含義,因此也就無法享受ASLR帶來的安全保護了。

浙江溫州皮鞋濕,下雨進水不會胖。

總結

以上是生活随笔為你收集整理的Linux的使用和认识实验,通过一个小实验认识Linux vDSO的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。