您的位置:首页 > 其它

在内核窥视用户态

2016-03-28 14:56 183 查看
在内核窥视用户态

首先,环境:VMware Server上运行的ubuntu10.4,arch为x86_64。

先看下面这个程序:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

int dataA;

char bufA[1000];

int main()

{

int dataB;

int i_GetChar;

char * bufB = NULL;

bufB = malloc(1500);

if (NULL == bufB)

{

printf("malloc failed\n");

return 0;

}

dataA = 0X55aa;

dataB = 0Xaa55;

memcpy(bufA, "bufA org data", strlen("bufA org data"));

memcpy(bufB, "bufB org data", strlen("bufB org data"));

printf("bufA = %p, bufB = %p, &dataA = %p, &dataB = %p\n", bufA, bufB, &dataA, &dataB);

for ( ; ; )

{

printf("get char(p:print; e:exit):");

i_GetChar = getchar();

if ('p' == i_GetChar)

{

printf("bufA: %s\n", bufA);

printf("bufA: %s\n", bufB);

printf("dataA: 0x%x\n", dataA);

printf("dataA: 0x%x\n", dataB);

}

else if ('e' == i_GetChar)

{

break;

}

}

free(bufB);

return 0;

}

复制代码
这个程序中有:

一个int型的全局变量dataA;

一个int型的局部变量dataB;

一个char型的全局数组bufA;

一段malloc的空间bufB。

程序先输出以上变量的地址,然后按"p"输出一次内容,按"e"退出程序。



接下来,简单的分析一下:

bufA = 0x601080, bufB = 0x6a0010, &dataA = 0x601468, &dataB = 0x7fff337284ac

仅从地址上看,他们就在不同的内存区域。

bufA和dataA是全局变量,在数据区;

bufB是malloc来的,在堆中;

dataB是局部变量,在进程的运行栈中。

抄一段linux自带的关于x86_64下地址空间的说明:

0000000000000000 - 00007fffffffffff (=47 bits) user space, different per mm

hole caused by [48:63] sign extension

ffff800000000000 - ffff80ffffffffff (=40 bits) guard hole

ffff880000000000 - ffffc7ffffffffff (=64 TB) direct mapping of all phys. memory

ffffc80000000000 - ffffc8ffffffffff (=40 bits) hole

ffffc90000000000 - ffffe8ffffffffff (=45 bits) vmalloc/ioremap space

ffffe90000000000 - ffffe9ffffffffff (=40 bits) hole

ffffea0000000000 - ffffeaffffffffff (=40 bits) virtual memory map (1TB)

... unused hole ...

ffffffff80000000 - ffffffffa0000000 (=512 MB) kernel text mapping, from phys 0

ffffffffa0000000 - fffffffffff00000 (=1536 MB) module mapping space

可见,这些变量的地址都在user space中。

对各个用户态进程来说,地址空间都是0000000000000000 - 00007fffffffffff,而不会冲突;

因为这只是虚拟地址,每个用户态进程都拥有自身的页表,相同的虚拟地址地址经过不同的页表转换为不同的物理地址;

而内核的页表并不映射user space,这些后面会用到。

内核中,每个进程有个结构体存放相关信息:struct task_struct;

struct task_struct内容很多,现在只找和内存资源相关的struct mm_struct *mm;

struct mm_struct中内容很多也很多,目前关心的是下面几个:

1,保存了进程使用的各个地址区域(vma)的struct vm_area_struct * mmap;;

2,保存了进程页表的位置的pgd_t * pgd;

3,保存进程堆/栈/数据区/代码区地址的一大堆东东

unsigned long total_vm, locked_vm, shared_vm, exec_vm;

unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;

unsigned long start_code, end_code, start_data, end_data;

unsigned long start_brk, brk, start_stack;

unsigned long arg_start, arg_end, env_start, env_end;

把他们全部打出来看看好了:



bufA和dataA在数据区;bufB是malloc来的,在堆之中;dataB是局部变量,在栈之中。

和理论分析一致!

更重要的是,用户态下输出的地址和内核态下得到的地址范围对的上,那么,这些地址就是虚拟地址。

把页表的第四级打出来,看看。

说明一下,下面两张图是补截的,地址和其他图不能完全对上。



再和内核的页表第四级对比,可以看出什么?



现在,我们知道了这些变量的虚拟地址,是不是就能直接在内核态下操作了呢?

实践的结果是————OOPS……

因为,用户态进程的虚拟地址,其转换是通过该进程的页表进行,内核的页表没有这些地址的信息。

自注:内核态不能(直接调用)用户态的变量和函数、。

不过已经知道了页表的位置,自己转一遍:



2010-08-07 10:41 上传
下载附件 (145.61
KB)

得到了物理地址,再加上PAGE_OFFSET获得内核态下能够操作的虚拟地址,把内容打出来验证:



最后,尝试修改:



每改一次,在用户态程序上验证一次,结果符合预期:



以上只是一个简单的尝试,因为在一开始就获得了用户态进程中各个变量的地址;

获得了一个用户态进程的堆、栈、数据段、代码段的地址,并能转换为可在内核态下操作的地址;

那么,理论上,可以对该用户态进程做任何事。

但要想实用,还需要进一步的研究,比如通过反编译,objdump之类的手段获得用户态程序中的地址。

from: bbs.chinaunix.net/thread-1926053-1-1.html

author: zyr-linux
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: