rootkit

第一章    背景
1.1 Rootkit的概念
1.2 Rootkit及其检测技术的发展现状
1.3 Rootkit检测工具介绍
1.4本书所涉及的概念和工具

第二章    Rootkit与检测技术相关的硬件基础
2.1 三环和零环
2.2 保护模式的内存保护
2.3  IDT/GDT/LDT
2.4 系统调用环境切换

第三章    Rootkit与检测技术相关的Windows内核基础
3.1 Windows内核架构
3.2 内核关键组件介绍

       2.2.1 对象管理器
       2.2.2  I/O管理器
       2.2.3 文件系统
    2.2.4 配置管理器以及注册表

       2.2.5  TDI接口
3.3 Windows内核模式程序的编写和编译
3.4 Windows内核模式程序的加载和调试


第四章    Rootkit检测的方法
4.1 检测Rootkit存在:基于特征的检测法
4.2 检测Rootkit行为:视图比对检测法
4.3 检测Rootkit行为:挂钩检测

第五章    Rootkit和检测技术的主要技巧
       5.1 用户层挂钩技术
       5.2 核心层SSDT挂钩技术
       5.3 核心层Inline挂钩技术
       5.4 驱动分派函数挂钩技术
       5.5 过滤驱动技术
       5.6  DKOM技术

第六章    Rootkit易失性数据检测
       6.1 进程对象专用检测技术
        6.1.1 进程对象基本介绍
    6.1.2 静态检测进程对象技术
    6.1.3 动态检测进程对象技术
       6.2 驱动对象专用检测技术

              6.2.1 驱动对象的工作机制
              6.2.2 驱动对象链检测技术
              6.2.3 驱动对象检测的一些新技术
       6.3 端口专用检测技术
    6.3.1 用户态端口检测技术
    6.3.2 核心态端口检测技术
       6.4 内存扫描检测技术
       6.5 外部强制策略用于Rootkit检测

第七章    Rootkit非易失性数据检测
7.1 文件检测技术
       7.1.1 直接I/O技术
       7.2.2 原始文件系统解析

              7.2.3 文件系统分派函数修复
       7.2.4 文件系统映像恢复
       7.2.5 文件的强制访问与删除

7.2 注册表检测技术
       7.2.1 配置管理器内部调用接口
       7.2.2 注册表原始文件解析

第八章    挂钩检测
8.1  SSDT挂钩检测
8.2  驱动分派函数挂钩检测
8.3  过滤驱动挂钩检测

              8.4  Inline挂钩检测

第九章 Rootkit检测工具的设计考虑
              9.1 多路扫描技术的设计考虑
              9.2 检测工具防破解的考虑
     9.2.1  花指令
     9.2.2  驱动加壳
              9.3 检测工具反调试的考虑
     9.3.1  反SOFTICE调试
      9.3.2  反WINDBG调试
第二章    Rootkit与检测技术相关的硬件基础
2.1
三环和零环
2.2 保护模式的内存保护
2.3  IDT/GDT/LDT
2.4 系统调用环境切换

2.1 三环和零环
Figure 3-1. The rings of Intel x86 processors.

X86处理器通过RING( )来进行访问控制的。共有4层,从0,到30层拥有最高的权限,3层拥有最低的权限。按照intel原有的设计,应用程序在3层,只能访问3层的数据,操作系统在0层,可以访问所有层的数据,而其他驱动程序位于12层,可以访问除0层以外的数据,应该是很好的设计,这样操作系统工作在最核心没有人可以修改他,其他驱动程序在1,2层,有要求则向0层调用,可以做到操作系统的完全安全性.
但现在的OS,包括WINDOWS和LINUX都没有采用,而只是使用2层,RING0和RING3,分别来存放操作系统数据和,应用程序数据.据说是为了兼容其他处理器,从而导致了一旦驱动加载了,就运行在0层,从而就拥有了和操作系统同样的权限,可以做任何事情,而ROOTKIT也就随之而生了.
虽然大多数指令可以同时使用于0层和3层,但有些和系统设置相关的指令,却只能在0层被使用
? LGDT — Load GDT register.
? LLDT — Load LDT register.
? LTR — Load task register.
? LIDT — Load IDT register.
? MOV (control registers) — Load and store control registers.
? LMSW — Load machine status word.
? CLTS — Clear task-switched flag in register CR0.
? MOV (debug registers) — Load and store debug registers.
? INVD — Invalidate cache, without writeback.
? WBINVD — Invalidate cache, with writeback.
? INVLPG —Invalidate TLB entry.
? HLT— Halt processor.
? RDMSR — Read Model-Specific Registers.
? WRMSR —Write Model-Specific Registers.
? RDPMC — Read Performance-Monitoring Counter.
? RDTSC — Read Time-Stamp Counter.
最后2条指令 RDPMC和RDTSC在CR4的位4(PCE)和位2(TSD)被设置的情况下可以同时被0层和3层调用.任何违反上面规定的操作,WINDOWS下,可能会产生通用保护故障的异常.

