windows内核基础-1
对象是引用计数的——只有当对象的最后一个引用被释放之后,对象才会被销毁并从内存中释放。 对象不能被用户模式直接访问,用户模式通过一种间接的访问模式,这种机制被称为句柄,句柄是指向一个表格的入口索引。该表格在进程的基础上维护,逻辑上指向驻留在系统空间的一个内核对象。有各种函数用来打开,创建对象,并返回对象的句柄,返回值为0则表示是一个无效句柄,内核代码可以使用对象的句柄或者对象的 直接指针,这个通常由代码调用的API决定,在某些情况下,由用户模式传给驱动程序的句柄必须用ObReferenceObject-ByHandle函数转换成对象指针。 句柄的值是4的倍数,第一个有效的句柄值是4,0永远不是有效的句柄值。 内核代码可以调用ObReferenceObject-ByHandle函数从一个有效的句柄得到指针。如果调用成功,对象的引用计数会加1 .所以即使拥有这个句柄的用户模式客户决定关闭句柄,也不会对拥有指针的内核代码造成威胁。在内核代码调用ObReferenceObject函数之前,不管句柄的拥有者做什么,通过指针访问对象都是安全的。ObReferenceObject函数会将对象的引用技术减1 ,如果内核代码忘记调用该函数,就会造成资源泄露,这个泄露只能通过重启系统来解决。 所有的对象都是引用计数的,对象管理器维护着句柄计数和对象引用总数,一旦某个对象不在被需要,该对象的客户必须关闭句柄,或者解除对此对象的引用,从这里开始,客户程序需认为句柄/指针已无效。对象管理器会在引用计数成为0时销毁对象。 每个对象指向一个对象类型: 对象类型保存着此类对象本身的信息,这意味着每一类对象都有一个对象类类型,对象类型也作为全局内核变量暴露出来,其中有一些在内核头文件中有定义。 对象名称: 一些类型的对象可以有名称。可以通过合适的Open函数使用名称来打开对象。 在用户模式下使用名称调用Create函数,在此名称的对象不存在的情况下,会创建一个对象。但是如果该对象已经存在,它只会打开已经存在的对象。
内核重要数据结构

内核对象,在windows内核中有一种很重要的数据结构管理机制,那就是内核对象。应用层的进程、线程文件、驱动模块、事件、信号量等对象或者打开的句柄在内核中都有与之对应的内核对象。
yigewindows对象可以分为对象头和对象体两个部分。在对象头中至少有一个OBJECT_HEADER和对象额外信息。对象体紧接着对象头中的OBJECT_HEADER 。一个对象指针总时指向对象体而不是对象头。访问对象头,需要将指针减去一个特定的偏移量。对象体内部一般会含有一个type和一个size成员,用来表示对象的类型和大小。
Dispatcher对象
放在对象体的一个共享的公共数据结构,包含了这个数据结构的内核对象都可以作为参数传递给KeWaitForSingleObject()和KeWaitForMultipleObjects()函数,以及应用层的WaitSingleObject()和WaitForMultiple Objects()函数。

(2)I/O对象
I/O对象在对象体开始位置并未放置DISPATCHER_HEADER结构,但通常会放置一个于type和size有关的整型成员,以表示该内核对象的类型
(3)其他对象
除了Dispatcher对象和I/O对象,剩下的都是其他内核对象。其中有两个常用的内核对象,分别是进程对象与线程对象
EPROCESS用于在内核中管理进行的各种信息。每个进程都对应于一个EPROCESS结构,用于记录进程执行期间的各种数据。尽管EPROCESS结构非常大,但它是一个不透明的结构,具体成员并未导出,并随着操作系统版本的变化而变化。
所有进程的EPROCESS内核结构都被放入了一个双向链表,R3在枚举系统进程的时候没通过遍历这个链表获得了进程的列表。因此有的ROOKIT会试图将自己进程的EPPROCESS结构从这个链表中摘掉,从而达到隐藏自己的目的。


ETHREAD结构是线程Nederland内核管理对象。每个线程都有一个对应的ETHREAD结构。ETHREAD结构也是一个不透明的结构,具体成员并未导出。

SSDT的全称是System Services Descriptor Table(系统服务描述符表),在内核中的实际名称是KeServiceDescriptorTable。这个表已通过内核ntoskml.exe导出(在x64里不导出)。
SSDT用于处理应用层通过kernel32.dll下发的各个API操作请求。ntdll.dll中的API是一个简单的包装函数,当kernel32.dll中的API通过ntdll.dll时,会先完成对参数的检查,再调用一个中断(int 2Eh 或者 SysEnter指令),从而实现R3层进入R0层,并将要调用的服务号(也就是SSDT数组中的索引号index值)存放到寄存器EAX中,最后根据存放在EAX中的索引值再SSDT数组中调用指定的服务


TEB结构体
与EPROCESS结构类似,在不同版本的Windows中,TEB结构略有差异。例如,在R3级的应用程序中,fs:[0]的地址指向TEB结构,这个结构的开头是一个NT_TIB结构。

NT_TIB结构的0x18偏移处是一个self指针,指向这个结构自身,也就是TEB结构的开头。TEB结构的0x30偏移处是一个指向PEB的指针。
TEB访问
(1)NtCurrentTeb函数调用
从ntdll.dll中导出了一个N他CurrentTeb函数,该函数可以返回当前线程的TEB结构体的地址。

(2)FS段寄存器访问
FS为寄存器,当代码运行在R3级时,基地址即为当前线程的线程环境块,所以该段也称为TEB段运行如下代码可获得TEB的指针。
mov eax,dword ptr fs:[18h] ;此时eax中为TEB的指针
PEB(进程环境块)存在于用户地址空间,记录了进程的相关信息,每个进程都有自己的PEB信息。
PEB访问:
TEB中的ProcessEnvironemntBlock就是PEB结构的地址,其结构的0x30偏移处是一个指向PEB(进程环境块)的指针。PEB的0x2偏移处是一个UChar成员,名叫“BeingDebugged“,进程被调式时值为1,未被调试时值为0.因此,访问PEB的地址有如下两种方式。
mov eax,dword ptr fs:[30h] ;此时eax里为PEB地址
通过TEB获取
mov eax,dword ptr fs:[18h]
mov eax,dword ptr [eax+30h]

PEB结构也是一个随着windows系统版本的变化而略有差异的结构。通过查阅MSDN或者winternl.h,可以知道PEB结构的定义如下。

windows内核启动过程
- 启动自检阶段:从BIOS中载入必要的指令,然后进行一系列自检操作。
- 初始化启动阶段,自检完成后,根据CMOS的设置,BIOS加载启动盘,将主引导记录(MBR)中的引导代码载入内存。启动代码搜素MDR中的分区表,找出活动分区,将第一个扇区中的引导代码载入内存。引导代码检测当前使用的文件系统,查找ntldr文件,找到之后将启动它。
- Boot加载阶段:先加载ntldr,然后(1),设置内存模式,(2)启动一个简单的文件系统,(3)读取boot.ini文件
- 检测和配置硬件阶段:在这个阶段会检查一些硬件设备
- 内核加载阶段:ntldr将首先加载windows内核Ntoskrnl.exe和硬件抽象层(HAL)。HAL会对硬件底层的特性进行隔离,为操作系统提供统一的调用接口。接下来,ntldr从注册表的HKEY_LOCAL_MACHINE\System\CurrentControlSet键下读取这台机器安装的 驱动程序,然后依次加载驱动程序。初始化底层设备驱动,在注册表的HKEY_LOCAL_MACHINE\System\CurrentControlSet\Serivers键下查找“Start”键的值为0和1 的设备驱动。start的键值可以为0,1,2,3,4,数值越小,启动越早。SEERVICE_BOOT_START(0)表示内核刚刚初始化,此时加载的都是与系统核心有关的重要驱动程序,例如磁盘驱动SERVICE_SYSTEM_START(1)稍晚一些,等等。
- Windows的会话管理启动:驱动程序加载完成之后,内核会启动会话管理器,这是一个名为smss.exe的程序,是windows系统中第一个创建的用户模式进程,其作用如下。(1)创建系统环境变量,(2)加载win32k.sys,它是Windows子系统的内核模式部分(3)启动csrss.exe,它是Windows子系统的用户模式部分,(4)启动winlogin.exe(5)创建虚拟内存页面文件(6)执行上次系统重启前未完成的重命名工作。
登录阶段:windows子系统启动的winlogin.exe系统服务提供对windows用户的登录和注销的支持,可以完成如下工作:(1)启动服务子系统,也称服务启动管理器(SCM)(2)启动本地安全授权(LSA)过程(3)显示登录界面
windowsR3与R0通信
windows分为应用层与内核层他们之间需要通信:当应用程序调用某个API时,实际上这个API被封装在某个DLL库文件中。而DLL动态库中的函数的更底层的函数包在ntdll.dll文件中,当kernel32.dll中的API通过ntdll.dll执行时,会完成参数的 检查工作,再调用一个中断(int 2eh或者Sys Enter指令),从R3层进入R0层。在内核ntoskrnl.exe中有一个SSDT,里面存放了与ntdll.dll中对于的SSDY系统服务处理函数,及内核态的Nt*系列函数,它们与ntdll.dll中对应的SSDT系统服务处理函数,即内核态的Nt*系列函数,它们与ntdll.dll中的函数一一对应
(1)从用户模式调用Nt*和Zw*API连接ntdll.lib
二者没有区别,都是通过设置系统服务表中的索引和栈中设置参数,经由SYSENTER(或syscall)指令进入内核态(而不是像在windows2000)由于是用户模式进入内核模式,代码会严格检查用户空间传入的参数。
(2)从内核模式调用Nt*和Zw*API,连接ntoskrnl.lib
Nt*系列API将直接调用对应的函数代码,而Zw*系列API则通过KiSystemService最终跳转到对应函数代码处,重要的是两种调用对内核Previous Mode的改变:如果从用户模式调用Native API,则Previous Mode 是用户态;如果从内核模式调用Native API,则Previous Mode是内核态。
内核主要由各种驱动组成,这些驱动有的是Windows系统自带的,有的是第三方软件厂商提供的。驱动加载之后,会生成对应的设备对象,并可以选择向R3提供一个可供访问和打开的符号链接,应用层程序可以根据内核驱动的符号链接名调用CreateFile函数打开。在获得一个句柄之后,程序就可以调用应用层函数与内核驱动进行通信了,例如ReadFile(),Writefile()及DeviceIoControl等。
内核驱动一旦执行了Driver Entry入口函数,就可以接受R3层的通信请求了。在内核驱动中专门有一组分发派遣函数用来分别响应应用层的调用请求。
每一个应用层负责I/O的API都对应一个内核中的分发派遣函数,例如CreateFile()对应于DisPatchCreate(),API 被调用后,传递给API的数据和命令就会通过IRP直接传递给对应的而驱动分发派遣函数来处理。当驱动的分发派遣函数处理完这个请求IRP请求之后,驱动可以结束或阻止这个IRP,或者把这个IRP发给下层驱动继续处理。

内核函数:
windows内核部分会调用一些内核层的函数

与应用层函数不同的是,在调用内核函数的时候需要注意它的IRQL,要求。内核在不同的情况下会运行在不同的IRQL级别上,因此在不同的IRQL(中断请求级别)级别上,必须调用符合该IRQL级别要求的内核函数。
内核驱动模块:
Windows内核驱动模块是内核的重要组成部分,既有微软自己开发的内核驱动,也有第三方开发的内核驱动;既有硬件的驱动,也有软件的驱动。内核驱动在磁盘上是一个扩展名为.sys的文件,遵守PE格式规范,能被系统加载运行。
编译好的驱动是如何在系统被加载并执行的呢
#include<ntddk.h>
//驱动卸载函数
VOID Unload(PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
KdPrint(("Driver Unload\n"));//日志输出函数
}
//驱动入口函数
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
UNREFERENCED_PARAMETER(DriverObject);
UNREFERENCED_PARAMETER(RegistryPath);
KdPrint(("DriverEntry\n"));
DriverObject->DriverUnload = Unload;
return STATUS_SUCCESS;
}
(1)创建一个服务(注册表)。在注册表的Services键下建立一个与驱动名称相关的服务键。这个服务键规定了驱动的一些属性,例如启动GROUP与startType决定了驱动加载的先后。越小越先启动
(2)对象管理器生成驱动对象(Driver Object)并传递给DriverEntry()函数。执行Driver Entry()函数,相当于main函数。
(3)创建控制设备对象
(4)创建控制设备符号链接
(5)如果是过滤驱动,则创建过滤设备对象并绑定
(6)注册特定的分发派遣函数
(7)其他初始化动作,例如hook、过滤,(文件系统过滤,网络防火墙等),回调框架(注册表回调)等的注册和初始化。
在接下来我们首先来了解一下什么是对象和句柄:
简而言之,内核对象就是一些数据结构用来描述存储内核中的一个内存块的数据信息。
概念:内核对象可以供系统和应用程序使用来管理各种各样的资源,windows程序员可以调用windowsAPI最终调用ntddk.dll中的函数区创建、打开和操作各种内核对象。常见的内核对象有:访问令牌、事件对象、文件对象、文件映射对象、I/O完成端口、作业对象、邮件槽对象、互斥量对象、管道对象、进程对象、信号量对象、线程对象、可等待计时器对象以及线程池工厂对象等。
对象分为对象头加对象体:
对象头便是相当于CPP中的基类,复杂管理一些简单的,所有对象共有的属性,诸如进程对象头,线程对象头,调试对象头等
对象体相当于cpp中的继承的子类,代表着具体的某个对象;
对象是引用计数的,只有对象的最后一个引用被释放后,对象才能被销毁,从内存中释放。系统模式对象不能被用户模式访问,而是使用一种间接的访问机制,被称为句柄。
句柄是指向一个表格的入口索引,该表格在进程的基础上维护,逻辑上指向驻留在系统空间的一个内核对象,打开或者创建对象时就会返回一个指向该对象的句柄。
windows下的异常处理
windows的异常处理是操作系统处理程序错误或异常的一系列流程和技术的总称

