首页
社区
课程
招聘
[原创]浅谈NT下Ring3无驱进入Ring0的方法
2007-1-14 17:49 32370

[原创]浅谈NT下Ring3无驱进入Ring0的方法

2007-1-14 17:49
32370
[原创]浅谈NT下Ring3无驱进入Ring0的方法
关键字:NT,Ring0,无驱

(测试环境:Windows 2000 SP4,Windows XP SP2.
Windows 2003 未测试)

在NT下无驱进入Ring0是一个老生常谈的方法了,网上也有一些C代码的例
子,我之所以用汇编重写是因为上次在

[原创/探讨]Windows 核心编程研究系列之一(改变进程 PTE)

的帖子中自己没有实验成功(其实已经成功了,只是自己太马虎,竟然还不
知道 -_-b),顺面聊聊PM(保护模式)中的调用门的使用情况。鉴于这些都是
可以作为基本功来了解的知识点,所以对此已经熟悉的朋友就可以略过不看
了,当然由于本人水平有限,各位前来“挑挑刺”也是非常欢迎的,呵呵。

        下面言归正传,我们知道在NT中进入Ring0的一般方法是通过
驱动,我的Windows 核心编程研究系列 文章前两篇都使用了
这个方法进入Ring0 完成特定功能。现在我们还可以通过在Ring3下直接写
物理内存的方法来进入Ring0,其主要步骤是:

0        以写权限打开物理内存对象;
1        取得 系统 GDT 地址,并转换成物理地址;
2        构造一个调用门;
3        寻找 GDT 中空闲的位置,将 CallGate 植入;
4        Call植入的调用门。

前面已打通主要关节,现在进一步看看细节问题:
[零]        默认只有 System 用户有写物理内存的权限
administrators 组的用户只有读的权限,但是通过修改用户
安全对象中的DACL 可以增加写的权限:

_SetPhyMemDACLs        proc        uses ebx edi esi \
                                _hPhymem:HANDLE,\
                                _ptusrname:dword
       
           local        @dwret:dword
        local        @htoken:HANDLE
        local        @hprocess:HANDLE
        local        @个
        local        @OldDACLs:PACL
        local        @SecurityDescriptor:PSECURITY_DESCRIPTOR
        local        @Access:EXPLICIT_ACCESS

        mov                @dwret,FALSE
               
        invoke        RtlZeroMemory,addr @NewDACLs,sizeof @NewDACLs
                        invoke        RtlZeroMemory,addr @SecurityDescriptor,\
                        sizeof        @SecurityDescriptor

        invoke        GetSecurityInfo,_hPhymem,SE_KERNEL_OBJECT,\
                        DACL_SECURITY_INFORMATION,NULL,NULL,\
                        addr @OldDACLs,NULL,\
                        addr @SecurityDescriptor

        .if        eax != ERROR_SUCCESS
                        jmp        SAFE_RET
        .endif

        invoke        RtlZeroMemory,addr @Access,sizeof @Access

        mov                @Access.grfAccessPermissions,SECTION_ALL_ACCESS
        mov                @Access.grfAccessMode,GRANT_ACCESS
        mov                @Access.grfInheritance,NO_INHERITANCE
        mov                @Access.stTRUSTEE.MultipleTrusteeOperation,\
                        NO_MULTIPLE_TRUSTEE
        mov                @Access.stTRUSTEE.TrusteeForm,TRUSTEE_IS_NAME
        mov                @Access.stTRUSTEE.TrusteeType,TRUSTEE_IS_USER
        push        _ptusrname
        pop                @Access.stTRUSTEE.ptstrName

        invoke        GetCurrentProcess
        mov                @hprocess,eax
        invoke        OpenProcessToken,@hprocess,TOKEN_ALL_ACCESS,\
                        addr @htoken

        invoke        SetEntriesInAcl,1,addr @Access,\
                        @OldDACLs,addr @NewDACLs
       
        .if        eax != ERROR_SUCCESS
                        jmp        SAFE_RET
        .endif

        invoke        SetSecurityInfo,_hPhymem,SE_KERNEL_OBJECT,\
                        DACL_SECURITY_INFORMATION,NULL,NULL,\
                        @NewDACLs,NULL
       
        .if        eax != ERROR_SUCCESS
                        jmp        SAFE_RET
        .endif

        mov                @dwret,TRUE

