0707:更新驱动遍历、隐藏。更换图床
0712:更新驱动通信
0922:更新内存加载
内存加载驱动的代码放在评论区了
WDK和VS安装参考文章:X86内核笔记0配置双机调试环境
打开VS2017,新建项目选择Visual C++ -> Windwos Drivers -> Legacy -> Empty WDM Driver
右键SourceFiles目录,新建项。创建一个扩展名为C的C++文件。(不要用cpp扩展名)。文件名随意起,不是非要和项目名一样。
在.c文件中先引入头文件 ntifs.h
。
删除INF文件
如果引入头文件出现找不到头文件,就如下图设置一下SDK版本。
然后设置一些项目属性:
取消警告视为错误。
DriverSetting中将驱动平台改为Win7.(我们要在win7上做实验。)
驱动大体分为三种,分别是:NT式驱动、WDM式驱动、WDF式驱动(KWDF内核驱动,UWDF用户驱动)。
NT虚拟驱动,老式驱动,从WIN95开始使用NT式驱动。 若所开发的驱动不与硬件打交道,建议使用NT式驱动或WDM式驱动。如果NT式驱动出现了绑定设备的情况,该驱动将无法卸载。只能通过重启系统进行卸载。对于服务器来说重启很伤。
相对于NT式驱动来讲,WDM式驱动支持卸载(热拔插)。无需重启即可卸载。并且WDM式驱动对于NT式驱动进行了一些封装和优化。本质区别不大。
WDF式驱动相较前两种,其最大的意义是简化开发。不像NT与WDM驱动那么底层化。WDF式驱动将WDM式驱动进行了封装,做成了一套架构,使得开发驱动变得更简单。同时带来的弊端就是无法掌控底层。
由于开发简便,不容易蓝屏,所以公司开发驱动一般选用WDF式驱动。
想要学习WDF式驱动,需要了解COM相关知识。
只有系统中存在WDFLDR.sys驱动,我们编写的WDF驱动才可以跑起来。并且项目中需要一个inf文件,NT/WDM式驱动则不需要这个inf文件。
DriverEntry是我们写代码时的入口函数。其编译生成的sys文件真正的入口点并不是DriverEntry。在IDA中可以看到驱动真正的入口点函数是GsDriverEntry。其内部调用了我们的DriverEntry函数。
如果不想让编译器生成GsDriverEntry而是直接将入口函数设置为DriverEntry,可以按照下图设置。
如果想要打印字符串对象中的字符串,可以使用如下格式:
点击生成解决方案即可。若报一些格式错误,就删除一些特殊符号之类的东西。
使用InstDrv.exe加载驱动。使用DbgView.exe查看输出(必须选中监视核心,否则无法监视驱动层输出)。
通过调用函数DbgBreakPoint为驱动增加一个断点。这个函数相当于int 3指令。这样我们就可以在windbg中断下。并且Windbg会自动识别PE结构中的PDB路径,自动加载PDB文件识别出我们的源码。
如果windbg调试过程中,出现了刷屏的情况,执行以下命令可以关闭刷屏。
在成功断在我们的代码中后,在Windbg中查看驱动对象结构。
Type:驱动对象类型。
Size:驱动对象大小
DeviceObject:设备对象,我们这里没添加设备,因此是null
DriverStart:驱动文件基址,也就是PE格式中的ImageBase。通过db命令可以看到4D 5A。
DriverSize:驱动模块大小,也就是PE格式中的SizeOfImage。
DriverExtension:驱动扩展对象。使用dt命令查看该对象
DriverName:驱动名,也就是驱动的文件名前面加个\Driver\。这个名字是个字符串结构体。
查看该字符串结构:
HardwareDatabase:驱动服务注册表路径。前往注册表查看该路径,可以发现一个名为“hellodriver”的文件夹,这就是我们的驱动。
DriverInit :驱动入口点,也就是PE文件的AddressOfEntryPoint。
DriverUnload:驱动卸载函数地址。
加载驱动大体分为两种:服务加载和直接加载。实际应用中可以将两种方法都利用上。
这种方式实际上加载该驱动的进程,并不是调用API的进程。而是通过API向系统通知我要加载一个驱动。系统进程接收到通知后加入到系统中的一个队列。并由系统进程在某时某刻加载该驱动。
也就是这种方式是通知系统进程来进行加载。
调用ZwLoadDriver或NtLoadDriver加载一个已被正确注册的驱动。
这种方法需要我们自己手动去注册表内注册该驱动的相关信息。这样该驱动才可以被加载。
直接加载的方式在调用API后就会直接加载该驱动,所以该驱动的加载者就是调用该API的进程。相比于服务加载会留下痕迹。
编写两个驱动A和B,在A中定义全局变量值为100,打印A的地址pA。在B中打印pA的数据,观察是否与A中定义的相同。
A代码:
B代码:
在驱动中写代码与3环不同,一些数据类型及常用API也最好使用驱动开发专用的版本。这算是一种代码规范。
在驱动中,原数据类型int char等均被封装、重定义。在驱动开发中应使用如下数据类型:
绝大多数内核函数都会有一个返回值,类型为NTSTATUS
。该类型本质就是一个LONG。
如GetLastError这种取错误码的函数,取到的值其实就是NTSTATUS转化后的错误码。
常用的NTSTATUS宏如下,负数(大于0X80000000)的返回值为错误,大于等于0为成功
同时有一个宏用于判断返回值是成功还是失败:
在内核开发中,字符串不要定义为char* x = "xx",WDK为我们准备了一些字符串相关的API。
windows开发人员很喜欢使用链表,你可以在很多内核结构中看到LIST_ENTRY
成员。这就是链表节点结构。也是WDK中提供的一个官方链表结构。LIST_ENTRY是一个双向链表。
驱动对象中有一个成员名为DriverSection,其数据类型为未公开类型_KLDR_DATA_TABLE_ENTRY
的结构指针。该结构信息可以在WRK源码中搜索到。成员如下:
其中第一个成员InLoadOrderLinks是一个链表节点结构。由此可知,_KLDR_DATA_TABLE_ENTRY是一个双向链表。
编写如下代码,加载该驱动:
在windbg中断下后,输入命令dt ldr
查看自身节点结构:
输入命令dt _KLDR_DATA_TABLE_ENTRY 0x83f99850
查看下一个节点的结构。
可以发现很多属性都是0。这里我们需要知道一个常识,windows很多链表都喜欢将头部节点成员置位null,从第二个节点开始才是真正的有效数据。
继续执行命令dt _KLDR_DATA_TABLE_ENTRY 0x86344c98
查看下一个节点。
可以发现找到了ntoskrnl模块。经过多次重复寻找,可以发现这个链表是一个双向循环链表
。
将手动遍历的方法写入代码中:
加载驱动,观察输出内容,与PCHUNTER做对比,可以发现已经将全部驱动遍历出来了:(多一个因为吧空节点字符串也打印出来了,理应过滤掉)
完成对驱动模块的遍历后,我们要开始搞事情了。在实际攻防对抗中,无论是外挂开发者或是内核木马开发者,为了让自己的驱动悄悄的运行起来,都会对自身的驱动模块做一些隐藏操作。使其自身无法被检测到。而断链就是一个古老但又有效的一个方法。无论你将来从事攻或防,了解一些老技术都是必不可少的。
断链练习不要乱找一个驱动就开始断链, 否则可能对系统造成影响,这里我们拿HTTP.sys做断链练习,理论上如果断链成功,PCHUNTER中应该看不到HTTP.sys驱动。代码如下:
加载驱动后,dbgview成功打印,说明此时已经成功断链了。去PCHUNTER中观察一下效果:
可以发现HTTP.sys仍然在列表中,但是它变红了。这是因为PCHUNTER的遍历方法更健壮一些,不单单是通过链表遍历。还会涉及特征、文件等。有精力的话可以逆向一下PCHUNTER。
所以为了达到完美隐藏,我们需要改善一下我们的代码,抹掉驱动对象中的一些特征,在此之前,我们需要了解一下如何通过驱动名来获取驱动对象指针。
微软有一个未公开的导出函数ObReferenceObjectByName
。这个函数可以根据驱动名获取驱动对象指针。在WRK源码中可以搜索到。
其中ObjectType对象类型我们通过F12未找到该类型都有哪些值。这些类型是未公开的。同样在WRK中可以找到。此处我们使用一个名为IoDriverObjectType
的导出变量。
加载驱动,观察效果(记得重启,刚刚链表已经断掉HTTP了。):
可以看到HTTP已经彻底从PCHUNTER中消失了,也没有了红色HTTP的记录。我们的断链操作成功了。
对自身驱动模块进行断链,省去了遍历和取驱动对象的步骤,理论上是更简单的,我们尝试一下。
加载驱动,观察效果:
发现驱动无法被加载。这是因为系统调用完DriverEntry后仍需要做一些处理。而我们吧自己的驱动隐藏掉了,导致系统找不到我们的驱动没办法做后续处理。从而返回驱动加载失败。
所以我们需要在驱动成功加载后再进行断链操作,这里使用新线程+延迟执行的方法来规避。
再次尝试加载驱动,分别观察刚加载驱动时PCHUNTER的列表和延迟10秒后PCHUNTER的列表。
可以看到我们的驱动已经成功被加载了,并在10秒后做了断链隐藏处理。但目前仍有一个BUG,那就是卸载驱动时会提示“请求的控件对此服务无效。”。若想修复这个BUG,需要在卸载之前还原我们的驱动,将节点重新接入链表中并恢复被清空的属性。
驱动在实际使用中不可能从入口点一路执行到结束。 将驱动按功能分成模块, 需要时调用才是实际的应用。通过用户层与内核层的通信,可以让用户程序在需要时调用驱动的特定功能。无论是攻击方或是防守方,驱动通信都是一个关键的战场。
这里的常规通信使用设备交互的方式,其类似与WIN32的消息和回调函数的组合。在内核中,消息被封装为一个结构体IRP(I/O Request Packae)。设备对象可以接收IRP数据从而实现通信。
用户应用想要向驱动发起通信,其本质是向驱动所绑定的设备发起通信,再由设备向下分发。所以我们需要首先创建一个设备:
3环想要打开我们的设备无法直接使用\\Device\\XXX
这种设备名,我们需要指定一个符号链接(别名)用于3环的访问。
在用户层,我们每次调用CreateFile、OpenFIle、DeleteFile、CloseHandle等API时,都会向0环发送一个消息,这个消息成为IRP数据包。这些API称为设备操作API
。如:当调用CreateFile时,会向内核层发送一个名为IRP_MJ_CREATE
的打开设备的IRP消息。其他常用IRP类型如下:
在用户层我们使用WIN32开发GUI时,通过回调函数
来处理窗口消息
。 而在内核层,我们通过派遣函数
来处理IRP消息
。只是换了个名字而已,本质一样。
在驱动对象中,有个属性名为MajorFunction
,这是个数组,每个元素都是一个函数指针,对应了各种类型IRP的派遣函数。
派遣函数格式如下:
驱动现在已经可以接收IRP消息了,那么我们在3环中就可以发送一个IRP消息了。使用DeviceIoControl
函数来向设备发送一个IRP_MJ_DEVICE_CONTROL
类型的IRP消息。(也可以用CreateFile进行通信,此处不做演示。)
3环的代码已经写完了,我们需要对IRP中的控制码再做一个详细的分支处理:
在驱动卸载时要删除设备和符号链接,否则会一直存在内核空间中,并且无法创建同名设备。
先创建设备,后创建符号链接。所以删除时先删除符号链接,再卸载设备。
如果代码没问题,执行后的效果如下:
前文提到了通过断链的方式隐藏驱动,但依然会有大量的残留。为了更加隐蔽的加载我们的驱动,需要使用内存加载的方式。相比于三环中的傀儡进程技术,驱动层写法多出了Cookie修复的步骤并且不需要修复TLS和延迟导入表。这种方式同样可以绕过驱动签名校验来强制加载我们的驱动。
内存加载驱动大体上分为5个步骤,其中每个步骤都需要我们有扎实的PE知识。
拉伸PE数据。
修复重定位。
修复IAT
修复Cookie验证
修复SEH异常
执行入口点函数
#include <ntimage.h>
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath){
/
/
代码
return
STATUS_UNSUCCESSFUL;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath){
/
/
代码
return
STATUS_UNSUCCESSFUL;
}
void UnloadDriver(PDRIVER_OBJECT driver);
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
DbgBreakPoint();
/
/
相当于 __asm{
int
3
}
DbgPrint(
"驱动加载了。\r\n"
);
/
/
驱动的打印函数,相当于
3
环的printf
DriverObject
-
>DriverUnload
=
UnloadDriver;
/
/
为驱动指定卸载函数
return
STATUS_SUCCESS;
}
/
/
驱动卸载函数
void UnloadDriver(PDRIVER_OBJECT driver) {
DbgPrint(
"驱动停止了。\r\n"
);
}
void UnloadDriver(PDRIVER_OBJECT driver);
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
DbgBreakPoint();
/
/
相当于 __asm{
int
3
}
DbgPrint(
"驱动加载了。\r\n"
);
/
/
驱动的打印函数,相当于
3
环的printf
DriverObject
-
>DriverUnload
=
UnloadDriver;
/
/
为驱动指定卸载函数
return
STATUS_SUCCESS;
}
/
/
驱动卸载函数
void UnloadDriver(PDRIVER_OBJECT driver) {
DbgPrint(
"驱动停止了。\r\n"
);
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING pReg) {
DbgPrint(
"-------%wZ--------"
,pReg);
/
/
传入字符串对象指针。
return
STATUS_SUCCESS;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING pReg) {
DbgPrint(
"-------%wZ--------"
,pReg);
/
/
传入字符串对象指针。
return
STATUS_SUCCESS;
}
kd> ed nt!Kd_SXS_Mask
0
;ed nt!Kd_FUSION_Mask
0
kd> ed nt!Kd_SXS_Mask
0
;ed nt!Kd_FUSION_Mask
0
kd> dt _DRIVER_EXTENSION
0x8831b790
ntdll!_DRIVER_EXTENSION
+
0x000
DriverObject :
0x8831b6e8
_DRIVER_OBJECT
+
0x004
AddDevice : (null)
+
0x008
Count :
0
+
0x00c
ServiceKeyName : _UNICODE_STRING
"hellodriver"
+
0x014
ClientDriverExtension : (null)
+
0x018
FsFilterCallbacks : (null)
kd> dt _DRIVER_EXTENSION
0x8831b790
ntdll!_DRIVER_EXTENSION
+
0x000
DriverObject :
0x8831b6e8
_DRIVER_OBJECT
+
0x004
AddDevice : (null)
+
0x008
Count :
0
+
0x00c
ServiceKeyName : _UNICODE_STRING
"hellodriver"
+
0x014
ClientDriverExtension : (null)
+
0x018
FsFilterCallbacks : (null)
kd> dt _UNICODE_STRING
8831b6e8
+
1c
ntdll!_UNICODE_STRING
"\Driver\hellodriver"
+
0x000
Length :
0x26
/
/
字符串长度
+
0x002
MaximumLength :
0x26
/
/
字符串最大长度
+
0x004
Buffer
:
0x87fc36c8
"\Driver\hellodriver"
/
/
字符串内容
kd> dt _UNICODE_STRING
8831b6e8
+
1c
ntdll!_UNICODE_STRING
"\Driver\hellodriver"
+
0x000
Length :
0x26
/
/
字符串长度
+
0x002
MaximumLength :
0x26
/
/
字符串最大长度
+
0x004
Buffer
:
0x87fc36c8
"\Driver\hellodriver"
/
/
字符串内容
void UnloadDriver(PDRIVER_OBJECT driver);
UINT32 i
=
100
;
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
DbgPrint(
"i addr = %08x\r\n"
, &i);
DriverObject
-
>DriverUnload
=
UnloadDriver;
return
STATUS_SUCCESS;
}
/
/
驱动卸载函数
void UnloadDriver(PDRIVER_OBJECT driver) {
DbgPrint(
"驱动停止了。\r\n"
);
}
void UnloadDriver(PDRIVER_OBJECT driver);
UINT32 i
=
100
;
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
DbgPrint(
"i addr = %08x\r\n"
, &i);
DriverObject
-
>DriverUnload
=
UnloadDriver;
return
STATUS_SUCCESS;
}
/
/
驱动卸载函数
void UnloadDriver(PDRIVER_OBJECT driver) {
DbgPrint(
"驱动停止了。\r\n"
);
}
void UnloadDriver(PDRIVER_OBJECT driver);
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
PUINT32 p
=
(PUINT32)
0x8e315000
;
DbgPrint(
"i value = %d\r\n"
,
*
p);
DriverObject
-
>DriverUnload
=
UnloadDriver;
return
STATUS_SUCCESS;
}
/
/
驱动卸载函数
void UnloadDriver(PDRIVER_OBJECT driver) {
DbgPrint(
"驱动停止了。\r\n"
);
}
void UnloadDriver(PDRIVER_OBJECT driver);
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
PUINT32 p
=
(PUINT32)
0x8e315000
;
DbgPrint(
"i value = %d\r\n"
,
*
p);
DriverObject
-
>DriverUnload
=
UnloadDriver;
return
STATUS_SUCCESS;
}
/
/
驱动卸载函数
void UnloadDriver(PDRIVER_OBJECT driver) {
DbgPrint(
"驱动停止了。\r\n"
);
}
UINT8,PUINT8
-
> unsigned char
UINT16,PUINT16
-
> unsigned short
UINT32,PUINT32
-
> unsigned
int
UINT64,PUINT64
-
> unsigned __int64
INT8,PINT8
-
> char
INT16,PINT16
-
> short
INT32,PINT32
-
>
int
INT64,PINT64
-
> __int64
LONG32,PLONG32
-
>
int
ULONG32,PULONG32
-
> unsigned
int
DWORD32,PDWRD32
-
>
int
UINT8,PUINT8
-
> unsigned char
UINT16,PUINT16
-
> unsigned short
UINT32,PUINT32
-
> unsigned
int
UINT64,PUINT64
-
> unsigned __int64
INT8,PINT8
-
> char
INT16,PINT16
-
> short
INT32,PINT32
-
>
int
INT64,PINT64
-
> __int64
LONG32,PLONG32
-
>
int
ULONG32,PULONG32
-
> unsigned
int
DWORD32,PDWRD32
-
>
int
STATUS_SEVERITY_SUCCESS
0x0
STATUS_SEVERITY_INFORMATIONAL
0x1
STATUS_SEVERITY_WARNING
0x2
STATUS_SEVERITY_ERROR
0x3
STATUS_UNSUCCESSFUL
0xC0000001
STATUS_ACCESS_VIOLATION
0xC0000005
STATUS_SEVERITY_SUCCESS
0x0
STATUS_SEVERITY_INFORMATIONAL
0x1
STATUS_SEVERITY_WARNING
0x2
STATUS_SEVERITY_ERROR
0x3
STATUS_UNSUCCESSFUL
0xC0000001
STATUS_ACCESS_VIOLATION
0xC0000005
NT_SUCCESS(NTSATUS类型参数)
/
/
NT_SUCCESS(NTSATUS类型参数)
/
/
UNICODE_STRING uStr
=
{
0
};
/
/
定义一个
unicode
字符串,类型为UNICODE_STRING
STRING aStr
=
{
0
};
/
/
定义一个ascii字符串,类型为STRING
ANSI_STRING aStr
=
{
0
};
/
/
所有ANSI与直接STRING 作用相同
UNICODE_STRING uStr
=
{
0
};
/
/
定义一个
unicode
字符串,类型为UNICODE_STRING
STRING aStr
=
{
0
};
/
/
定义一个ascii字符串,类型为STRING
ANSI_STRING aStr
=
{
0
};
/
/
所有ANSI与直接STRING 作用相同
RtlInitUnicodeString(&uStr,L
"unicode string"
);
/
/
初始化
unicode
字符串,为其赋值。不会申请内存。
RtlInitString(&aStr,
"ascii string"
);
/
/
初始化ascii字符串,为其赋值,不会申请内存。
RtlInitAnsiString(&aStr,
"ascii string"
);
/
/
初始化ascii字符串,为其赋值,不会申请内存。
RtlInitUnicodeString(&uStr,L
"unicode string"
);
/
/
初始化
unicode
字符串,为其赋值。不会申请内存。
RtlInitString(&aStr,
"ascii string"
);
/
/
初始化ascii字符串,为其赋值,不会申请内存。
RtlInitAnsiString(&aStr,
"ascii string"
);
/
/
初始化ascii字符串,为其赋值,不会申请内存。
RtlAnsiStringToUnicodeString(&uStr,&aStr,true);
/
/
将ascii字符串转为
unicode
字符串,无需为
unicode
字符串做初始化,第三个参数为true则自动申请内存。为false则不申请,仅修改
unicode
现有空间。若为true,则需要手动释放字符串内存。
RtlUnicodeStringToAnsiString();
/
/
将
unicode
字符串转为ascii字符串,用法与上面相同。
RtlAnsiStringToUnicodeString(&uStr,&aStr,true);
/
/
将ascii字符串转为
unicode
字符串,无需为
unicode
字符串做初始化,第三个参数为true则自动申请内存。为false则不申请,仅修改
unicode
现有空间。若为true,则需要手动释放字符串内存。
RtlUnicodeStringToAnsiString();
/
/
将
unicode
字符串转为ascii字符串,用法与上面相同。
RtlFreeUnicodeString();
/
/
释放
unicode
字符串内存,当字符串初始化中为其分配了内存时,需要释放内存。
RtlFreeAnsiString();
/
/
释放ascii字符串内存,当字符串初始化中为其分配了内存时,需要释放内存。
RtlFreeUnicodeString();
/
/
释放
unicode
字符串内存,当字符串初始化中为其分配了内存时,需要释放内存。
RtlFreeAnsiString();
/
/
释放ascii字符串内存,当字符串初始化中为其分配了内存时,需要释放内存。
char aStr[
0x1000
]
=
{
0
};
RtlStringCbPrintfA(aStr,
0x1000
,
"%d---%s"
,
123
,
"test"
);
/
/
参数
1
: Ascii字符串指针
wchar uStr[
0x1000
]
=
{
0
};
RtlStringCbPrintfW(uStr,
0x1000
, L
"%d---%s"
,
123
, L
"test"
);
/
/
参数
1
:
Unicode
字符串指针
char aStr[
0x1000
]
=
{
0
};
RtlStringCbPrintfA(aStr,
0x1000
,
"%d---%s"
,
123
,
"test"
);
/
/
参数
1
: Ascii字符串指针
wchar uStr[
0x1000
]
=
{
0
};
RtlStringCbPrintfW(uStr,
0x1000
, L
"%d---%s"
,
123
, L
"test"
);
/
/
参数
1
:
Unicode
字符串指针
RtlCompareUnicodeString(&uStr1,&uStr2,TRUE);
/
/
比较两个
unicode
字符串是否相等,true忽略大小写
RtlCompareString
/
/
比较两个ascii字符串是否相等
RtlCompareUnicodeString(&uStr1,&uStr2,TRUE);
/
/
比较两个
unicode
字符串是否相等,true忽略大小写
RtlCompareString
/
/
比较两个ascii字符串是否相等
ExAllocatePool(
type
,size);
/
/
type
:内存类型,PagePool和NonPagePool,分别为分页内存和非分页内存。
/
/
分页内存:后面章节会详细说,暂时理解为不可执行的内存
/
/
非分页内存:后面章节会详细说,暂时理解为可执行的内存 通常填NonPagePool,对应属性为PTE的XD
/
NX位。
ExAllocatePoolWithTag(
type
,size,tag);
/
/
tag:内存标志,四个字节最多,如
'test'
,为申请的内存起个名字。用单引号包含,内部最终转为
16
进制数据。
ExAllocatePool(
type
,size);
/
/
type
:内存类型,PagePool和NonPagePool,分别为分页内存和非分页内存。
/
/
分页内存:后面章节会详细说,暂时理解为不可执行的内存
/
/
非分页内存:后面章节会详细说,暂时理解为可执行的内存 通常填NonPagePool,对应属性为PTE的XD
/
NX位。
ExAllocatePoolWithTag(
type
,size,tag);
/
/
tag:内存标志,四个字节最多,如
'test'
,为申请的内存起个名字。用单引号包含,内部最终转为
16
进制数据。
RtlFillMemory(pointer,length,value);
/
/
相当于memset
RtlEqualMemory(pointer,Source,Length)
/
/
相当于memcmp结果取反
RtlMoveMemory(pointer,Source,Length)
/
/
相当于memmove
RtlCopyMemory(pointer,Source,Length)
/
/
相当于memcpy
RtlZeroMemory(pointer,Length)
/
/
相当于memset第二参数为
0.
RtlFillMemory(pointer,length,value);
/
/
相当于memset
RtlEqualMemory(pointer,Source,Length)
/
/
相当于memcmp结果取反
RtlMoveMemory(pointer,Source,Length)
/
/
相当于memmove
RtlCopyMemory(pointer,Source,Length)
/
/
相当于memcpy
RtlZeroMemory(pointer,Length)
/
/
相当于memset第二参数为
0.
ExFreePool(pointer);
/
/
释放内存
ExFreePool(pointer);
/
/
释放内存
/
/
驱动代码中的延迟不可以使用Sleep,而是KeDelayExecutionThread
LARGE_INTEGER li
=
{
0
};
/
/
时长结构。
li.QuadPart
=
-
10000
*
5000
;
/
/
时间单位 负数代表相对时间 正数代表绝对时间。
5000
代表
5
秒。
KeDelayExecutionThread(KernelMode,FALSE,&li);
/
/
第一个参数:延迟模式,我们这里选内核模式
/
/
第二个参数:强制唤醒。如果为FALSE,那么休眠时间未结束前,不会被唤醒。
/
/
第三个参数:延迟时长。
/
/
驱动代码中的延迟不可以使用Sleep,而是KeDelayExecutionThread
LARGE_INTEGER li
=
{
0
};
/
/
时长结构。
li.QuadPart
=
-
10000
*
5000
;
/
/
时间单位 负数代表相对时间 正数代表绝对时间。
5000
代表
5
秒。
KeDelayExecutionThread(KernelMode,FALSE,&li);
/
/
第一个参数:延迟模式,我们这里选内核模式
/
/
第二个参数:强制唤醒。如果为FALSE,那么休眠时间未结束前,不会被唤醒。
/
/
第三个参数:延迟时长。
/
/
线程函数
VOID myThreadFun(_In_ PVOID StartContext) {
/
/
线程函数代码
}
HANDLE tHandle
=
NULL;
NTSTATUS tRet
=
PsCreateSystemThread(&tHandle,THREAD_ALL_ACCESS,NULL,NULL,NULL, myThreadFun,NULL);
/
/
最后一个参数是线程函数启动参数。
if
(NT_SUCCESS(tRet)){
ZwClose(tHandle);
/
/
相当于CloseHandle
}
/
/
线程函数
VOID myThreadFun(_In_ PVOID StartContext) {
/
/
线程函数代码
}
HANDLE tHandle
=
NULL;
NTSTATUS tRet
=
PsCreateSystemThread(&tHandle,THREAD_ALL_ACCESS,NULL,NULL,NULL, myThreadFun,NULL);
/
/
最后一个参数是线程函数启动参数。
if
(NT_SUCCESS(tRet)){
ZwClose(tHandle);
/
/
相当于CloseHandle
}
typedef struct _Monster {
/
/
定义一个结构体,成员包含节点结构。这样该结构体也可以作为节点。
UINT32
ID
;
LIST_ENTRY node;
UINT32 hp;
UINT32 level;
UNICODE_STRING name;
}Monster,
*
PMonster;
Monster m1
=
{
0
};
InitializeListHead(&m1.node);
/
/
初始化链表,防止出现垃圾数据作为指针的情况。
IsListEmpty(&m1.node);
/
/
判断整条链表是否为空,传入整条链表中任意一个节点即可。
Monster m2
=
{
0
};
InsertHeadList(&m1.node, &m2.node);
/
/
将m2节点插入至链表头部。
Monster m3
=
{
0
};
InsertTailList(&m1.node, &m3.node);
/
/
将m3节点插入至链表尾部。
RemoveHeadList(&m2.node);
/
/
将整条链表的头部节点移除,传入任一节点即可。
RemoveTailList(&m2.node);
/
/
将整条链表的尾部节点移除,传入任一节点即可。
RemoveEntryList(&m3.node);
/
/
将指定节点移除,断链。
/
/
通过FLINK找到Monster,架设要找到m2下一个节点
PMonster pm
=
(PMonster)((UCHAR)m2.node.Flink
-
((UCHAR)(&m2.node)
-
(UCHAR)&m2));
/
/
计算出ListEntry结构相对于Monster结构的偏移,用Flink减去该偏移得到m3的地址。
typedef struct _Monster {
/
/
定义一个结构体,成员包含节点结构。这样该结构体也可以作为节点。
UINT32
ID
;
LIST_ENTRY node;
UINT32 hp;
UINT32 level;
UNICODE_STRING name;
}Monster,
*
PMonster;
Monster m1
=
{
0
};
InitializeListHead(&m1.node);
/
/
初始化链表,防止出现垃圾数据作为指针的情况。
IsListEmpty(&m1.node);
/
/
判断整条链表是否为空,传入整条链表中任意一个节点即可。
Monster m2
=
{
0
};
InsertHeadList(&m1.node, &m2.node);
/
/
将m2节点插入至链表头部。
Monster m3
=
{
0
};
InsertTailList(&m1.node, &m3.node);
/
/
将m3节点插入至链表尾部。
RemoveHeadList(&m2.node);
/
/
将整条链表的头部节点移除,传入任一节点即可。
RemoveTailList(&m2.node);
/
/
将整条链表的尾部节点移除,传入任一节点即可。
RemoveEntryList(&m3.node);
/
/
将指定节点移除,断链。
/
/
通过FLINK找到Monster,架设要找到m2下一个节点
PMonster pm
=
(PMonster)((UCHAR)m2.node.Flink
-
((UCHAR)(&m2.node)
-
(UCHAR)&m2));
/
/
计算出ListEntry结构相对于Monster结构的偏移,用Flink减去该偏移得到m3的地址。
typedef struct _Monster {
UINT32
id
;
UINT32 hp;
UINT32 level;
UNICODE_STRING name;
}Monster,
*
PMonster;
/
/
树节点比较函数
RTL_GENERIC_COMPARE_RESULTS NTAPI myCmpFunc(_In_ struct _RTL_GENERIC_TABLE
*
Table,_In_ PVOID FirstStruct,_In_ PVOID SecondStruct) {
PMonster m1
=
(PMonster)FirstStruct;
/
/
内部强转为自己需要的结构体
PMonster m2
=
(PMonster)SecondStruct;
if
(m1
-
>
id
=
=
m2
-
>
id
) {
/
/
判断方法自己指定,我这里按照
ID
判断两个节点的大小关系
return
GenericEqual;
}
return
m1
-
>
id
> m2
-
>
id
? GenericGreaterThan : GenericLessThan;
}
/
/
节点新建函数
VOID NTAPI myAllocFunc( _In_ struct _RTL_GENERIC_TABLE
*
Table, _In_ CLONG ByteSize ) {
ExAllocatePool(NonPagedPool,ByteSize);
/
/
申请内存
}
/
/
节点删除函数
VOID NTAPI myFreeFunc( _In_ struct _RTL_GENERIC_TABLE
*
Table, _In_ __drv_freesMem(Mem) _Post_invalid_ PVOID
Buffer
) {
ExFreePool(
Buffer
);
/
/
释放该节点申请出来的内存
}
Monster m1
=
{
0
,
100
,
10
,L
"monster 1"
};
Monster m2
=
{
1
,
100
,
10
,L
"monster 2"
};
Monster m3
=
{
2
,
100
,
10
,L
"monster 3"
};
RTL_GENERIC_TABLE table
=
{
0
};
/
/
初始化二叉树
RtlInitializeGenericTable(&table, myCmpFunc, myAllocFunc, myFreeFunc,NULL);
BOOLEAN isNewEle
=
FALSE;
/
/
插入
/
更新节点,将节点强转为void
*
,isNewEle接收该节点是否为新加加点或已存在节点。通过myCmpFunc来判断两个节点是否为同一个节点。 在插入节点时,会调用myAllocFunc为节点重新分配一个内存并将数据拷贝过去。
RtlInsertElementGenericTable(&table, (PVOID)&m1,sizeof(m1),&isNewEle);
RtlInsertElementGenericTable(&table, (PVOID)&m2,sizeof(m1),&isNewEle);
RtlInsertElementGenericTable(&table, (PVOID)&m3,sizeof(m1),&isNewEle);
/
/
查找节点,lookupM只需要赋值
id
属性,查找也会根据这个
id
去对比是否相同。返回查找到的结点指针
Monster lookupM
=
{
0
,
0
,
0
,
0
};
PMonster lookupResult
=
(PMonster)RtlLookupElementGenericTable(&table,&lookupM);
/
/
删除节点,也是根据
id
删除。删除后会自动调用myFreeFunc释放内存。
RtlDeleteElementGenericTable(&table, &lookupM);
/
/
取节点个数
ULONG nodeNum
=
RtlNumberGenericTableElements(&table);
ULONG nodeNum
=
RtlNumberGenericTableElementsAvl(&table);
/
/
安全函数,防止一边加节点一边读节点
/
/
遍历节点,key用于取下一个节点,key为null时,取第一个节点。 返回值为下一个节点,同时自动更新key指向返回值所属节点。返回值为null说明遍历结束。
PVOID key
=
NULL;
PMonster pm
=
(PMonster)RtlEnumerateGenericTableWithoutSplaying(&table, &key);
while
(pm!
=
NULL) {
DbgPrint(pm
-
>name.
Buffer
);
pm
=
(PMonster)RtlEnumerateGenericTableWithoutSplaying(&table, &key);
}
/
/
二叉树用完(如驱动卸载),要将二叉树内所有节点销毁掉,防止内存泄漏。
typedef struct _Monster {
UINT32
id
;
UINT32 hp;
UINT32 level;
UNICODE_STRING name;
}Monster,
*
PMonster;
/
/
树节点比较函数
RTL_GENERIC_COMPARE_RESULTS NTAPI myCmpFunc(_In_ struct _RTL_GENERIC_TABLE
*
Table,_In_ PVOID FirstStruct,_In_ PVOID SecondStruct) {
PMonster m1
=
(PMonster)FirstStruct;
/
/
内部强转为自己需要的结构体
PMonster m2
=
(PMonster)SecondStruct;
if
(m1
-
>
id
=
=
m2
-
>
id
) {
/
/
判断方法自己指定,我这里按照
ID
判断两个节点的大小关系
return
GenericEqual;
}
return
m1
-
>
id
> m2
-
>
id
? GenericGreaterThan : GenericLessThan;
}
/
/
节点新建函数
VOID NTAPI myAllocFunc( _In_ struct _RTL_GENERIC_TABLE
*
Table, _In_ CLONG ByteSize ) {
ExAllocatePool(NonPagedPool,ByteSize);
/
/
申请内存
}
/
/
节点删除函数
VOID NTAPI myFreeFunc( _In_ struct _RTL_GENERIC_TABLE
*
Table, _In_ __drv_freesMem(Mem) _Post_invalid_ PVOID
Buffer
) {
ExFreePool(
Buffer
);
/
/
释放该节点申请出来的内存
}
Monster m1
=
{
0
,
100
,
10
,L
"monster 1"
};
Monster m2
=
{
1
,
100
,
10
,L
"monster 2"
};
Monster m3
=
{
2
,
100
,
10
,L
"monster 3"
};
RTL_GENERIC_TABLE table
=
{
0
};
/
/
初始化二叉树
RtlInitializeGenericTable(&table, myCmpFunc, myAllocFunc, myFreeFunc,NULL);
BOOLEAN isNewEle
=
FALSE;
/
/
插入
/
更新节点,将节点强转为void
*
,isNewEle接收该节点是否为新加加点或已存在节点。通过myCmpFunc来判断两个节点是否为同一个节点。 在插入节点时,会调用myAllocFunc为节点重新分配一个内存并将数据拷贝过去。
RtlInsertElementGenericTable(&table, (PVOID)&m1,sizeof(m1),&isNewEle);
RtlInsertElementGenericTable(&table, (PVOID)&m2,sizeof(m1),&isNewEle);
RtlInsertElementGenericTable(&table, (PVOID)&m3,sizeof(m1),&isNewEle);
/
/
查找节点,lookupM只需要赋值
id
属性,查找也会根据这个
id
去对比是否相同。返回查找到的结点指针
Monster lookupM
=
{
0
,
0
,
0
,
0
};
PMonster lookupResult
=
(PMonster)RtlLookupElementGenericTable(&table,&lookupM);
/
/
删除节点,也是根据
id
删除。删除后会自动调用myFreeFunc释放内存。
RtlDeleteElementGenericTable(&table, &lookupM);
/
/
取节点个数
ULONG nodeNum
=
RtlNumberGenericTableElements(&table);
ULONG nodeNum
=
RtlNumberGenericTableElementsAvl(&table);
/
/
安全函数,防止一边加节点一边读节点
/
/
遍历节点,key用于取下一个节点,key为null时,取第一个节点。 返回值为下一个节点,同时自动更新key指向返回值所属节点。返回值为null说明遍历结束。
PVOID key
=
NULL;
PMonster pm
=
(PMonster)RtlEnumerateGenericTableWithoutSplaying(&table, &key);
while
(pm!
=
NULL) {
DbgPrint(pm
-
>name.
Buffer
);
pm
=
(PMonster)RtlEnumerateGenericTableWithoutSplaying(&table, &key);
}
/
/
二叉树用完(如驱动卸载),要将二叉树内所有节点销毁掉,防止内存泄漏。
typedef struct _KLDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
ULONG __Undefined1;
ULONG __Undefined2;
ULONG __Undefined3;
ULONG NonPagedDebugInfo;
ULONG DllBase;
ULONG EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT __Undefined5;
ULONG __Undefined6;
ULONG CheckSum;
ULONG TimeDateStamp;
} KLDR_DATA_TABLE_ENTRY,
*
PKLDR_DATA_TABLE_ENTRY;
typedef struct _KLDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
ULONG __Undefined1;
ULONG __Undefined2;
ULONG __Undefined3;
ULONG NonPagedDebugInfo;
ULONG DllBase;
ULONG EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT __Undefined5;
ULONG __Undefined6;
ULONG CheckSum;
ULONG TimeDateStamp;
} KLDR_DATA_TABLE_ENTRY,
*
PKLDR_DATA_TABLE_ENTRY;
typedef struct _KLDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
ULONG __Undefined1;
ULONG __Undefined2;
ULONG __Undefined3;
ULONG NonPagedDebugInfo;
ULONG DllBase;
ULONG EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT __Undefined5;
ULONG __Undefined6;
ULONG CheckSum;
ULONG TimeDateStamp;
} KLDR_DATA_TABLE_ENTRY,
*
PKLDR_DATA_TABLE_ENTRY;
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
DbgBreakPoint();
KLDR_DATA_TABLE_ENTRY
*
ldr
=
DriverObject
-
>DriverSection;
DriverObject
-
>DriverUnload
=
UnloadDriver;
return
STATUS_SUCCESS;
}
typedef struct _KLDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
ULONG __Undefined1;
ULONG __Undefined2;
ULONG __Undefined3;
ULONG NonPagedDebugInfo;
ULONG DllBase;
ULONG EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT __Undefined5;
ULONG __Undefined6;
ULONG CheckSum;
ULONG TimeDateStamp;
} KLDR_DATA_TABLE_ENTRY,
*
PKLDR_DATA_TABLE_ENTRY;
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
DbgBreakPoint();
KLDR_DATA_TABLE_ENTRY
*
ldr
=
DriverObject
-
>DriverSection;
DriverObject
-
>DriverUnload
=
UnloadDriver;
return
STATUS_SUCCESS;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
PKLDR_DATA_TABLE_ENTRY selfNode
=
DriverObject
-
>DriverSection;
PKLDR_DATA_TABLE_ENTRY preNode
=
selfNode;
UINT32 index
=
1
;
do {
DbgPrint(
"[db] %d driver name = %wZ \r\n"
, index
+
+
,&preNode
-
>BaseDllName);
preNode
=
preNode
-
>InLoadOrderLinks.Flink;
}
while
(preNode !
=
selfNode);
DriverObject
-
>DriverUnload
=
UnloadDriver;
return
STATUS_SUCCESS;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
PKLDR_DATA_TABLE_ENTRY selfNode
=
DriverObject
-
>DriverSection;
PKLDR_DATA_TABLE_ENTRY preNode
=
selfNode;
UINT32 index
=
1
;
do {
DbgPrint(
"[db] %d driver name = %wZ \r\n"
, index
+
+
,&preNode
-
>BaseDllName);
preNode
=
preNode
-
>InLoadOrderLinks.Flink;
}
while
(preNode !
=
selfNode);
DriverObject
-
>DriverUnload
=
UnloadDriver;
return
STATUS_SUCCESS;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
PKLDR_DATA_TABLE_ENTRY selfNode
=
DriverObject
-
>DriverSection;
PKLDR_DATA_TABLE_ENTRY preNode
=
selfNode;
UNICODE_STRING httpName
=
{
0
};
RtlInitUnicodeString(&httpName,L
"HTTP.sys"
);
do {
if
(preNode
-
>BaseDllName.Length !
=
0
/
/
过滤空字符串
&& RtlCompareUnicodeString(&preNode
-
>BaseDllName,&httpName,TRUE)
=
=
0
) {
DbgPrint(
"%wZ\r\n"
, &preNode
-
>BaseDllName);
RemoveEntryList(preNode);
break
;
}
preNode
=
preNode
-
>InLoadOrderLinks.Flink;
}
while
(preNode !
=
selfNode);
DriverObject
-
>DriverUnload
=
UnloadDriver;
return
STATUS_SUCCESS;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
PKLDR_DATA_TABLE_ENTRY selfNode
=
DriverObject
-
>DriverSection;
PKLDR_DATA_TABLE_ENTRY preNode
=
selfNode;
UNICODE_STRING httpName
=
{
0
};
RtlInitUnicodeString(&httpName,L
"HTTP.sys"
);
do {
if
(preNode
-
>BaseDllName.Length !
=
0
/
/
过滤空字符串
&& RtlCompareUnicodeString(&preNode
-
>BaseDllName,&httpName,TRUE)
=
=
0
) {
DbgPrint(
"%wZ\r\n"
, &preNode
-
>BaseDllName);
RemoveEntryList(preNode);
break
;
}
preNode
=
preNode
-
>InLoadOrderLinks.Flink;
}
while
(preNode !
=
selfNode);
DriverObject
-
>DriverUnload
=
UnloadDriver;
return
STATUS_SUCCESS;
}
/
/
NTKERNELAPI是一个宏,用于指定内核模块中的导出函数。
NTKERNELAPI NTSTATUS ObReferenceObjectByName(
__in PUNICODE_STRING ObjectName,
/
/
驱动对象名,如HTTP.sys的驱动对象名就是\Driver\HTTP
__in ULONG Attributes,
/
/
权限,给一个FILE_ALL_ACCESS即可。
__in_opt PACCESS_STATE AccessState,
/
/
opt为可选参数,直接写NULL
__in_opt ACCESS_MASK DesiredAccess,
/
/
opt为可选参数,直接写NULL
__in POBJECT_TYPE ObjectType,
/
/
对象类型
__in KPROCESSOR_MODE AccessMode,
/
/
访问模式,有个枚举是_MODE,里面有个值是KernelMode,填写即可。
__inout_opt PVOID ParseContext,
/
/
opt为可选参数,直接写NULL
__out PVOID
*
Object
/
/
驱动对象二级指针
);
/
/
NTKERNELAPI是一个宏,用于指定内核模块中的导出函数。
NTKERNELAPI NTSTATUS ObReferenceObjectByName(
__in PUNICODE_STRING ObjectName,
/
/
驱动对象名,如HTTP.sys的驱动对象名就是\Driver\HTTP
__in ULONG Attributes,
/
/
权限,给一个FILE_ALL_ACCESS即可。
__in_opt PACCESS_STATE AccessState,
/
/
opt为可选参数,直接写NULL
__in_opt ACCESS_MASK DesiredAccess,
/
/
opt为可选参数,直接写NULL
__in POBJECT_TYPE ObjectType,
/
/
对象类型
__in KPROCESSOR_MODE AccessMode,
/
/
访问模式,有个枚举是_MODE,里面有个值是KernelMode,填写即可。
__inout_opt PVOID ParseContext,
/
/
opt为可选参数,直接写NULL
__out PVOID
*
Object
/
/
驱动对象二级指针
);
extern POBJECT_TYPE
*
IoDriverObjectType;
extern POBJECT_TYPE
*
IoDriverObjectType;
extern POBJECT_TYPE
*
IoDriverObjectType;
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
PKLDR_DATA_TABLE_ENTRY selfNode
=
DriverObject
-
>DriverSection;
PKLDR_DATA_TABLE_ENTRY preNode
=
selfNode;
UNICODE_STRING httpName
=
{
0
};
RtlInitUnicodeString(&httpName,L
"HTTP.sys"
);
UNICODE_STRING httpObjName
=
{
0
};
RtlInitUnicodeString(&httpObjName, L
"\\Driver\\HTTP"
);
do {
if
(preNode
-
>BaseDllName.Length !
=
0
&& RtlCompareUnicodeString(&preNode
-
>BaseDllName,&httpName,TRUE)
=
=
0
) {
DbgPrint(
"%wZ\r\n"
, &preNode
-
>BaseDllName);
PDRIVER_OBJECT pHttpObj
=
NULL;
ObReferenceObjectByName(&httpObjName,FILE_ALL_ACCESS,NULL,NULL,
*
IoDriverObjectType, KernelMode,NULL, &pHttpObj);
/
/
取驱动对象指针
pHttpObj
-
>Flags
=
0
;
/
/
清除几个属性,防止被搜索到。
pHttpObj
-
>DriverSection
=
0
;
pHttpObj
-
>DriverInit
=
0
;
RemoveEntryList(preNode);
break
;
}
preNode
=
preNode
-
>InLoadOrderLinks.Flink;
}
while
(preNode !
=
selfNode);
DriverObject
-
>DriverUnload
=
UnloadDriver;
return
STATUS_SUCCESS;
}
extern POBJECT_TYPE
*
IoDriverObjectType;
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
PKLDR_DATA_TABLE_ENTRY selfNode
=
DriverObject
-
>DriverSection;
PKLDR_DATA_TABLE_ENTRY preNode
=
selfNode;
UNICODE_STRING httpName
=
{
0
};
RtlInitUnicodeString(&httpName,L
"HTTP.sys"
);
UNICODE_STRING httpObjName
=
{
0
};
RtlInitUnicodeString(&httpObjName, L
"\\Driver\\HTTP"
);
do {
if
(preNode
-
>BaseDllName.Length !
=
0
&& RtlCompareUnicodeString(&preNode
-
>BaseDllName,&httpName,TRUE)
=
=
0
) {
DbgPrint(
"%wZ\r\n"
, &preNode
-
>BaseDllName);
PDRIVER_OBJECT pHttpObj
=
NULL;
ObReferenceObjectByName(&httpObjName,FILE_ALL_ACCESS,NULL,NULL,
*
IoDriverObjectType, KernelMode,NULL, &pHttpObj);
/
/
取驱动对象指针
pHttpObj
-
>Flags
=
0
;
/
/
清除几个属性,防止被搜索到。
pHttpObj
-
>DriverSection
=
0
;
pHttpObj
-
>DriverInit
=
0
;
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2021-9-22 17:11
被SSH山水画编辑
,原因: 更新驱动通信