异常处理的基本过程
windows正常启动后,将运行在保活模式下,当有中断或异常发生时,CPU会通过中断描述符(IDT) 来寻找处理函数。
1.IDT是一张位于物理内存的线性表,共256项。在32位模式下每个IDT项的长度是8字节。操作系统在启动阶段会初始化这个表,IDT的位置和长度是由CPU的IDTR寄存器描述的。IDTR寄存器共有48位,其中高32位是表的基址,低6位是表的长度。尽管可以使用SIDT和LIDT指令来读写该寄存器,但LIDT是特权指令,只能在ring 0特权级下运行。
IDT的每一项都是一个门结构,它是发生中断或异常时cpu转移控制权的必经之路。
- 任务门,主要用于cpu的任务切换
- 中断门,主要用于描述中断处理程序的入口
陷阱门,主要用于描述异常处理程序的入口
2.异常处理的准备工作:
当有中断或者异常发生时,cpu会根据中断类型号,转而执行对应的中断处理程序。各个异常处理函数除了针对本异常的特定处理之外,通常那个会将异常信息进行封装,以便进行后续处理。
封装的内容主要有俩个部分,一个是异常记录,包含本次异常的信息,是一个数据结构定义如下:

如下是一些常见产生异常的原因:

另一部分被封装的内容称为陷阱帧,它精确的描述了发生异常时线程的状态(windows 的任务调度是基于线程的)。该结构预处理器高度相关,因此在不同平台上有不同的定义。


可以看到上述结构中包含每个寄存器的状态,但该结构一般仅供系统内核自身或者调试系统使用。当需要把控制权交给用户注册的异常处理程序时,会将上述结构转换成一个名为CONTEXT的结构,它包含线程运行时处理器各主要寄存器的完整镜像,用于保存线程运行环境。
以下就是x86平台上的CONTEXT


3.内核态的异常处理过程
- 检测当前系统是否正在被内核调试器调试。如果存在,把异常处理的控制权转交给内核调试器,内核调试器获得权限后,会根据用户对异常处理的设置来确定是否要处理该异常。如果无法确定是否要处理该异常,就把权限交给用于,自行决定
- 如果不存在内核调试器,或者在第一次处理时选择不处理该异常,系统就会调用nt!RtlDispatchException函数,根据线程注册的结构化异常处理过成来处理该异常
- 如果RtlDispatchException函数没有处理该异常,系统会该调试器第2 次处理机会,此时调试器可以再次取得对异常的处理权。
- 如果不存在内核调试器,或者第2 次机会调试仍不处理,系统就会认定在这种情况下不能继续运行了,为了避免引起更加严重的、不可预知的错误,系统会直接调用。。。产生蓝屏错误
4.用户态的异常处理
当previousmode为usermode时,表示使用户模式下产生的异常。此时KiDispathException函数仍然会检测内核调试器是否存在。如果内核调试器存在,会优先把控制权交给内核调试器
SEH的概念及基本知识
现在我们已经基本了解了异常处理的基本过程。在没有调试器参与的情况下,系统主要依靠SEH机制(用户模式和内核模式下均可使用)和VEH机制(仅支持用户模式)进行异常处理。
SEH(结构化异常处理)是windows操作系统用于自身除错的一种机制,也是开发人员处理错误或者异常的强有力的武器,SEH是一种错误保护和修改机制,它告诉系统当程序运行出现异常或者错误时由谁来处理,给了应用程序一个改正错误的机会。从程序设计的角度来说,就是系统在终结程序之前给程序提供一个执行预先设定的回调函数的机会。
SEH相关数据结构
1.TEB结构
TEB(线程信息块),是保存线程基本信息的数据结构。在用户模式下,它位于TEB的头部,TEB是操作系统为了保存每个线程的私有数据创建的,如下。