另外还有些所谓的IO敏感指令,包括

·         cli — stop interrupt processing (on the current CPU)
·         sti — start interrupt processing (on the current CPU)
·         in — read data from a hardware port
·         out — write data to a hardware port
这些指令在0层可以直接被使用,在3层被使用的时候还要检查IO许可位图综合判断是否允许调用,详细的可参考INTEL或AMD的软件开发手册

2.2 保护模式的内存保护
X86处理器的保护,分为段式保护和页式保护.
首先介绍下CPU的运行模式,本文介绍的CPU默认为X86架构。
目前的CPU的运行模式主要有以下几种:
1,实模式:CPU在启动后所处的模式,大家熟悉的DOS就是运行在这种模式下,缺点是默认只能访问1MB内存,单任务.优点就是所有程序都运行在0层,应用程序和系统程序具有同样的权限,可以说是病毒编写者的最爱.
2,保护模式:建议的CPU运行模式,支持分段,分页,可以访问更大的内存,可以运行多任务,大家熟悉的WINDOWS,LINUX均运行在该模式下,提供了丰富的系统管理特性.
3,系统管理模式:(SMM)这个模式提供给操作系统或执行环境一个透明的机制来处理电源管理和OEM独有的特性
4,虚拟8086模式:为了能在保护模式中运行原来能在实模式下运行的程序,所特意提供的一种虚拟的8086模式,在这种模式下允许在保护模式的多任务环境下执行实模式程序.所以我们可以在WIN9X,WIN2K下运行大多数的DOS程序.
5.64位扩展模式:INTEL和AMD均推出了支持64位的CPU,支持64位的线性寻址,其又分位个子模式;
   a::64位模式支持64位线性寻址,支持超过64GB的物理地址,所有64位程序均运行在此模式下.
   b: 兼容模式:原有的32位程序运行于此模式下,从而可以使32位程序不用修改就直接运行于64位操作系统下,保护了用户的投资.

X86处理器的分段模式包括以下几种形式:
1平坦模式:
在这种模式下,系统能访问一个连续的,不分段的地址空间,所有的段被映射到同一个地址空间,所有的段都与相同的基地址0,界限为4GB(IA32).我们所用的操作系统,无论是WINDOWS,还是LINUX都运行在这种模式下.
2,多段模式:
不同的段,代码段,数据段,堆栈段等位于不同的段中,由于现在OS大多不采用,所以也就不详细介绍了.

页式保护:
由于现在操作系统大多采用页式保护来进行操作系统的保护,所以稍微详细介绍下.
首先要打开分页模式必须使CPU处于保护模式下,实模式下不能使用分页。

只有在CR0寄存器的位01(启用保护模式)位311(启用分页)后才能使用分页模式.
我们在访问系统的时候一般会通过虚拟地址来访问,后者通过分段机制转换为线性地址,(份段是不能被禁止的),如果禁止分页,那么直接将线性地址当作物理处理而发到地址线上.如果启用了分页,那么再通过分页机制,将线性性地址转为物理地址,再发到地址线上进行访问.
只要进入保护模式,那么自动启用段级保护,只要启用分页,那么自动启用页级保护.虽然在保护模式下我们不能关掉段级保护,但却能关掉页级保护,只需要做如下操作:
1,清除CR0WP
2,对每一级的页目录项和页项设置R/W位为W,U/S位为U即可以把页设置为可写的用户页,也就相当于关闭了页级保护.
我们在访问内存的时候,系统会做以下操作:
通过段选择子,GDT中找到段基地址,进行各种特权检查,确保目标段可以使用,然后将段基地址加上我们的偏移行成线性地址,然后通过分页形成物理地址,放到地址线上进行访问.
实际上系统为了性能考虑,增加了所谓的段描述符映射寄存器,位于CPU内部,除了第一次访问内存需要做以上事情外,对以后的所有访问,如果不改变段的话,均通过映射寄存器来访问,以增加效率,以前有人实现在DOS下访问1M以上空间就是利用INTEL的缺陷,CPU切入保护模式,设置4GB界限,然后切换回实模式.INTEL CPU在切换回来后并没有重新设置映射寄存器,导致界限还是4GB,从而访问额外的内存.
系统分页可以包括以下几种情况:
1 非PAE模式下4K分页,线性地址被分为3个部分,最高10位为页目录表偏移,中间10位为页表偏移,最低12位为4KB页内偏移,从而可以寻址2^10*2^10*2^12=4GB内存