SAFE_RET:

        .if        @NewDACLs != NULL
                        invoke        LocalFree,@NewDACLs
                        mov        @NewDACLs,NULL
        .endif

        .if        @SecurityDescriptor != NULL
                        invoke        LocalFree,@SecurityDescriptor
                        mov        @SecurityDescriptor,NULL
        .endif

        mov                eax,@dwret
        ret

_SetPhyMemDACLs                endp

[一] 可以在Ring3下使用SGDT指令取得系统GDT表的虚拟地址,
这条指令没有被Intel设计成特权0级的指令。据我的
观察,在 Windows 2000 SP4 中 GDT 表的基址都是相同的,
而且在 虚拟机VMware 5.5 虚拟的 Windows 2000 SP4中
执行 SGDT 指令后返回的是错误的结果,在虚拟的 Windows XP
中也有同样情况,可能是虚拟机的问题,大家如果有条件可以试一下:
       
local        @stGE:GDT_ENTRY
       
        mov                @dwret,FALSE
       
        lea                esi,@stGE
        sgdt        fword ptr [esi]
       
        assume        esi:ptr GDT_ENTRY
       
        ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
        ;在 VMware 虚拟环境下用以下两条指令替代
        ;只用于 Windows 2000 SP4
        ;mov        [esi].Base,80036000h
        ;mov        [esi].Limit,03ffh
        ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
       
        mov                eax,[esi].Base
        invoke        @GetPhymemLite,eax
        .if        eax == FALSE
                        jmp        quit
        .endif
       
下面就是虚拟地址转换物理地址了,这在Ring0中很简单,
直接调用MmGetPhysicalAddress 即可,但在Ring3中要
另想办法,还好系统直接将 0x80000000 ? 0xa0000000 影射到物
理0地址开始的位置,所以可以写一个轻量级的GetPhysicalAddress
来替代 :)

@GetPhymemLite        proc        uses esi edi ebx                        _vaddr
        local        @dwret:dword
       
        mov                @dwret,FALSE

        .if        _vaddr < 80000000h
                        jmp        quit
        .endif

        .if        _vaddr >= 0a0000000h
                        jmp        quit
        .endif

        mov                eax,_vaddr
        and                eax,01ffff000h                ;or sub eax,80000000h
        mov                @dwret,eax
quit:
        mov                eax,@dwret
        ret

@GetPhymemLite        endp

[二]调用门在保护模式中可以看成是低特权级代码向高特权级代
码转换的一种实现机制,如图1所示(由于本人较懒,所以借用李
彦昌先生所著的80x86保护模式系列教程 中的部分截图,希望李先
生看到后不要见怪 ^-^):



                                        图1
要说明的是调用门也可以完成相同特权级的转换。一般门的结构如图2所示:
               
门描述符        m+7        m+6        m+5        m+4        m+3        m+2        m+1        m+0
        Offset(31...16)        Attributes        Selector        Offset(15...0)

门描述
符属性        Byte m+5        Byte m+4
        BIT7        BIT6        BIT5        BIT4        BIT3        BIT2        BIT1        BIT0        BIT7        BIT6        BIT5        BIT4        BIT3        BIT2        BIT1        BIT0
        P        DPL        DT0        TYPE        000        Dword Count

                                                                                图2
        简单的介绍一下各个主要位置的含义:

Offset 和 Selector 共同组成目的地址的48位全指针,这意味着,
如果远CALL指令指向一个调用门,则CALL指令中的偏移被丢弃;
P位置位代表门有效,DPL是门描述符的特权级,后面要设置成3,以
便在Ring3中可以访问。TYPE 是门的类型,386调用门是 0xC ,
Dword Count 是系统要拷贝的双字参数的个数,后面也将
用到。下面是设置CallGate的代码:

mov                eax,_FucAddr
        mov                @CallGate.OffsetL,ax                ;Low Part Addr Of FucAddr
        mov                @CallGate.Selector,8h        ;Ring0 Code Segment
        mov                @CallGate.DCount,1                ;1 Dword
        mov                @CallGate.GType,AT386CGate        ;Must A CallGate

        shr                eax,16
        mov                @CallGate.OffsetH,ax                ;High Part Addr Of FucAddr

[三]        既然可以读些物理内存了,也知道了GDT的物理基地址
和长度,所以可以通过将GDT整个读出,然后寻找一块空闲的区域来
植入前面设置好的CallGate:
       