可见,SEH指针在TEB的偏移0处,在x86平台的用户模式下,windows将FS段选择器指向当前线程的TEB数据,即SEH总是由fs:[0]指向的。而当线程运行在内核模式下时,Windows将FS段选择器指向内核中的KPCRB结构。
2.EXCEPTION_REGISTRATION_RECORD结构
TEB偏移量为0的EXCEPTION_REGISTRATION_RECORD主要用于描述线程异常处理过程的地址,多个该结构的链表描述了多个线程异常处理过程的嵌套关系。

3.EXCEPTION_RECORD结构和CONTEXT结构
这两个结构分别描述了异常发生的异常相关信息和线程状态信息。
4.EXCEPTON_POINTERS结构
当一个异常发生时,在没有调试器干预的情况下,操作系统会将异常信息转交给用户态的异常处理过程。实际上,由于同一个线程在用户态和内核态使用的是两个不同的栈,为了让用户态的异常处理程序能够访问与异常相关的数据,操作系统必须把与本次异常相关联的EXCEPTION_RECORD结构和CONTEXT结构放到用户态栈中,同时在栈中放置一个EXCEPTION_POINTERS结构,它包含两个指针,一个指向EXCEPTION_RECORD结构,另一个指向CONTEXT结构。

SEH处理程序的安装和卸载
由于fs:[0]总是指向当前异常处理程序的链表头,当程序中需要安装一个新的SEH异常处理程序时,只要填写一个新的EXCEPTION_REGISTRATION-RECORD结构,并将其插入该链表的头部即可。根据SEH的设计要求,它的作用范围与安装它的函数相同,SHE是基于栈帧的异常处理机制。
在安装SEH处理程序之前,需要准备一个符合SEH标准的回调函数,然后使用如下代码进行安装;