2, 1非PAE模式下4M分页,线性地址被分为2个部分,最高10位为页目录表偏移, ,低22位为4MB页内偏移,从而可以寻址2^10*2^22=4GB内存.

其中页目录表中存放的是页目录项,页表中存放的是页表项,4M分页只有页目录没有页表.

注意我们前面所说R/W位位于位1,代表了该页是否可写(0,只读,1可写),U/S位位于位2,代表了该页是否为系统页(0,系统页,只能被RING0程序访问,1,用户页,可以同时为0,3程序访问).

还有就是PAE模式,本来这种模式是为了寻址超过4GB内存才被使用的,,它可以让CPU支持36位物理寻址,支持物理地址达64GB,但实际意义不大,超过4GB的话建议大家还是用64CPU来寻址,PAE模式有一定性能上的损失.不过之所以在这里介绍他是因为,AMD首先在其AMD64 CPU中率先推出的NX执行保护技术,该技术对于系统来说还是有很大的革新的意义.
众所周知,很多攻击技术都是利用缓冲区溢出,来实现的,就是说利用程序的漏洞,攻击者可以设定一段精心构造的代码,然后通过溢出,将堆栈中的返回地址替换为攻击者设定好的地址从而执行相应的代码,一般包括堆溢出和栈溢出,他们的特点是都是位于数据段中,不同于我们通常程序代码所处的代码段.而以前的X86处理器并没有执行段的概念,只要该页是可读的,那么他就可以被执行,虽然有很多方法可以从软件上来进行补救,但都无法从根本上解决缓冲区溢出的问题,基于这种情况下AMD首先推出了NX执行保护的概念,INTEL也随后跟进了,也就是说在硬件上增加了执行保护.所有的程序和代码在执行前都会进行页保护检查,凡是违反保护的,将一律不被执行,所以尽管你还是可以溢出,但你已经无法获得执行了,所以可以从根本上很好的解决缓冲区溢出的问题.
简单介绍下64CPU的一些东西:
AMD64是一个架构。在这个架构下,支持64位操作数,虚拟地址空间也被扩展到了64位。AMD6464位的操作增加一个名为Long ModeCPU运作模式。在Long Mode下有2种执行模式:一种被称为64-Bit Mode。一种被称为Compatibility Mode。原来32BITCPU支持的32位、16位保护模式和实模式则被称为Legacy Mode. AMD64 Architecture Programming Manual中有下表描述AMD64支持的各种模式。

分页机制在Long Mode下有2种可能性。 1种是1PAGE4K,另外1种为1PAGE2M4K PAGE的地址转换由4级页表转换得来。而2M PAGE的地址转换由3级页表转换得来。

4K PAGE的地址转换由4级页表转换,是把地址划分为6组。Bit48-63Bit 47的符号扩展,不在转换中起作用。为什么要这么设计要问AMDBit39-47Page Map Level 4(PML4) IndexBit30-38Page Directory Pointer(PDP) IndexBit21-29Page Directory(PD) IndexBit12-20Page Table (PT) Index。转换过程与32位下的一样,就不多介绍了,要介绍的是各表的Entry因为他们和”NX执行保护”大大相关。下面是各表的Entry格式


2MPAGE则是3级转换得出物理地址位置。
把地址划分为5组。Bit48-63Bit 47的符号扩展,不在转换中起作用。Bit39-47Page Map Level 4(PML4)Bit30-38Page Directory Pointer(PDP)Bit21-29Page Directory(PD)。省略了Page Table

各表项如下:

Page Map Level 4(PML4)Page Directory Pointer(PDP) IndexPage Directory(PD) Index Page Table (PT) Index都是以表的形式存在。每个表容纳一定数量的Entry。我们可以看到在各个表的Entry都会有1NX Bit,位于Bit 63。其他的Bit我们暂不关心,这个NX bit则是这次”数据执行保护”的关键所在。
No Execute (NX) Bit.,在各Entry的中的Bit63决定当对应的项映射到虚拟内存中,对应的地址是否可以是代码并且被执行。当NX位被设定为1,代码不可以从对应的虚拟地址执行。如果NT位被设定为0,代码可以从对应的地址执行。
NX是扩展功能,通常在某个特殊寄存器的某个Bit会有一个标志,来起用NX功能。这个Bit就是No-Execute Enable (NXE) Bit。在Extended Feature Enable Register(EFER)寄存器的Bit11EFER寄存器是Model Specific Register(MSR)的其中一个。EFER位于MSRC0000080H处。可以通过特权指令RDMSR/WMSR来进行读写。当NXE1时,NX功能就启动了。所谓”数据执行保护”也就被启动出来。在启动NX功能前,必须通过CPUID指令来确定NX功能确实存在。CPUID指令的Function 80000001H用于检查CPUSignatureAMD扩展功能。可用来检查NX功能是否存在。
NXE,也就是EFERBit11被置为1前,NX Bit被认为是保留位。如果将NX Bit1则会导致一个Page Fault Exception。只有将NXE置为1后,NX位才开始发挥作用。当NX发生作用时,如果每次执行指令时都去检查NX Bit,恐怕CPU的效率不会很好。因此,NX Bit只在指令TLBTLB是虚拟地址和物理地址转换用的CACHE)加载的时候才会被检查。CPU在指令预取时,如果没有命中指令TLB,就需要尝试将虚拟地址和物理地址转换加载到TLB。此时,如果NX Bit被检查到为1,就引发一个Page Fault exception。而且无论是否是RING0,这项检查都无法逃过。整个过程和违反其他保护的检查的结果一样都会触发页中断处理. 由于每个表Entry都有NX位。因此不同表Entry的能控制的NX范围也不同,比如PTE中的NX只能控制对应的PAGE是否NX。而PDENX位则作用于PDE所指向的整个PT。当PDENX位为1,无论对应的PTE是否NX被置位,对应的页面就是NX,不可执行.
上面讲到的都是Long ModeNXAMD在设计CPU时,同样将NX设计到了Legacy Mode中。但是Legacy Mode的情况有些特殊,32BITPTEPDE需要兼容INTELIA32PTEPDE。而且IA32PTEPDE并没有什么空闲的地方可以插入NX位。为了兼容INTEL,所以只能从INTEL原有的模式上下手.PENTIUM PRO开始,INTEL定义了一个名为Physical Address Extension(PAE)的扩展模式。在这个模式下,能够使用到36Bit的物理地址。能动脑筋的恐怕就是Physical Address Extension(PAE)起用后的PDPEPDEPTE了。在PAE起用后,PDPEPDEPTE被扩展为64Bit. 由于PAE只用36BIT的地址空间,Bit63到可以占用一下。INTELPAE中规定了4K2M2种页面格式,那AMD看来也必须兼容4K2M2种格式的页面。AMD在设计Legacy ModeNX功能时,不象Long Mode那么全面。并没有在所有的级别都设定了NX Bit
首先看看Legacy Mode情况下的4K页面。
虚拟地址分成4部分。Bit30-31Page Directory Pointer(PDP)Bit21-29Page Directory Index,Bit 12-20Page Table(PT) Index。最低12位为页内偏移

Legacy mode PAE 4K PDPE

Legacy mode PAE 4K PDE


Legacy mode PAE 4K PTE
我们可以看到在Legacy PAE Mode下,4K的页面模式,只有PTEPDE能控制NX。而PDPE则无法控制NX
再看看Legacy Mode情况下的2M页面。

虚拟地址分成3部分。Bit30-31Page Directory Pointer(PDP)Bit21-29Page Directory Index

Legacy mode PAE 2M PDPE

Legacy mode PAE 2M PDP
Legacy PAE Mode下,2M的页面模式,只有PDE能控制NX。而PDPE同样无法控制NX
自从AMD64推出这样的规格后。INTEL也开始走“兼容”路线。INTEL称为EM64TNX规格的兼容是EM64T的一部分。
NX规格可以使得某片区域的RAM无法被执行。但是又不影响读写,这样的区域很符合堆和栈的特征。通常堆和栈是只能被读写,不被用来生成可执行代码的。这个很不错的特性被WINDOWS XP SP2所利用。就是所谓的”数据执行保护”了。但是”数据执行保护”有其局限性,因为”数据执行保护”归根结底是个CPU提供的硬件保护措施,如果CPU不支持NX特性,那么”数据执行保护”就无从谈起。不过现在的CPU也大多都支持NX”数据执行保护”的这个功能了.
数据执行保护”对一些应用提出的新的要求。比如可执行文件的壳程序以及大家都很习惯用的API HOOK,远线程等等。启动了”数据执行保护”后,正常情况下通过malloc/HeapAlloc获得的RAM,则是不可执行的。意味着当壳解码真正执行内容时,如果是解码到malloc/HeapAlloc中获得的空间,那代码会无法执行起来。

必须在执行目标代码前通过VirtualProtectEx,来改变页面的属性为PAGE_EXECUTE/PAGE_EXECUTE_WRITE_COPY/PAGE_EXECUTE_READ,或者PAGE_EXECUTE_READWRITE。这对可执行文件的壳作者提出了新的挑战。如果壳代码不顾一切,先将所有的的页面全部设定为PAGE_EXECUTE_READWRITE,那么就彻底破坏了NX的原有保护功能。在壳代码所在的进程范围内无法阻止病毒蠕虫和恶意入侵。通常HEAPSTACK是只能被读写,不被用来生成可执行代码的。这个很不错的特性被WINDOWS XP SP2所利用。就是所谓的”数据执行保护”了。但是”数据执行保护”有其局限性,因为”数据执行保护”归根结底是个CPU提供的硬件保护措施,如果CPU不支持NX特性,那么”数据执行保护”就无从谈起。
WINDOWS XP SP2以及其他能支持NXWINDOWS OS下,有时候由于NX的存在导致的问题会严重影响系统。原先在没有NX的情况下勉强是跑起来了。但是一旦起用了NX功能,应用就崩溃了,所以MS为我们还保留了一手, Boot.iniNX功能新增了2个开关。/NOEXECUTE/EXECUTE/NOEXECUTE用来起用NX功能。而/EXECUTE则关闭了NX功能。当然是否关闭他就看你自己的要求了.
虽然有了NX执行保护,但并不能说就高枕无忧了,网上已经有方法号称可以避开32XP Serivce Pack 2NX保护来溢出.
因为WINDOWS中提供了关闭NX的机制,只要溢出程序能够以ProcessExecuteFlagsMEM_EXECUTE_OPTION_ENABLE而不是MEM_EXECUTE_OPTION_DISABLE调用NtSetInformationProcess就可以关闭当前进程的NX执行保护
ULONG ExecuteFlags = MEM_EXECUTE_OPTION_ENABLE;

NtSetInformationProcess(
    NtCurrentProcess(),    // (HANDLE)-1
    ProcessExecuteFlags,   // 0x22
    &ExecuteFlags,         // ptr to 0x2
    sizeof(ExecuteFlags)); // 0x4
NTDLL中已经为我们构造好了整个调用过程;首先把代码执行导向执行MOV AL,1
RET的地方,比如xor eax,eax/inc eax/ret; mov eax, 1/ret;等等,NTDLL中已经有了
ntdll!NtdllOkayToLockRoutine:
7c952080 b001             mov     al,0x1
7c952082 c20400           ret     0x4
然后执行导向
ntdll!LdrpCheckNXCompatibility+0x13:
7c91d3f8 3c01             cmp     al,0x1
7c91d3fa 6a02             push    0x2
7c91d3fc 5e               pop     esi
7c91d3fd 0f84b72a0200     je   ntdll!LdrpCheckNXCompatibility+0x1a (7c93feba)

ntdll!LdrpCheckNXCompatibility+0x1a:
7c93feba 8975fc           mov     [ebp-0x4],esi
7c93febd e941d5fdff       jmp  ntdll!LdrpCheckNXCompatibility+0x1d (7c91d403)

ntdll!LdrpCheckNXCompatibility+0x1d:
7c91d403 837dfc00         cmp     dword ptr [ebp-0x4],0x0
7c91d407 0f8560890100     jne  ntdll!LdrpCheckNXCompatibility+0x4d (7c935d6d)

ntdll!LdrpCheckNXCompatibility+0x4d:
7c935d6d 6a04             push    0x4
7c935d6f 8d45fc           lea     eax,[ebp-0x4]
7c935d72 50               push    eax
7c935d73 6a22             push    0x22
7c935d75 6aff             push    0xff
7c935d77 e8b188fdff       call    ntdll!ZwSetInformationProcess (7c90e62d)
7c935d7c e9c076feff       jmp  ntdll!LdrpCheckNXCompatibility+0x5c (7c91d441)

ntdll!LdrpCheckNXCompatibility+0x5c:
7c91d441 5e               pop     esi
7c91d442 c9               leave
7c91d443 c20400        ret     0x4
返回回来后原来进程的NX就已经被关闭了,然后就可以做想做的事了,至于如何对付我想就不用多说了吧..


2.3 GDT/IDT./LDT
X86 处理器有些系统关键的寄存器和数据结构 , 参考如下 :

下半部分的线性地址到物理地址的转换我们前面已经介绍过了,就不多说了.
上面的包括了系统寄存器和关键数据表格GDT,IDT,LDT,TSS各种GATE.
EFLAGS为标志寄存器,我们平时进行各种比较判断的时候,CPU都是根据里面的标志位进行指令的跳转,里面有IOPL,该位决定我们能否在RING3进行IO,CR0~CR4为控制寄存器,要注意的是CR0的位16,该位是INTEL486后引入的,486之前对于只读页,只在RING3受控制,RING0则无限制,而在486以后就引入该位,因为很多OS的写时拷贝机制必须依赖他,RING0如果不受限制的话,该机制将无法有效的实现,所以即使在RING0写只读页,也要能引发异常中断,所以增加了该位来控制RING0是否可以写只读页,如果为0则不能写,如果为1则可以写.RING3程序根本不能访问CR0,达到了既能实现OS机制,又不影响系统的安全性..
CR1为保留寄存器,不能被使用,CR2中存放是发生页中断时候的线性地址,只在页中断处理程序中使用,而一般不用,CR3又被称为PDBG,页目录基地址寄存器,用来存放当前映射的页目录表的物理地址..CR4主要进行CPU架构扩展比如支持PAE,PSE等等,INTELAMD64位处理器中又增加了CR8寄存器提供了TRP任务优先寄存器的支持.

系统中存在着一些系统表格,OS就是通过他们来进行管理和实现各种保护,中断处理等,分为GDT(全局描述符表),LDT(局部描述符表)IDT(中断描述符表).,其中GDTIDT系统有专门的寄存器来存放他们的基地址和界限,LDT则位于GDT.这些表中存放的是描述符,包括普通描述符和系统描述符,前者包括代码段,数据段,后者包括LDT,TSS各种门(任务门,调用门,中断门,陷阱门)



通过SGDT,SIDT我们可以在RING0RING3获得他们的地址和界限,但只能在RING0进行访问,因为他们的地址处于系统页中(在以前的WIN9X系列中直接可以在RING3访问,导致CIH类似的病毒横行,NT系列进行了保护,一般不能直接访问,但还是可以通过内核对象\device\physicalmemoryl来访问,不过在WIN2K3SP1以后及VISTAMS进行了更多的保护)
我们看下WIN2K下的GDT

如果你稍微注意下的话,就会发现我们的程序中在RING0,CS总是8,DS总是0X10,RING3, ,CS总是0X1B,DS总是0X23,也就是说总是对应于GDT中第1,2,3,4表项目,而不象WIN9X下那么随意,那么做其实也是为了向后的兼容,因为在XP以后,MS引入了在PENTIUM2以后引入的指令SYSENTER,SYSEXIT,2条指令都要求RING0 CS,RING DS,RING3 CS ,RING3 DS必须连续排列.GDT中可以存放普通数据描述符和调用门,TSS,任务门,首先说下什么是门,这里的门其实已经很形象的解释了他的用途,可以用来进行特权级的切换,最常用的就是RING3-.RONG0之门.这也是很多RING3病毒趋之若 骛,谁都想获得系统控制的最高权限.
而用的最多的恐怕就属调用门了,
其中SEGMENT SELECTOR就是你要跳转的目标代码段选择子一般就是8,最低16BIT和最高16BITOFFSET就是你要跳转的目标地址,P1表示该描述符存在有效,DPL一般为3,TYPE就是0XC,表示调用门,PARAM COUNT为你要从RING3堆栈拷贝到RING0堆栈的参数个数,其实这个没什么用因为我们可以直接在RING0访问RING3堆栈,所以就是多余,可能INTLEAMD后来也发现这个了,就在64BITCPU下取消了该段.调用的时候通过远调用选择子为调用门的选择子,偏移地址随意,因为目标地址已经在调用门里了,网上的相关代码很多,可以参考下64位下的调用门基本差不多,不过OFFSET也为64位.
首先让我们来看看 WINXP-64下的GDT概貌吧。

0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 9b 20 0
ff ff 0 0 0 93 cf 0
ff ff 0 0 0 fb cf 0
ff ff 0 0 0 f3 cf 0
0 0 0 0 0 fb 20 0
0 0 0 0 0 0 0 0
68 0 70 10 4e 8b 0 0
0 f8 ff ff 0 0 0 0
ff f 0 0 fe f3 40 ff
0 0 0 0 0 0 0 0
ff ff 0 0 0 9a cf 0
0 0 0 0 0 0 0 0
GDTR
Limit: 6f 0
BASE: 0 0 4e 0 0 f8 ff ff

IDTR
Limit: ff f
BASE: 70 0 4e 0 0 f8 ff ff

让我们来看下WINXP -64 1218版本中的GDT吧。
可以看到原来的NULL SELECTOR还是一样,不过好象扩展到16位的。
后面开始就是所谓的正式的东西了,先简单说下,INTEL在PII以后利用的FASTCALL技术,从RING3切换进RING0 利用指令SYSENTER,SYSEXIT来进行切换,而64位下则采用了SYSCALL,SYSRET来切换,提高了系统切换的性能,但 也有了一定的限制,就是不能象9X那样随意了,在GDT中RING0代码段,数据段,RING3代码段,数据段必须连续排列才行。

而SELECTOR 10H估计就是64位下的RING0代码段的SELECTOR了。
0 0 0 0 0 9b 20 0
由于在LONG MODE下代码段只考虑D,L,P,DPL,C位由此可见他属于64位模式的,存在的RING0,非一致代码段

而SELECTOR 18H估计就是64位下的RING0数据段的ELECTOR了。奇怪的是64位下的只考虑P位,其他一概忽略,确实令人费解。
需要说明的是在64位下DS,ES,SS,所引用的数据段根本就不考虑基地址和界限,完全是FLAT模式的,而FS,GS则有些特殊
系统专门配置了FS。BASE,GS。BASE,的专门的MSR寄存器来存放其基地址,估计在64位WINDOWS下他们有特殊的用途。并专门 配备SWAPGS等指令快速切换RING0,RING3的GS地址等。。。。。。

后面是相应的RING3代码段和数据段
ff ff 0 0 0 fb cf 0
ff ff 0 0 0 f3 cf 0
0 0 0 0 0 fb 20 0
23H,2BH,33H这里有3个段。
也就是说RING3代码段有2个,这是为什么呢???
其实也很简单,就是为了兼容性,大家都知道64位WNDOWS下核心是64位的,所有的驱动程序作为OS的一个部分所以也必须是64位的
而应用层则不同,可以同时使用32位和64位,这也就是为什么要有2个RING3代码段的原因了。
首先我们看23H的代码段
ff ff 0 0 0 fb cf 0
其CS。L=0,CS。D=0也就是当指令在这个段的时候系统处于COMPATIABLE MODE下,我们所有的32位程序执行的时候,系统会根据其
是32位还是64位的PE来进行加载。而WOW64估计也是根据他来判断是否需要进行相应的参数转换(进行系统调用的时候)。
而33H的代码段
0 0 0 0 0 fb 20 0
其CS。L=1,CS。D=0也就是当指令在这个段的时候系统处于64 BIT MODE下,也就是64位应用程序执行时候的SELECTOR。
至于 CS。L=1,CS。D=1的这个模式AMD说是为未来处理器准备的,所以我们也就不得而知了。

大家注意40H所对应的描述符是16个字节的,也就是我们所说的TSS
68 0 70 10 4e 8b 0 0
0 f8 ff ff 0 0 0 0
可见他是个基地址为0XFFFFF800004E1070界限为68H的TSS
另外要说的是64位下的GDT是混合8,16字节的,数据代码段的描述符是8字节,而系统描述符是16个字节的, 所以程序员在自己定位的时候需要小心。

53H对应是RING3数据段,界限是0FFFH,基地址0FFFE0000,运行时FS指向他,估计就是运行在ring3下的程序,当前线程的 TEB 所在地址空间。
(由此可见每个RING3程序都有4GB空间,另外的好处就是在驱动通过影射内存来实现共享内存的方式可以直接在应用的32位指针中使用,而不用进行其他转换。)
ff f 0 0 fe f3 40 ff
而后面哪个 ff ff 0 0 0 9a cf 0有什么用目前还不清楚。
至于LDT由于现代OS大多不用,所以也就不介绍了.

不同于GDT,IDT中只能存放门,而且不能放调用门,只能是任务门,中断门,陷阱门,中断门和陷阱门的区别在于是否关中断.,IDT中最多只能存放256.


