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带来的安全保护了。

浙江温州皮鞋湿,下雨进水不会胖。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/422027.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux 漏洞数量,Debian Linux被列为过去20年漏洞数量最多的操作系统

1999 至 2019 年间&#xff0c;研究人员共发现了 Debian Linux 中的 3067 个安全漏洞。至于 Windows 平台&#xff0c;Server 2008 以 1421 个安全漏洞位列第一。Android 和 Linux 内核分别以 2563 和 2357 个漏洞排名第二和第三&#xff0c;macOS 以 2212 个漏洞排名第四。然而…

请问这博客能有几种方便写法?

好像这博客园不如网易写博客方便&#xff01; 网易博客支持一键写博客&#xff0c;这点能方便我收集网络资源&#xff0c;以便后续查阅和梳理。 网易支持word写博客&#xff0c;图文并茂。 支持邮件写博客等等&#xff0c;都相当方便&#xff01;&#xff01; 转载于:https://w…

监测ASP.NET MVC 网站

使用MiniProfiler调试ASP.NET MVC网站性能&#xff0c;MiniProfiler可以很好的处理网站后端每个处理时间的事件&#xff0c;但是MiniProfiler是无法远程做监测的动作&#xff0c;MiniProfiler只能够监测本地端的动作&#xff0c;所以MiniProfier比较适合开发期间使用。 在开发A…

Win10 安装 MongoDB 3.6.5 失败的问题及解决方法

MongoDB 3.6.5 2008R2Plus SSL (64 bit) Setup Wizard ended prematurely 在安装 MongoDB 的时候&#xff0c;出现了MongoDB 3.6.5 2008R2Plus SSL (64 bit) Setup Wizard ended prematurely的错误&#xff0c;原因不明&#xff0c;但有解决办法&#xff1a; 解决办法 在安装…

c语言判断字符配对,【C语言】判断花括号{}是否匹配

标签&#xff1a;c#define _CRT_SECURE_NO_WARNINGS#include#includeint main(){int i 0, a 0;int j 0, b 0;int count 0;char arr1[50];char arr2[20];printf("请输入源代码:\n");scanf("%s", arr1);for (i 0; i {if (arr1[i] ‘{‘ || arr1[i] ‘…

字符串格式化

string的ljust()、rjust()、center()用来对齐 >>> for x in range(1, 11): ... print(repr(x).rjust(2), repr(x*x).rjust(3), end ) ... # Note use of end on previous line ... print(repr(x*x*x).rjust(4)) ...1 1 12 4 83 9 274 16 …

Closure

又叫static scope&#xff0c;或直观点叫bag。一个袋子&#xff0c;里面的环境等等都在这个袋子里。(define bank-account (let ((balance 10)) (lambda (n) (set! balance ( balance n)) balance)))(bank-account 20) ; donating 20 dollars ;Value: 30(ban…

c语言如何开发应用程序,怎样用c语言编写软件?如许多小的程序。

面向过程编程的一般思路&#xff1a;分析问题->设计流程和模块->编码和测试->改进并总结做算法问题的一般思路&#xff1a;先用数学方法计算分析&#xff0c;充分理解问题。在思考程序哪些地方应该用循环、指针或数组等细节问题之前&#xff0c;先要确定程序的大体框架…

halcon学习笔记——(4)HDevelop language(结构语句)

1) if ... endif / if ... else ... endif / if ... elseif ... else ... endif 2) for ... endfor 3) while ... endwhile 4) repeat ... until&#xff08;循环体至少被执行一次&#xff0c;直到满足条件时退出。等同于C语言的do...while语句&#xff09; 此外&#xff0c;也…

c语言 字符串分隔,c语言字符串分割–strtok | 逗号分隔-huangea的博客

c语言字符串分割–strtok | 逗号分隔 代码&#xff0c;原理是使用字符串分隔函数Strtok()函数详解&#xff1a;该函数包含在”string.h”头文件中函数原型&#xff1a;char* strtok (char* str,constchar* delimiters );函数功能&#xff1a;切割字符串&#xff0c;将str切分成…

Qt+ArcGIS Engine 10.1 开发(一)

Qt作为一个跨平台C图形用户界面应用程序开发框架&#xff0c;相当于微软的MFC(只能运行在Windows平台上)&#xff0c;Qt命运多舛&#xff0c;几经易主&#xff0c;现在属于芬兰IT服务公司Digia。 Qt环境安装 Qt的最新版本是Qt5.0,该版本是在12月中旬发布的&#xff0c;在这里我…

c语言 返回函数是结构体指针变量,一个函数返回值为指向结构体的指针的问题...

一个函数返回值为指向结构体的指针的问题#include #include struct student{int num;char name[10];struct student *next;}struct student *creat(int x) //这里不知道为什么错&#xff1f;&#xff1f;{struct student *head,*p,*q;int i;for(i0;i{p(struct student*)malloc(…

分享30个新鲜PSD网站模板免费下载

Web 设计始终是设计师充满挑战性的工作。他们需要的专业技能和与的伟大想法&#xff0c;来完成一个设计良好的网站。某个时候设计师需要帮助几个免费的资源&#xff0c;以使工作更轻松。这篇文章是一个完美的解决方案&#xff0c;为网站设计的资源。在这里我们目前 30 新鲜 PSD…

C语言编程快速入门黎明,何用C语言模拟键盘输入?

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼static char szUnd[] "_______ __ ____ _____ ______ ______ ___ _______ ______";//在窗口中输出文字作为信息标题HDC hdc;PAINTSTRUCT ps;TEXTMETRIC tm;switch (iMsg){case WM_CREATE://处理窗口创建的消息hdc GetDC…