;申请一片空间,以便存放读出的GDT
Invoke        VirtualAlloc,NULL,@tmpGDTLimit,MEM_COMMIT,\
PAGE_READWRITE       
        .if        eax == NULL
                        jmp        quit
        .endif
       
        mov                @pmem,eax
        invoke        @ReadPhymem,@tmpGDTPhyBase,@pmem,@tmpGDTLimit,\
                        _hmem

        .if        eax == FALSE
                        jmp        quit
        .endif
       
        mov                esi,@pmem
        mov                ebx,@tmpGDTLimit
        shr                ebx,3
        ;找到第一个GDT描述符中P位没有置位的地址。
mov                ecx,1
        .while        ecx < ebx
                        mov        al,byte ptr [esi+ecx*8+5]
                        bt        ax,7
                .if        CARRY?

                .else
                        jmp        lop0
                .endif
                Inc                ecx
        .endw
       
        invoke        VirtualFree,@pmem,0,MEM_RELEASE
        jmp                quit

lop0:
        lea                eax,[ecx*8]
        mov                @OffsetGatePos,eax
        add                @PhyGatePos,eax

        mov                esi,@pmem
        add                esi,eax

        invoke        RtlMoveMemory,addr oldgatebuf,esi,8
       
        ;释放内存空间
        invoke        VirtualFree,@pmem,0,MEM_RELEASE

[四] 现在主要工作基本完成了,剩下的就是设计一个运行在Ring0
中的子函数,在这个子函数中我将调用Ring0里面真正的
MmGetPhysicalAddress来取得实际的物理地址,所以这个函数要
有一个输入参数用来传递要转换的虚拟地址,并且还要考虑到如何
获取返回的物理地址(EDX:EAX)。在网络上的C版本代码中,这是通
过定义几个全局变量来传递的,因为没有发生进程切换,所以可以
使用原进程中的一些变量。然而我在传递虚拟地址上采用了另一种
做法,就是通过实际形参来传递的:
       
        Ring0Fuc        proc                        ;_vaddr
       
                ;手动保存
                push        ebp
                mov                ebp,esp
                sub                esp,4
                mov                eax,[ebp+0ch]
                mov                [ebp-4],eax                ;first local val
                pushad
                pushfd
                cli
       
                mov                eax,[ebp-4]
                ;调用真正的 MmGetPhysicalAddress.
                invoke        MmGetPhysicalAddress,eax
                mov                phymem_L,eax
                mov                phymem_H,edx

                popfd
                popad
                ;手动还原
                mov                esp,ebp
                pop        ebp
                retf        4

Ring0Fuc        endp

        最后,通过一个远CALL来调用这个调用门:
       
                lea                edi,FarAddr
                push        _vaddr
                call        fword ptr [edi]

通过亲手编码,可以对调用门、远调用等一些80386+保护模式中
的概念在windows的实现中有了进一步的了解,不再像以前那样模棱
两可了。看似全部写完了,其实中间还有很多可以挖掘出来扩展说
的细节,但我现在已没有精力写了…( :( ),还要准备其他东西
,结尾就用这个不是结尾的结尾,结尾吧(绕口令?)。:)

比较好的排版请到我的blog中观赏:

http://blog.csdn.net/mydo/

如果斑竹不忙的话,请帮我重排一下这里的版面,乱七八糟的。
我排来排去总排不好:(                                                               

                                                    侯佩|hopy
                                                                        2006.01.14 17:09 (机场)办公室

阿里云助力开发者!2核2G 3M带宽不限流量!6.18限时价,开 发者可享99元/年,续费同价!

收藏
点赞7
打赏
分享
最新回复 (29)
雪    币: 191
活跃值: (1922)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
4st0ne 4 2007-1-14 18:18
2
0
一来就看到好文章...支持一下.
最近在捣鼓WIN32ASM,正好拿来研究..
雪    币: 625
活跃值: (1057)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
xzchina 1 2007-1-14 18:20
3
0
好文.
去研究研究!
hopy 兄 你的Windows 核心编程研究系列之一中图一的图片链接失效了.
http://hopy.bokee.com/inc/p1.JPG
雪    币: 442
活跃值: (107)
能力值: ( LV9,RANK:350 )
在线值:
发帖
回帖
粉丝
hopy 8 2007-1-14 18:21
4
0
谢谢,我知道了
雪    币: 258
活跃值: (230)
能力值: ( LV12,RANK:770 )
在线值:
发帖
回帖
粉丝
qiweixue 19 2007-1-14 18:25
5
0
靠!


call fword ptr [edi]

这一个call跑到火星上去了.
雪    币: 4
活跃值: (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
燕北飞 2007-1-14 18:28
6
0
LZ就是活跃在csdn的asm版的hopy  佩服~~敬仰~~
雪    币: 214
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
colboy 2007-1-14 18:28
7
0
学习!!!!!!!!!!!!!
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
WisdomZh 2007-1-14 19:03
8
0
我觉得还是这篇用汇编实现的更干净!!!
雪    币: 248
活跃值: (1031)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
sixL 2007-1-14 19:10
9
0
好!
缺简单应用示例。
雪    币: 441
活跃值: (149)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
rockhard 4 2007-1-15 11:56
10
0
C语言的实现。
http://www.pcvc.net/category/content.asp?sendid=139

看汇编不太习惯
雪    币: 442
活跃值: (107)
能力值: ( LV9,RANK:350 )
在线值:
发帖
回帖
粉丝
hopy 8 2007-1-15 12:14
11
0
你说的没错,我是用汇编重写并且自己改变了一些,我在文章开头
也提到了这点,但是没有给出C的连接,谢谢楼上
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zhucheba 2007-1-15 12:38
12
0
好文章.支持一下
雪    币: 716
活跃值: (162)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
elance 6 2007-1-15 14:47
13
0
good,
thanx!!!
雪    币: 242
活跃值: (163)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
林海雪原 6 2007-1-16 15:46
14
0
严重学习了,希望能看懂!
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
HOoKyOu 2007-1-17 16:50
15
0
借楼主宝地请教一下:
在Ring3下通过读写物理内存来进Ring0,
用NtMapViewOfSection映射物理内存,是不是有什么限制啊?

为什么在一些机子上成功:
NtMapViewOfSection返回00000000(STATUS_SUCCESS)

另一些机子上又不成功(GetLastError返回1008或487):
NtMapViewOfSection返回C00000F4(STATUS_INVALID_PARAMETER_6)

//******************************************
……
ntStatus = _NtMapViewOfSection(hPhyMem, (HANDLE)-1, virtualAddr, 0,*length, &viewBase, length,ViewShare, 0, PAGE_READWRITE );
……
//******************************************
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
宽容 2007-1-22 08:28
16
0
好文章...研究一下.
雪    币: 228
活跃值: (114)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
machoman 1 2007-2-17 11:53
17
0
PM,直接修改PFN,牛!好文章,学习了,

不知道M$在vista中是否还能用?
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ddrmsdos 2007-2-25 09:51
18
0
只是在想一个问题,如果我在写GDT的时候写错了,比如应该写在空的位置上,结果我写在一个已有内容的地方,是不是有可能造成系统崩溃。
雪    币: 442
活跃值: (107)
能力值: ( LV9,RANK:350 )
在线值:
发帖
回帖
粉丝
hopy 8 2007-2-25 23:35
19
0
yes,非常有可能!
雪    币: 442
活跃值: (107)
能力值: ( LV9,RANK:350 )
在线值:
发帖
回帖
粉丝
hopy 8 2007-2-25 23:36
20
0
to machoman:

vista 下此法无效!
雪    币: 902
活跃值: (4105)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
kagayaki 2007-2-28 04:30
21
0
顶!!!!!!!!!!!!!!!
雪    币: 442
活跃值: (107)
能力值: ( LV9,RANK:350 )
在线值:
发帖
回帖
粉丝
hopy 8 2007-2-28 20:00
22
0
向大家申明一下:

很多朋友看了本人的拙作后,给我发电子邮件询问细节,虽然
我都作了回应,但是最好还是QQ或msn联系比较方便:

qq:411987378
msn:kf2b@hotmail.com

谢谢大家支持!

侯佩|hopy
雪    币: 207
活跃值: (12)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
oooooooo 1 2007-3-6 07:52
23
0
谢谢楼主,学习,学习...
雪    币: 247
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
pengmo 2007-4-5 14:03
24
0
帖子里面有些错误 比如有个local  @个
不知道什么意思

能不能把整个例子发上来
请问这个只用masm就可以编译吗
雪    币: 375
活跃值: (12)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
xPLK 3 2007-4-6 20:31
25
0
收下学习了
佩服佩服
游客
登录 | 注册 方可回帖
返回