可见中断门的格式和调用门基本差不多,调用门通过CALL来调用,而中断门通过INT来调用.

2.4
系统环境调用切换
现代操作系统一般都采用分层模型 , 核心层为应用层功能服务 , 一般都通过系统服务调用从 RING3 切换到 RING0,. 早期的 OS 一般都通过中断门老实现这个功能比如 LINUX INT 0X80,WINNT,WIN2K 下的 INT 0X2E, 中断门的用法和调用门差不多 , 而在 XP 以后则采用了快速系统调用 SYSENTER SYSEXIT 来进行系统调用和返回 .
其优点是快速而且没有保留堆栈的开销, ITNEL 的手册上关于他们介绍的很详细,我简要说明一下 SYSENTER INTEL P2 后引进的快速从 RING3~RING0 FASTCALL ,从 FAMILY 6 MODEL 3 STEP 3 也就是从 PII300 以后引进的,这也是为什么 WINXP 需要 PII300 以上的原因。在使用 SYSENTER 之前必须定义好 RING0 CS EIP ESP ,通过设置相应 MSR 寄存器 , WRMSR 指令来设定(必须在 RING0 层执行)通过将相应的寄存器地址号放入 ECX 中, WRMSR 可以设置这些 MSR 寄存器 , 对应关系如下 SYSENTER_CS_MSR 174H SYSENTER_ESP_MSR 175H SYSENTER_EIP_MSR 176H 执行 SYSENTER 指令的系统必须满足 1 :转换后的 RING0 代码段必须是 FLAT 4GB 的可读可执行的非一致代码段 .2: 转换后的 RING0 堆栈段必须是 FLAT 4GB 的可读可写向上扩展的数据段由于 FASTCALL 不保存任何返回的地址,所以在调用前你必须自己设定好, RING0 代码段 SELECTOR RING0 堆栈段 SELECTOR RING3 代码段 SELECTOR RING3 堆栈段 SELECTOR ,必须在 GDT 中连续的排列所以在 XP 下相应的 SELECTOR ,必然是 8H 10H 1BH 23H ,必须将返回至 RING3 EIP,ESP 通过寄存器传递进 RING0 以便 SYSEXIT 返回使用,在 SYSEXIT 返回之前, EDX RING3 EIP ECX RING3 ESP 而相应的 CS SS ,则由 RING0 CS 加上 10H 18H 来返回
RING3~RING0
1. 装载 SYSENTER_CS_MSR CS 寄存器 .
2. 装载 SYSENTER_EIP_MSR EIP 寄存器。
3. SYSENTER_CS_MSR+8 装载到 SS 寄存器
4. 装载 SYSENTER_ESP_MSR ESP 寄存器。
5. 切换 RING0.
6. 清除 EFLAGS VM 标志
7. 执行 RING0 例程
RING0~RING3
1 SYSENTER_CS_MSR+16 装载到 CS 寄存器
2. EDX 的值送入 EIP
3. SYSENTER_CS_MSR+24 装载到 SS 寄存器
4. ECX 的值送入 ESP
5. 切换回 RING3
6. 执行 EIP 处的 RING3 指令
XP 也是用了同样的方法 , 同时由于没有堆栈的保留 , 所以所有的数据传送需要通过寄存器来传送比如 :
ntdll!NtReadFile:
77f5bfa8 b8b7000000        mov     eax,0xb7
77f5bfad ba0003fe7f        mov     edx,0x7ffe0300
77f5bfb2 ffd2              call    edx
77f5bfb4 c22400            ret     0x24
这里的 B7 就是 NtReadFile 在核心服务表对应的子功能号 ,
7ffe0300 8bd4             mov     edx,esp
7ffe0302 0f34             sysenter
7ffe0304 c3               ret
通过EDX,传入RING3堆栈指针.
以上是32位的情况,64位下有所不同,因为AMD首先在64位中引入,所以INTEL也只能跟进,采用和AMD同样的指令使用     SYSCALL和SYSRET来进行切换
ntdll!NtReadFile:
00000000'77f9fc60  4c8bd1          mov     r10,rcx
00000000'77f9fc63  b8bf000000      mov     eax,0xbf
00000000'77f9fc68  0f05            syscall
00000000'77f9fc6a  c3              ret

参考文献:
1,Intel? 64 and IA-32 Architectures Software Developer’s Manual
2,AMD64 Technology AMD64 Architecture Programmer’s Manual
3, Microsoft? Windows? Internals, Fourth Edition: Microsoft Windows Server? 2003, Windows XP, and Windows 2000
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值