在内核窥视用户态
在内核窥视用户态首先,环境:VMware Server上运行的ubuntu10.4,arch为x86_64。
先看下面这个程序:#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int dataA;
char bufA;
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 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,这些后面会用到。 本帖最后由 zyr-linux 于 2010-08-07 10:48 编辑
内核中,每个进程有个结构体存放相关信息: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是局部变量,在栈之中。
和理论分析一致!
更重要的是,用户态下输出的地址和内核态下得到的地址范围对的上,那么,这些地址就是虚拟地址。
把页表的第四级打出来,看看。
说明一下,下面两张图是补截的,地址和其他图不能完全对上。
再和内核的页表第四级对比,可以看出什么?
本帖最后由 zyr-linux 于 2010-08-07 10:54 编辑
现在,我们知道了这些变量的虚拟地址,是不是就能直接在内核态下操作了呢?
实践的结果是————OOPS……
因为,用户态进程的虚拟地址,其转换是通过该进程的页表进行,内核的页表没有这些地址的信息。
不过已经知道了页表的位置,自己转一遍:
得到了物理地址,再加上PAGE_OFFSET获得内核态下能够操作的虚拟地址,把内容打出来验证:
最后,尝试修改:
每改一次,在用户态程序上验证一次,结果符合预期:
以上只是一个简单的尝试,因为在一开始就获得了用户态进程中各个变量的地址;
获得了一个用户态进程的堆、栈、数据段、代码段的地址,并能转换为可在内核态下操作的地址;
那么,理论上,可以对该用户态进程做任何事。
但要想实用,还需要进一步的研究,比如通过反编译,objdump之类的手段获得用户态程序中的地址。 呵呵,大赞! 好东东哦 楼主关键地方代码也贴出来给我们学习学习阿 是的不错的。 回复 1# zyr-linux
能否把程序代码发到我的邮箱?rickcheung@foxmail.com
多谢啦! :wink: 看得不太懂 查找页表,转换地址的代码,linux源代码中有现成的,不过我习惯了自己写。//进行转换是传递信息的结构体
struct PGT_TansInfo
{
//地址信息
unsigned long ul_Vaddr; //虚拟地址
unsigned long ul_Paddr; //物理地址
//页表信息
pgd_t * pst_Level4Table; //页表位置
unsigned long ul_PageSize; //页大小
struct PGT_Level_Info astLevel;
};
/******************************************************************************
函数: int PgtOp_VaddrTrans(struct PGT_TansInfo * vpst_TansInfo)
功能: 地址转换函数
输入: 1) struct PGT_TansInfo * vpst_TansInfo: 转换信息结构体指针
输出: 成功返回0,失败返回非0
备注: 转换虚拟地址为物理地址,并纪录转换中信息
可用于检查虚拟地址是否正确
******************************************************************************/
int MemTool_VaddrTrans(struct PGT_TansInfo * vpst_TansInfo)
{
unsigned long ul_Index;
unsigned long * aul_Talbe = NULL;
//参数检查并赋值
if ((NULL == vpst_TansInfo) || (NULL == vpst_TansInfo->pst_Level4Table))
{
printk("vpst_TansInfo or pst_Level4Table is NULL!\n");
return -1;
}
//X86_64中,虚拟地址前17bit一致
if ((0 != (vpst_TansInfo->ul_Vaddr & MEMTOOL_ADDR_HEAD_MASK))
&& (MEMTOOL_ADDR_HEAD_MASK != (vpst_TansInfo->ul_Vaddr & MEMTOOL_ADDR_HEAD_MASK)))
{
printk("vaddr 0x%lx is error!\n", vpst_TansInfo->ul_Vaddr);
return -1;
}
aul_Talbe = (unsigned long *)vpst_TansInfo->pst_Level4Table;
//查找第四级页表
ul_Index = pgd_index(vpst_TansInfo->ul_Vaddr);
if (0 == vpst_TansInfo->pst_Level4Table.pgd)
{
return -1;
}
else
{
vpst_TansInfo->astLevel.ul_TableAddr = (unsigned long)aul_Talbe;
vpst_TansInfo->astLevel.ul_Index = ul_Index;
vpst_TansInfo->astLevel.ul_Value = aul_Talbe;
}
//查找第三级页表
aul_Talbe = (unsigned long *)(PAGE_OFFSET + ((aul_Talbe & PAGE_MASK) & MEMTOOL_ADDR_NX_MASK));
ul_Index = pud_index(vpst_TansInfo->ul_Vaddr);
if (0 == aul_Talbe)
{
return -1;
}
else
{
vpst_TansInfo->astLevel.ul_TableAddr = (unsigned long)aul_Talbe;
vpst_TansInfo->astLevel.ul_Index = ul_Index;
vpst_TansInfo->astLevel.ul_Value = aul_Talbe;
//判断是不是1GB页
if (0 != (_PAGE_PAT & aul_Talbe))
{
vpst_TansInfo->ul_PageSize = PGT_PAGESIZE_1G;
//取物理地址
vpst_TansInfo->ul_Paddr = ((aul_Talbe & PUD_MASK) & MEMTOOL_ADDR_NX_MASK)
+ (vpst_TansInfo->ul_Vaddr & (PUD_SIZE - 1));
return 0;
}
}
//查找第二级页表
aul_Talbe = (unsigned long *)(PAGE_OFFSET + ((aul_Talbe & PAGE_MASK) & MEMTOOL_ADDR_NX_MASK));
ul_Index = pmd_index(vpst_TansInfo->ul_Vaddr);
if (0 == aul_Talbe)
{
return -1;
}
else
{
vpst_TansInfo->astLevel.ul_TableAddr = (unsigned long)aul_Talbe;
vpst_TansInfo->astLevel.ul_Index = ul_Index;
vpst_TansInfo->astLevel.ul_Value = aul_Talbe;
}
//判断4K页还是2MB页
if (0 == (_PAGE_PAT & aul_Talbe))
{
//4K页,查找第一级页表
aul_Talbe = (unsigned long *)(PAGE_OFFSET + ((aul_Talbe & PAGE_MASK) & MEMTOOL_ADDR_NX_MASK));
ul_Index = pte_index(vpst_TansInfo->ul_Vaddr);
if (0 == aul_Talbe)
{
return -1;
}
else
{
vpst_TansInfo->astLevel.ul_TableAddr = (unsigned long)aul_Talbe;
vpst_TansInfo->astLevel.ul_Index = ul_Index;
vpst_TansInfo->astLevel.ul_Value = aul_Talbe;
vpst_TansInfo->ul_PageSize = PGT_PAGESIZE_4K;
//取物理地址
vpst_TansInfo->ul_Paddr = ((aul_Talbe & PAGE_MASK) & MEMTOOL_ADDR_NX_MASK)
+ (vpst_TansInfo->ul_Vaddr & (PAGE_SIZE - 1));
}
}
else
{
vpst_TansInfo->ul_PageSize = PGT_PAGESIZE_2M;
//取物理地址
vpst_TansInfo->ul_Paddr = ((aul_Talbe & PMD_MASK)& MEMTOOL_ADDR_NX_MASK)
+ (vpst_TansInfo->ul_Vaddr & (PMD_SIZE - 1));
}
return 0;
}
页:
[1]
2