SEH 实例跟踪:
00401020 55 push ebp
00401021 8bec mov ebp, esp
00401023 6800104000 push 401000h
;压入异常处理程序的地址,即填充ERR结构的handler
00401028 64ff3500000000 push dword ptr fs:[0]
;压入当前ERR指针,即填充ERR结构的Next,此时ERR填充完毕
0040102f 64892500000000 mov dword ptr fs:[0], esp
;将当前ERR结构的指针放到fs:[0]指向的TIB结构中
00401036 33f6 xor esi, esi
;esi清零
00401038 8b06 mov eax, dword ptr [esi]
;读取线性地址0,产生异常后控制权转交给操作系统的异常处理代码
;系统处理完将跳转到当前的Handler处),继续执行
0040103a 6a00 push 0
0040103c 6800304000 push 403000h
00401041 680f304000 push 40300Fh
00401046 6a00 push 0
00401048 e813000000 call 00401060
0040104d 648b2500000000 mov esp, dword ptr fs:[0]
00401054 648f0500000000 pop dword ptr fs:[0];恢复原fs:[0]的内容
0040105b 8be5 mov esp, ebp
0040105d 5d pop ebp
0040105e c3 ret
重新加载程序执行到0x00401036处时,SEH异常回调函数安装完毕。
SEH异常处理程序原理及设计
用户态的异常是从ntdll!KiUserExceptionDispatcher函数开始的。此时,栈中有EXCEPTION_RECORD和CONTEXT两个结构。
该函数的主要流程可以用以下代码表示
KiUserExceptionDispatcher(PECEPTION_RECORD pExceptRec,CONTEXT *pContext)
{
DWORD retValue;
//RtlDispatchException如果在执行过程中发生意外情况,将不会返回
if(RtlDispatchException(pExceptRec,pContext))
//如果异常被处理且返回值为继续执行,那么使用修复的CONTEXT恢复执行,NtConinute不会返回
retVaule = NtConinute(pContext,0);
else
//如果异常没有被处理,将再次引发异常。注意第3个参数,FALSE表示第2次异常,给i函数不会返回
retValue = NtRaiseException(pException,pContext,FALSE);
//如果执行到这里,说明以上过程再次发生异常,那么就要标记该异常为不可继续执行
EXCEPTION_RECORD excptRec2;
excptRec2.ExceptionCode = retValue;
excptRec2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
excptRec2.ExceptionRecord = pExcptRec;
excptRec2.NumberParameters = 0;
RtlRaiseException(&excptRec2);
}
从上面的代码中可以看出,ntdll!RtlDispatchException函数用来具体分发异常,当异常被处理以后,会使用NtContinue服务恢复该线程的执行,如果异常没有被处理,就调用NtRaiseException函数引发第2次异常。
//异常标志定义
#define EXCEPTION_NONCONTINUABLE 0x1 //不可继续执行的异常
#define EXCEPTION_UNWINDING 0x2 //正在进行栈展开
#define EXCEPTION_EXIT_UNWIND 0x4 //正在退出栈展开
#define EXCEPTION_STACK_INVALID 0x8 //栈超出限制或未对齐
#define EXCEPTION_NESTED_CALL 0x10 //嵌套异常处理函数调用
#define EXCEPTION_TARGET_UNWIND 0x20 //目标帧展开
#define EXCEPTION_COLLIDED_UNWIND 0x40 //冲突异常处理函数调用
//MmExecutionOptionFlags on windows 7
#define MEM_EXECUTE_OPTION_DISABLE_EXCEPTIONCHAIN_CALIDATION 0x40
//ProcessInformationClass定义的一部分
#define ProcessExecuteFlags 34
typedef struct _DISPATCHER_CONTEXT
{
PEXCEPTION_REGISTRATION_REDORD registrationPointer;
} DISPATCHER_CONTEXT;
BOOLEAN __stdcall RtlDispatchException (EXCEPTION_RECORD pExcptRec,CONTEXT *pContext)
{
BOOLEAN Completion;
EXCEPTION_RECORD pExcptRec;
EXCEPTION_REGISTRATION_RECORD *registrationPointerForCheck;
EXCEPTION_REGISTRATION_RECORD *RegistrationPointer;
EXCEPTION_REGISTRATION_RECORD *NestedRegistration;
EXCEPTION_DISPOSITION Disposition;
EXCEPTION_RECORD ExceptioRecordl;
EXCEPTION_CONTEXT DispatcherContext;
ULONG ProcessExecuteOption;
ULONG StackBase ,StackLimit;
BOOLEAN IsSEHOPEnable;
NTSTATUS status;
Completion = FLASE;
//首先调用VEH异常处理例程,其返回值包括EXCEPTION_CONTINUE_EXECUTION(0xffffffff)
//和EXCEPTION_CONTINUE_SEARCH(0x0)两种情况
//这是从windows xp开始加入的异常处理方式
//如果返回值不是EXCEPTION_CONTINUE_SEARCH,就结束异常分发过程。
if(RtlCallVectoredExceptionHandlers(pExcptRec,pContext)!=EXCEPTION_CONTINUE_SEARCH)
{
Completion = TRUE;
}
else
{
//获取栈的内存范围
RtlpGetStackLimits(&StackLimit,&StcakBase);
ProcessException = 0;
//从fs:[0]获取SHE 链的头节点
RegistrationPointerForCheck = RtlGetRegistrationHead();
//默认假设SHEOP机制已经启用,这是一种对SEH链的安全性进行增强验证的机制
IsSEHOPEnable = TRUE;
//查询进程的ProcessExecuteFlags标志,决定是否进行SEHOP验证
status = zwQueryInformationProcess(NtCurrentProcess(),ProcessExecuteFlags,&ProcessExecuteOption,sizeof(ULONG),NULL);
//查询失败或者没有设置标志位时 ,进行SEHOP增强验证
//只有在确认查询到了禁用了SEHOP时,才不进行增强验证。
if(NT_SUCCESS(status)&&(ProcessExecuteOption&MEM_EXECUTE_OPTION_DISABLE_EXCEPTIONCHAIN_VALIDATION))
{
//若确实未开启SEHOP增强校验机制,设置此标志
IsSEHOPEnable = FALSE;
}
else{
//否则开始进行SEHOP(SEH覆写保护机制)
if(RegiatrationPointerForCheck == -1)
break;
//验证SEH链中各节点的有效性并遍历至最后一个节点
do{
//若发生以下情况,认为栈无效,此时不再执行基于栈的SHE处理
//1.SEH节点不在栈中

浙公网安备 33010602011771号