Windows内核开发
应用程和驱动层的通信1
我们在三环的时候窗口程序之间通过MSG传递消息。
而R3和R0的通信依靠IRP(I/O Request Packet)传递消息
R3与R0通信还需要一个关键的角色:设备对象
步骤:
1.创建设备对象IoCreateDevice
-创建一个设备名称
-创建符号链接让3环能找到这个设备
2.设备数据交互方式
-DO_BUFFERD_IO 缓冲区读写,将三环缓冲区数据复制到零环
-DO_DIRECT_IO 直接读写,三环和零环不同线性地址映射到同一个物理页
3.设置派遣函数处理消息,其实就是回调函数
#include <Windows.h>
#include <iostream>
int main() {
// 打开一个设备
HANDLE hFile = CreateFileW(L"\\\\.\\StarHook2",GENERIC_READ | GENERIC_WRITE,NULL,NULL,
OPEN_EXISTING,FILE_ATTRIBUTE_SYSTEM,NULL);
if (hFile) {
printf("打开成功");
CloseHandle(hFile);
}else {
printf("打开失败");
}
system("pause");
return 0;
}
#include <ntddk.h>
void DriverUnload(PDRIVER_OBJECT pDriver) {
// 删除符号链接和设备对象
UNICODE_STRING linkName = RTL_CONSTANT_STRING(L"\\??\\StarHook2");
IoDeleteSymbolicLink(&linkName);
IoDeleteDevice(pDriver->DeviceObject); // 删除设备对象
DbgPrint("DriverUnload");
}
NTSTATUS
DispatchIRQ(
struct _DEVICE_OBJECT* DeviceObject,
struct _IRP* Irp // 沟通桥梁
)
{
DbgPrint("IRP_MJ_CREATE 触发了");
Irp->IoStatus.Status = STATUS_SUCCESS; // 告诉3环处理结果状态 GetLastError获取的值
Irp->IoStatus.Information = 0; // 返回的数据大小0
IoCompleteRequest(Irp,IO_NO_INCREMENT); // 表示完成了这次请求响应
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath) {
// 三环和0环的通信要通过IRP, IRP需要设备对象
// 创建设备对象
UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\StarHook1");
PDEVICE_OBJECT pDevice = NULL;
// 1.驱动对象 2.设备扩展大小 0 3.设备名称 4.设备类型 5.设备属性 6.设备是否是一个独占设备 7.输出设备对象
NTSTATUS ret = IoCreateDevice(pDriver,0,&DeviceName,FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,FALSE,&pDevice);
if (NT_SUCCESS(ret)) {
// 如果创建设备对象成功 我们创建一个符号链接,三环通过符号链接才能访问到0环的设备
UNICODE_STRING linkName = RTL_CONSTANT_STRING(L"\\??\\StarHook2");
// 符号链接 设备名字
ret = IoCreateSymbolicLink(&linkName,&DeviceName);
// 指定数据交互方式
if (NT_SUCCESS(ret)) {
pDevice->Flags = pDevice->Flags | DO_BUFFERED_IO; // 将三环缓存区数据复制到零环
// 设置回调函数 我要处理CREATE事件
pDriver->MajorFunction[IRP_MJ_CREATE] = DispatchIRQ;
}else {
IoDeleteDevice(pDevice);
}
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
应用程和驱动层之间的通信2
#include <Windows.h>
#include <iostream>
int main() {
// 打开一个设备
HANDLE hFile = CreateFileW(L"\\\\.\\StarHook2",GENERIC_READ | GENERIC_WRITE,NULL,NULL,
OPEN_EXISTING,FILE_ATTRIBUTE_SYSTEM,NULL);
if (hFile) {
printf("打开成功");
// 往设备中写入数据
const char* str = "hello StarHook";
DWORD realWrite = 0;
DWORD realRead = 0;
BOOL ret = WriteFile(hFile, str, strlen(str) + 1,&realWrite,NULL);
if (ret) {
printf("写入成功");
}
char readBuff[0x100] = { 0 };
ret = ReadFile(hFile, readBuff, 0x100, &realRead, NULL);
if (ret) {
printf("读取成功:%s\n",readBuff);
}
CloseHandle(hFile); // 这里其实也会给0环发送一个消息 关闭设备
}else {
printf("打开失败");
}
system("pause");
return 0;
}
#include <ntddk.h>
void DriverUnload(PDRIVER_OBJECT pDriver) {
UNICODE_STRING DriverLink = RTL_CONSTANT_STRING(L"\\??\\StarHook2");
IoDeleteSymbolicLink(&DriverLink);
IoDeleteDevice(pDriver->DeviceObject);
DbgPrint("DriverUnload");
}
NTSTATUS
DispatchIRQ_Create(
struct _DEVICE_OBJECT* DeviceObject,
struct _IRP* Irp // 沟通桥梁
)
{
DbgPrint("IRP_MJ_CREATE 触发了");
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS
DispatchIRQ_Close(
struct _DEVICE_OBJECT* DeviceObject,
struct _IRP* Irp // 沟通桥梁
) {
DbgPrint("IRP_MJ_CLOSE 触发了");
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
// 收到写入数据的消息
NTSTATUS
DispatchIRQ_Write(
struct _DEVICE_OBJECT* DeviceObject,
struct _IRP* Irp // 沟通桥梁
) {
DbgPrint("IRP_MJ_WRITE 触发了");
// 1.获取缓冲区的大小
PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(Irp); // 获取当前的Irp栈
ULONG buffLen = pStack->Parameters.Write.Length;
// 2.获取缓冲区
PVOID buff = Irp->AssociatedIrp.SystemBuffer;
if (buff) {
DbgPrint("Irp收到数据:%s", buff);
}
// 返回数据
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
// 收到三环需要读取的消息 那么我们就要写入
NTSTATUS
DispatchIRQ_Read(
struct _DEVICE_OBJECT* DeviceObject,
struct _IRP* Irp // 沟通桥梁
) {
// 获取当前IRP栈
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
// 获取缓冲区
PVOID buff = Irp->AssociatedIrp.SystemBuffer;
// 往缓冲区里面写入数据
RtlCopyMemory(buff, "Hello,Three App", strlen("Hello,Three App") + 1);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = strlen("Hello,Three App") + 1;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath) {
// 创建设备对象
UNICODE_STRING DriverName = RTL_CONSTANT_STRING(L"\\Device\\StarHook1");
PDEVICE_OBJECT pDevice = NULL;
NTSTATUS ret = IoCreateDevice(pDriver, 0, &DriverName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDevice);
if (NT_SUCCESS(ret)) {
UNICODE_STRING DriverLink = RTL_CONSTANT_STRING(L"\\??\\StarHook2");
ret = IoCreateSymbolicLink(&DriverLink,&DriverName);
if (NT_SUCCESS(ret)) {
pDevice->Flags |= DO_BUFFERED_IO; // 设置数据交互模式
// 设置回调函数
pDriver->MajorFunction[IRP_MJ_CREATE] = DispatchIRQ_Create;
pDriver->MajorFunction[IRP_MJ_CLOSE] = DispatchIRQ_Close;
pDriver->MajorFunction[IRP_MJ_WRITE] = DispatchIRQ_Write;
pDriver->MajorFunction[IRP_MJ_READ] = DispatchIRQ_Read;
}else {
IoDeleteDevice(pDevice);
}
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
应用程和驱动层之间的通信3
#include <Windows.h>
#include <iostream>
// 1.设备类型 2.自定义数字0x800~0xfff 3.数据交互方式 4.权限
#define MSG_CODE_WRITE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED,FILE_ANY_ACCESS)
#define MSG_CODE_READ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED,FILE_ANY_ACCESS)
int main() {
// 打开一个设备
HANDLE hFile = CreateFileW(L"\\\\.\\StarHook2",GENERIC_READ | GENERIC_WRITE,NULL,NULL,
OPEN_EXISTING,FILE_ATTRIBUTE_SYSTEM,NULL);
if (hFile) {
printf("打开成功");
// 往设备中写入数据
const char* str = "hello StarHook";
// 通过DeviceIoControl通信
// 1.句柄 2.消息码 3.写入缓冲区的指针 4.缓冲区大小 5.输出缓冲区的指针 6.缓冲区大小
// 7.返回实际的大小 8.重叠IO
LPVOID inBuff = malloc(0x100);
ZeroMemory(inBuff, 0x100);
memcpy(inBuff,str, strlen(str) + 1);
DWORD returnByte = 0;
// 往零环写入数据
DeviceIoControl(hFile, MSG_CODE_WRITE, inBuff,0x100,NULL,0,&returnByte,NULL);
// 读取零环数据
DeviceIoControl(hFile, MSG_CODE_READ, NULL, 0,inBuff, 0x100, &returnByte, NULL);
printf("零环返回数据大小:%d,内容:%s", returnByte,inBuff);
CloseHandle(hFile); // 这里其实也会给0环发送一个消息 关闭设备
}else {
printf("打开失败");
}
system("pause");
return 0;
}
#include <ntddk.h>
void DriverUnload(PDRIVER_OBJECT pDriver) {
UNICODE_STRING DriverLink = RTL_CONSTANT_STRING(L"\\??\\StarHook2");
IoDeleteSymbolicLink(&DriverLink);
IoDeleteDevice(pDriver->DeviceObject);
DbgPrint("DriverUnload");
}
NTSTATUS
DispatchIRQ_Create(
struct _DEVICE_OBJECT* DeviceObject,
struct _IRP* Irp // 沟通桥梁
)
{
DbgPrint("IRP_MJ_CREATE 触发了");
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS
DispatchIRQ_Close(
struct _DEVICE_OBJECT* DeviceObject,
struct _IRP* Irp // 沟通桥梁
) {
DbgPrint("IRP_MJ_CLOSE 触发了");
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
// 收到写入数据的消息
NTSTATUS
DispatchIRQ_Write(
struct _DEVICE_OBJECT* DeviceObject,
struct _IRP* Irp // 沟通桥梁
) {
DbgPrint("IRP_MJ_WRITE 触发了");
// 1.获取缓冲区的大小
PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(Irp); // 获取当前的Irp栈
ULONG buffLen = pStack->Parameters.Write.Length;
// 2.获取缓冲区
PVOID buff = Irp->AssociatedIrp.SystemBuffer;
if (buff) {
DbgPrint("Irp收到数据:%s", buff);
}
// 返回数据
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS
DispatchIRQ_Control(
struct _DEVICE_OBJECT* DeviceObject,
struct _IRP* Irp // 沟通桥梁
) {
DbgPrint("DispatchIRQ_Control 触发了");
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
// 收到三环需要读取的消息 那么我们就要写入
NTSTATUS
DispatchIRQ_Read(
struct _DEVICE_OBJECT* DeviceObject,
struct _IRP* Irp // 沟通桥梁
) {
// 获取当前IRP栈
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
// 获取缓冲区
PVOID buff = Irp->AssociatedIrp.SystemBuffer;
// 往缓冲区里面写入数据
RtlCopyMemory(buff, "Hello,Three App", strlen("Hello,Three App") + 1);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = strlen("Hello,Three App") + 1;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
#define MSG_CODE_WRITE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED,FILE_ANY_ACCESS)
#define MSG_CODE_READ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED,FILE_ANY_ACCESS)
// 统一入口处理
NTSTATUS
DispatchIRQ_ALL(
struct _DEVICE_OBJECT* DeviceObject,
struct _IRP* Irp // 沟通桥梁
) {
// 获取当前Irp栈
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
ULONG inLen = irpStack->Parameters.DeviceIoControl.InputBufferLength; // 获取缓冲区大小
ULONG outLen = irpStack->Parameters.DeviceIoControl.OutputBufferLength; // 获取缓冲区大小
// 判断消息
if (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
if (irpStack->Parameters.DeviceIoControl.IoControlCode == MSG_CODE_WRITE)
{
// 读取缓冲区
PVOID buff = Irp->AssociatedIrp.SystemBuffer;
DbgPrint("%s",buff);
Irp->IoStatus.Information = 0;
}else if (irpStack->Parameters.DeviceIoControl.IoControlCode == MSG_CODE_READ) {
// 那么我们就要写入数据 往三环
PVOID buff = Irp->AssociatedIrp.SystemBuffer; // 拿到三环的缓冲区
RtlCopyMemory(buff, "Hello,Three App", strlen("Hello,Three App") + 1);
Irp->IoStatus.Information = strlen("Hello,Three App") + 1;
}
}else {
Irp->IoStatus.Information = 0;
}
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath) {
// 创建设备对象
UNICODE_STRING DriverName = RTL_CONSTANT_STRING(L"\\Device\\StarHook1");
PDEVICE_OBJECT pDevice = NULL;
NTSTATUS ret = IoCreateDevice(pDriver, 0, &DriverName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDevice);
if (NT_SUCCESS(ret)) {
UNICODE_STRING DriverLink = RTL_CONSTANT_STRING(L"\\??\\StarHook2");
ret = IoCreateSymbolicLink(&DriverLink,&DriverName);
if (NT_SUCCESS(ret)) {
pDevice->Flags |= DO_BUFFERED_IO; // 设置数据交互模式
// 设置回调函数
for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
pDriver->MajorFunction[i] = DispatchIRQ_ALL; // 统一入口
}
// pDriver->MajorFunction[IRP_MJ_CREATE] = DispatchIRQ_Create;
// pDriver->MajorFunction[IRP_MJ_CLOSE] = DispatchIRQ_Close;
// pDriver->MajorFunction[IRP_MJ_WRITE] = DispatchIRQ_Write;
// pDriver->MajorFunction[IRP_MJ_READ] = DispatchIRQ_Read;
// pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIRQ_Control;
}else {
IoDeleteDevice(pDevice);
}
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
数据交互方式
三环和零环设备进行通信数据交互方式:
1.DO_BUFFERED_IO 缓冲区读写方式
当三环往零环写入数据的时候,需要提供一个缓冲区。
-调用API进入零环以后,操作系统会将三环的缓冲区数据复制到零环的一个地址下Irp->AssociatedIrp.SystemBuffer
当三环往零环读取数据的时候,刚好反过来,同样三环提供一个缓冲区。
-进入零环后,操作系统不会复制三环的内容。零环将数据写入到零环缓冲区,返回到三环的时候,操作系统会将零环的缓冲区数据复制到三环。
【即读取的时候,复制三环数据到零环,写入的时候,复制零环数据到三环】
2.DO_DIRECT_IO 直接读写方式
三环对零环进行读写操作的时候都会提供一个缓冲区,操作系统用内存描述符表MDL记录这块内存
进入零环以后通过MmGetMdlVirtualAddress获取三环地址
MmGetSystemAddressForMdlSafe映射一个零环地址,与三环的缓冲区指向同一个物理页。
直接读写方式WriteFile ReadFile 和 IoDeviceControl略有不同
【两个线性地址映射到同一个物理地址,不用复制操作了】
3.其他方式
零环直接使用三环虚拟地址进行读写操作,这种方式不推荐。
#include <Windows.h>
#include <iostream>
// 1.设备类型 2.自定义数字0x800~0xfff 3.数据交互方式 4.权限
#define MSG_CODE_WRITE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED,FILE_ANY_ACCESS)
#define MSG_CODE_READ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED,FILE_ANY_ACCESS)
int main() {
// 打开一个设备
HANDLE hFile = CreateFileW(L"\\\\.\\StarHook2",GENERIC_READ | GENERIC_WRITE,NULL,NULL,
OPEN_EXISTING,FILE_ATTRIBUTE_SYSTEM,NULL);
if (hFile) {
DWORD realRead = 0;
char buff[0x100] = {0};
ReadFile(hFile, buff, 0x100, &realRead, NULL);
printf("%s\n",buff);
CloseHandle(hFile); // 这里其实也会给0环发送一个消息 关闭设备
}else {
printf("打开失败");
}
system("pause");
return 0;
}
#include <ntddk.h>
void DriverUnload(PDRIVER_OBJECT pDriver) {
UNICODE_STRING DriverLink = RTL_CONSTANT_STRING(L"\\??\\StarHook2");
IoDeleteSymbolicLink(&DriverLink);
IoDeleteDevice(pDriver->DeviceObject);
DbgPrint("DriverUnload");
}
NTSTATUS
DispatchIRQ_Create(
struct _DEVICE_OBJECT* DeviceObject,
struct _IRP* Irp // 沟通桥梁
)
{
DbgPrint("IRP_MJ_CREATE 触发了");
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
// 收到三环需要读取的消息 那么我们就要写入
NTSTATUS
DispatchIRQ_Read(
struct _DEVICE_OBJECT* DeviceObject,
struct _IRP* Irp // 沟通桥梁
) {
// 获取当前IRP栈
PIO_STACK_LOCATION pIoStack = IoGetCurrentIrpStackLocation(Irp);
// 三环需要读取零环数据 获取缓冲区大小
// 直接读写 三环的映射地址保存到MDL表中 Irp->MdlAddress 起始地址
ULONG uLen = MmGetMdlByteCount(Irp->MdlAddress); // 直接读写的方式 不能使用之前的方式了
DbgPrint("三环缓冲区大小:%d", uLen);
// 获取三环的缓冲区地址
ULONG r3Addr = MmGetMdlVirtualAddress(Irp->MdlAddress); // 其实是起始地址 + 偏移 = 真正地址
DbgPrint("三环缓冲区地址:%x", r3Addr);
// 然后我们要把这个地址在零环也映射一份,那么零环和三环就是指向同一个物理页了
PVOID r0Buff = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
RtlCopyMemory(r0Buff,"Hello,R3 Direct Io",strlen("Hello,R3 Direct Io") + 1);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = strlen("Hello,R3 Direct Io") + 1;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath) {
// 创建设备对象
UNICODE_STRING DriverName = RTL_CONSTANT_STRING(L"\\Device\\StarHook1");
PDEVICE_OBJECT pDevice = NULL;
NTSTATUS ret = IoCreateDevice(pDriver, 0, &DriverName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDevice);
if (NT_SUCCESS(ret)) {
UNICODE_STRING DriverLink = RTL_CONSTANT_STRING(L"\\??\\StarHook2");
ret = IoCreateSymbolicLink(&DriverLink,&DriverName);
if (NT_SUCCESS(ret)) {
// pDevice->Flags |= DO_BUFFERED_IO; // 设置数据交互模式
pDevice->Flags |= DO_DIRECT_IO; // 设置数据交互方式为直接读写
// 设置回调函数
pDriver->MajorFunction[IRP_MJ_CREATE] = DispatchIRQ_Create;
pDriver->MajorFunction[IRP_MJ_READ] = DispatchIRQ_Read;
}else {
IoDeleteDevice(pDevice);
}
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
DeviceIoControl实现直接读写
注意:
使用Device 写通信的话 不能使用MDL操作 使用Device control和缓冲区拷贝通信方式一样,而使用读的话就会使用Mdl
【如果三环写入不用MDL,如果三环读取用MDL】
#include <Windows.h>
#include <iostream>
// 1.设备类型 2.自定义数字0x800~0xfff 3.数据交互方式 4.权限
#define MSG_CODE_WRITE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED,FILE_ANY_ACCESS)
#define MSG_CODE_READ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED,FILE_ANY_ACCESS)
// in 写入数据
#define MSG_DIRECT_WRITE CTL_CODE(FILE_DEVICE_UNKNOWN,0x803,METHOD_IN_DIRECT,FILE_ANY_ACCESS)
// out 读取数据
#define MSG_DIRECT_READ CTL_CODE(FILE_DEVICE_UNKNOWN,0x804,METHOD_OUT_DIRECT,FILE_ANY_ACCESS)
int main() {
// 打开一个设备
HANDLE hFile = CreateFileW(L"\\\\.\\StarHook2",GENERIC_READ | GENERIC_WRITE,NULL,NULL,
OPEN_EXISTING,FILE_ATTRIBUTE_SYSTEM,NULL);
if (hFile) {
DWORD returnBytes = 0;
LPVOID inBuff = malloc(0x100);
ZeroMemory(inBuff,0x100);
memcpy(inBuff, "Hello R0 Direct Write", strlen("Hello R0 Direct Write") + 1);
// 写入数据到0环
DeviceIoControl(hFile, MSG_DIRECT_WRITE,inBuff,0x100,NULL,0,&returnBytes,NULL);
// 从0环读取数据到三环
DeviceIoControl(hFile, MSG_DIRECT_READ, NULL, 0, inBuff, 0x100, &returnBytes, NULL);
printf("从0环读取到数据:%s\n",inBuff);
CloseHandle(hFile); // 这里其实也会给0环发送一个消息 关闭设备
}else {
printf("打开失败");
}
system("pause");
return 0;
}
#include <ntddk.h>
void DriverUnload(PDRIVER_OBJECT pDriver) {
UNICODE_STRING DriverLink = RTL_CONSTANT_STRING(L"\\??\\StarHook2");
IoDeleteSymbolicLink(&DriverLink);
IoDeleteDevice(pDriver->DeviceObject);
DbgPrint("DriverUnload");
}
// in 写入数据
#define MSG_DIRECT_WRITE CTL_CODE(FILE_DEVICE_UNKNOWN,0x803,METHOD_IN_DIRECT,FILE_ANY_ACCESS)
// out 读取数据
#define MSG_DIRECT_READ CTL_CODE(FILE_DEVICE_UNKNOWN,0x804,METHOD_OUT_DIRECT,FILE_ANY_ACCESS)
NTSTATUS
DispatchIRQ_Create(
struct _DEVICE_OBJECT* DeviceObject,
struct _IRP* Irp // 沟通桥梁
)
{
DbgPrint("IRP_MJ_CREATE 触发了");
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS
DispatchIRQ_Control(
struct _DEVICE_OBJECT* DeviceObject,
struct _IRP* Irp // 沟通桥梁
) {
// 获取当前的Irp栈
PIO_STACK_LOCATION pIoStack = IoGetCurrentIrpStackLocation(Irp);
switch (pIoStack->Parameters.DeviceIoControl.IoControlCode)
{
case MSG_DIRECT_WRITE: { // 如果三环是写入
// 直接读写的特点,会把三环的地址信息放到MDL中 我们需要通过MDL去操作
// 不过我们使用Device 通信的话 不能使用MDL操作 使用Device control和缓冲区拷贝方式一样
PVOID buff = Irp->AssociatedIrp.SystemBuffer;
DbgPrint("三环传递过来的数据:%s\n",buff);
Irp->IoStatus.Information = 0;
break;
}case MSG_DIRECT_READ: { // 如果三环是读取 那么它就会使用Mdi了
// 那么我们就要往里面写入数据了
RtlCopyMemory(MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority),
"Hello R3 Device Direct", strlen("Hello R3 Device Direct") + 1);
Irp->IoStatus.Information = strlen("Hello R3 Device Direct") + 1;
break;
}
}
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
// 收到三环需要读取的消息 那么我们就要写入
NTSTATUS
DispatchIRQ_Read(
struct _DEVICE_OBJECT* DeviceObject,
struct _IRP* Irp // 沟通桥梁
) {
// 获取当前IRP栈
PIO_STACK_LOCATION pIoStack = IoGetCurrentIrpStackLocation(Irp);
// 三环需要读取零环数据 获取缓冲区大小
// 直接读写 三环的映射地址保存到MDL表中 Irp->MdlAddress 起始地址
ULONG uLen = MmGetMdlByteCount(Irp->MdlAddress); // 直接读写的方式 不能使用之前的方式了
DbgPrint("三环缓冲区大小:%d", uLen);
// 获取三环的缓冲区地址
ULONG r3Addr = MmGetMdlVirtualAddress(Irp->MdlAddress); // 其实是起始地址 + 偏移 = 真正地址
DbgPrint("三环缓冲区地址:%x", r3Addr);
// 然后我们要把这个地址在零环也映射一份,那么零环和三环就是指向同一个物理页了
PVOID r0Buff = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
RtlCopyMemory(r0Buff,"Hello,R3 Direct Io",strlen("Hello,R3 Direct Io") + 1);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = strlen("Hello,R3 Direct Io") + 1;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath) {
// 创建设备对象
UNICODE_STRING DriverName = RTL_CONSTANT_STRING(L"\\Device\\StarHook1");
PDEVICE_OBJECT pDevice = NULL;
NTSTATUS ret = IoCreateDevice(pDriver, 0, &DriverName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDevice);
if (NT_SUCCESS(ret)) {
UNICODE_STRING DriverLink = RTL_CONSTANT_STRING(L"\\??\\StarHook2");
ret = IoCreateSymbolicLink(&DriverLink,&DriverName);
if (NT_SUCCESS(ret)) {
// pDevice->Flags |= DO_BUFFERED_IO; // 设置数据交互模式
pDevice->Flags |= DO_DIRECT_IO; // 设置数据交互方式为直接读写
// 设置回调函数
pDriver->MajorFunction[IRP_MJ_CREATE] = DispatchIRQ_Create;
pDriver->MajorFunction[IRP_MJ_READ] = DispatchIRQ_Read;
pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIRQ_Control;
}else {
IoDeleteDevice(pDevice);
}
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
## API的调用过程
```c++
比如:CloseHandle-->Kernel32.dll-->KernelBase.dll-->ntdll-->内核层--->内核层CloseHandle的API
三环跟零环的本质区别是什么?【R0 VS R3】
-权限区别不一样
权限体现在cs ss那些
那么提权:调用门 中断门 陷阱门 TSS任务段 任务门...
那么权限不一样,肯定要提权,3环不提权怎么进入零环。
mov eax,0x32
ntdll中eax的值决定了进入内核层要调用哪个API。
mov edx,esp 【给0环的函数,进行参数传值】
sysenter 【进入0环】
retn
前面也说三环和零环的本质区别就是权限问题,调用完sysenter以后,它就提权了。
总的来说要进入零环,就必须把cs ss eip esp这些东西替换掉,一定要提权,才能进入零环。
快速系统调用
前面:CloseHandle->kernel32.dll->kernelBase.dll->ntdll
eax:系统服务号【即进入内核以后要调用哪个API】
然后我们发现无论是调用内核哪个函数edx的值都是0x7FFE0300
0x7FFE0300其实是某结构体的一个成员
结构体:_KUSER_SHARED_DATA
结构体的首地址:R3:0x7FFE0000 R0:0xFFDF0000
结构体所在的内存空间是共享数据区,R3和R0共享,原理就是0环也同样有一个线性地址跟这个共享区指向这里。
线性地址不同但是指向的是一个物理页,这块共享区域主要就是用来用户层和内核层之间快速的传递信息的。
【也就是说虽然R0,R3线性地址不同,但是这个结构体指向的物理页是同一个】
0x7FFE0300 = R3:0x7FFE0000 + 0x300
也就是说我们在ntdll进入内核之前,给edx传递的值就是systemCall这个成员的地址
KUSER_SHARED_DATA + 0X300 == systemCall 就决定了我们进入内核的函数在哪里
然后观察内存,其实systemCall保存的就是KiFastSystemCall这个函数的地址
KiFastSystemCall 快速系统调用
然后我们发现快速系统调用就是给内核函数传递参数,调用sysenter这个指令
sysenter/sysexit 指令从奔腾||处理器开始引入,在这之前不支持这个指令。
在奔腾||处理器之前是使用KiIntSystemCall:int 2E 进入内核的
后面就是sysenter
操作系统在启动的时候,会通过CPUID汇编指令判断是否支持sysenter指令
支持:将KUSER_SHARED_DATA + 0X300即systemCall的函数地址,填上KiFastSystemCall
不支持:将KUSER_SHARED_DATA + 0X300即systemCall的函数地址,填上KiIntSystemCall
填上KiIntSystemCall:
.text:77F070C0 arg_4 = byte ptr 8
.text:77F070C0
.text:77F070C0 lea edx, [esp+8]
.text:77F070C4 int 2Eh ; DOS 2+ internal - EXECUTE COMMAND
.text:77F070C4 ; DS:SI -> counted CR-terminated command string
.text:77F070C6 retn
所以早期的CPU应该是通过中断门,陷阱门之类的方式提权的,int指令。
sysenter修改cs ss eip esp
从MSR寄存器来的。
MSR寄存器是一堆寄存器的统称,它有很多兄弟姐妹,由于数量太多,没办法每个都取一个名字,所以干脆用序号命名
当调用sysenter进入零环的时候,CPU会去
MSR 174加载CS
MSR 175加载ESP
MSR 176加载EIP
MSR 174的值+8 = SS
在windbg中通过rdmsr + 序号/地址 指令查看msr寄存器的值
然后我们发现rdmsr 174很熟悉的0环权限的cs的段选择子 0008
SysEnter
三环进入零环:
CS SS EIP ESP
SysEnter比起KiIntSystemCall,快就快在KiIntSystemCall要去使用内存,完成提权。而Msr只需要通过寄存器读取,通过寄存器肯定比通过内存要快很多。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w45hTQBW-1659295510884)(https://files.mdnice.com/user/28517/3ca924b0-62da-4f83-a01f-a7c8383d0c1f.png)]
SSDT
三环API通过sysEnter快速系统调用进入0环,那么三环的寄存器保存到哪里了呢?
因为三环的API进入到0环,肯定还要出来,出来那么肯定需要保存进入0环之前的环境。
寄存器环境啊 EIP那些。其实就是跟我们TSS任务段切换任务一样。
其实线程上下文环境不是三环进行保存的,而是0环进行保存的
调用完sysEnter会到这个函数进行执行
KiFastCallEntry
那么我们就要IDA分析这个函数,看看他是怎么执行的。在ntkrnlpa.exe里面
在3环里面fs的值是3B,fs:[0]指向TEB
在0环里面fs的值是30,fs:[0]指向KPCR【用来记录CPU状态信息】
IDA快捷键
-T可以把某个偏移修改为结构体的偏移形式
回顾:
-R把变量变为ebp偏移
-C把ebp偏移变为变量
线程上下文环境结构体:
typedef struct _KTRAP_FRAME {
+0x000 DbgEbp : Uint4B
+0x004 DbgEip : Uint4B
+0x008 DbgArgMark : Uint4B
+0x00c DbgArgPointer : Uint4B
+0x010 TempSegCs : Uint2B
+0x012 Logging : UChar
+0x013 Reserved : UChar
+0x014 TempEsp : Uint4B
+0x018 Dr0 : Uint4B
+0x01c Dr1 : Uint4B
+0x020 Dr2 : Uint4B
+0x024 Dr3 : Uint4B
+0x028 Dr6 : Uint4B
+0x02c Dr7 : Uint4B
+0x030 SegGs : Uint4B
+0x034 SegEs : Uint4B
+0x038 SegDs : Uint4B
+0x03c Edx : Uint4B
+0x040 Ecx : Uint4B
+0x044 Eax : Uint4B
+0x048 PreviousPreviousMode : Uint4B
+0x04c ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x050 SegFs : Uint4B
+0x054 Edi : Uint4B
+0x058 Esi : Uint4B
+0x05c Ebx : Uint4B
+0x060 Ebp : Uint4B
+0x064 ErrCode : Uint4B
+0x068 Eip : Uint4B
+0x06c SegCs : Uint4B
+0x070 EFlags : Uint4B
+0x074 HardwareEsp : Uint4B
+0x078 HardwareSegSs : Uint4B 【修改完esp已经到了这里】
+0x07c V86Es : Uint4B
+0x080 V86Ds : Uint4B
+0x084 V86Fs : Uint4B
+0x088 V86Gs : Uint4B
} KTRAP_FRAME, *PKTRAP_FRAME;
KeServiceDescriptorTable SSDT有四个成员:
-1.0环函数地址表【四个字节】
-2.统计API调用次数
-3.0环函数地址表函数数量
-4.0环函数参数表【一个字节Byte数组,参数大小】
其实除了SSDT表,内核还有一张表叫做shadowSSDT。
SSDT主要是处理Kernel32.dll
shadowSSDT主要是处理user32.dll,GDI32.dll
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0O2ghy35-1659295510892)(https://files.mdnice.com/user/28517/947b8870-d373-4a16-b4e5-ffccb5748751.png)]
SSDT HOOK
前面去其实就是三环给了eax一个系统调用号 -> sysEnter -> KiFastCallEntry -> 找到SSDT表 -> 根据系统调用号解析SSDT表调用API
KeServiceDescriptorTable SSDT有四个成员:
-1.0环函数地址表【四个字节】
-2.统计API调用次数
-3.0环函数地址表函数数量
-4.0环函数参数表【一个字节Byte数组,参数大小】
其实除了SSDT表,内核还有一张表叫做shadowSSDT。
SSDT主要是处理Kernel32.dll
shadowSSDT主要是处理user32.dll,GDI32.dll
#include <ntifs.h>
typedef struct _SSDT {
PULONG funAddrTable; // 函数地址表
PULONG funUseCount; // 函数使用次数
ULONG funNumber; // 函数个数
PULONG funParamTable; // 函数参数字节表
}SSDT,*PSSDT;
// 定义函数指针
typedef NTSTATUS (NTAPI* _NtClose)(HANDLE Handle);
// SSDT表地址在 Kernel32导出变量中 KeServiceDescriptorTable 000000000056A9C0 915
// 而所有的程序都会引入Kernel32 所以我们可以直接使用这个导出变量,就可以拿到SSDT表的地址
EXTERN_C PSSDT KeServiceDescriptorTable;
_NtClose g_OldNtCloseAddr = NULL; // 保存原本的函数地址 HOOK完要给它恢复
PULONG g_funTableAddr = NULL;
// HOOK函数 替换掉NtClose
NTSTATUS NTAPI MyNtClose(HANDLE Handle) {
DbgPrint("SSDT HOOK成功,HOOK NtClose");
return g_OldNtCloseAddr(Handle); // 让原本拿到NtClose完成剩下工作 Hook不能影响原本的函数内容
}
// 安装Hook
NTSTATUS InstallSSDTHook() {
// 我们Hook CloseHandle 对应 -> NtClose
// KeServiceDescriptorTable->funAddrTable 这个地址是不可以修改的 所以我们做一个新的映射
PHYSICAL_ADDRESS pyhAddr = MmGetPhysicalAddress(KeServiceDescriptorTable->funAddrTable); // 获取物理地址
g_funTableAddr = MmMapIoSpace(pyhAddr, PAGE_SIZE, MmNonCached); // 物理地址 映射多大 是否需要缓存
// g_funTableAddr 映射出来的这个线性地址 就可以直接进行修改操作了
g_OldNtCloseAddr = g_funTableAddr[0x32];
g_funTableAddr[0x32] = (ULONG)MyNtClose;
return STATUS_SUCCESS;
}
// 卸载Hook
NTSTATUS UnInstallSSDTHook() {
if (g_OldNtCloseAddr != NULL) {
g_funTableAddr[0x32] = g_OldNtCloseAddr; // 修改回去原本的NtClose
g_OldNtCloseAddr = NULL;
MmUnmapIoSpace(g_funTableAddr, PAGE_SIZE); // 然后取消之前的物理地址映射
}
return STATUS_SUCCESS;
}
void DriverUnload(PDRIVER_OBJECT pDriver) {
UnInstallSSDTHook(); // 卸载钩子
DbgPrint("DriverUnload");
}
// eax KiFaseCallEntry SSDT 函数地址表 eax and 0x0FFF 索引 调用API
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath) {
InstallSSDTHook(); // 安装钩子
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
KeServiceDescriptorTable + 0x40 = ShaowSSDT
动态获取调用号-解析ntdll导出表
前面是有缺陷的,因为系统版本不一样,系统调用号可能不一样,那么到别的电脑上可能就运行不了了。所以我们要动态解析。
其实就是解析导出表,拿到函数的导出序号~
就跟我们解析PE文件的导出表是一样的
#pragma once
#include <ntifs.h>
#include <ntimage.h>
// 加载PE文件
CHAR* PeLoadFile(PHANDLE hFile);
// 获取函数地址
ULONG GetFunAddr(CHAR* g_fileBuff,CHAR* funName);
// Rva转Foa
ULONG RvaToFoa(CHAR* g_fileBuff,ULONG Rva);
// 获取调用号
ULONG GetServiceCode(CHAR *g_fileBuff);
#include "petools.h"
// 加载PE文件
CHAR* PeLoadFile(PHANDLE hFile) {
// 打开PE文件
OBJECT_ATTRIBUTES oa = {0};
oa.Length = sizeof(OBJECT_ATTRIBUTES);
oa.RootDirectory = NULL;
oa.SecurityDescriptor = NULL;
oa.SecurityQualityOfService = NULL;
UNICODE_STRING objPath = RTL_CONSTANT_STRING(L"\\??\\c:\\Windows\\system32\\ntdll.dll");
oa.ObjectName = &objPath;
oa.Attributes = OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE; // 不区分大小写 内核句柄
IO_STATUS_BLOCK ioStatus = {0};
LARGE_INTEGER lai = { 0 };
NTSTATUS ret = ZwCreateFile(hFile, GENERIC_READ, &oa,&ioStatus, &lai, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,NULL,0);
if (!NT_SUCCESS(ret)) {
return ret;
}
// 打开成功 那么就要把PE文件加载到内存
FILE_STANDARD_INFORMATION fileInfomation = {0};
// 查询文件基本信息 从而获取文件的大小
ZwQueryInformationFile(*hFile, &ioStatus, &fileInfomation, sizeof(FILE_BASIC_INFORMATION),FileStandardInformation);
CHAR* g_fileBuff = ExAllocatePool(PagedPool,fileInfomation.EndOfFile.QuadPart);
if (g_fileBuff == NULL) { // 创建缓冲区失败
return -1;
}
LARGE_INTEGER offset = { 0 }; // 把整个PE文件读入缓冲区
ret = ZwReadFile(*hFile, NULL, NULL, NULL, &ioStatus, g_fileBuff, fileInfomation.EndOfFile.QuadPart,&offset,NULL);
if (!NT_SUCCESS(ret)) {
return ret;
}
return g_fileBuff;
}
// 获取函数地址
ULONG GetFunAddr(CHAR* g_fileBuff, CHAR* funName) {
if (g_fileBuff == NULL) {
return 0;
}
// 解析PE文件 拿到导出表
PIMAGE_DOS_HEADER pDosHeader = g_fileBuff;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + g_fileBuff);
PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader;
PIMAGE_OPTIONAL_HEADER pOptionalHeader = &pNtHeader->OptionalHeader;
// 数据目录表
IMAGE_DATA_DIRECTORY dataDict = pOptionalHeader->DataDirectory[0];
PIMAGE_EXPORT_DIRECTORY pExportTable = (PIMAGE_EXPORT_DIRECTORY)(RvaToFoa(g_fileBuff,dataDict.VirtualAddress) + g_fileBuff);
// 然后解析导出表 拿到函数序号
PULONG funNameTable = RvaToFoa(g_fileBuff,pExportTable->AddressOfNames) + g_fileBuff;
PULONG funAddrTable = RvaToFoa(g_fileBuff,pExportTable->AddressOfFunctions) + g_fileBuff;
SHORT* funOrinalsTable = RvaToFoa(g_fileBuff,pExportTable->AddressOfNameOrdinals) + g_fileBuff;
for (size_t i = 0; i < pExportTable->NumberOfNames; i++){
CHAR* curName = RvaToFoa(g_fileBuff, funNameTable[i]) + g_fileBuff;
if (strcmp(funName, curName) == 0) {
// 名称和序号是一一对应的
return RvaToFoa(g_fileBuff, funAddrTable[funOrinalsTable[i]]) + g_fileBuff;
}
}
return 0;
}
// Rva转Foa
ULONG RvaToFoa(CHAR* g_fileBuff,ULONG Rva) {
PIMAGE_DOS_HEADER pDosHeader = g_fileBuff;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + g_fileBuff);
PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader;
// 获取区段
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
for (size_t i = 0; i < pFileHeader->NumberOfSections; i++) {
if (Rva >= pSectionHeader->VirtualAddress &&
Rva < (pSectionHeader->Misc.VirtualSize + pSectionHeader->VirtualAddress)) {
// Rva - 区段的Rva + 区段的Foa = Foa
return (ULONG)(Rva - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData);
}
pSectionHeader++;
}
return 0;
}
// 获取函数调用号
ULONG GetServiceCode(CHAR* g_fileBuff)
{
ULONG code = -1;
ULONG funAddr = GetFunAddr(g_fileBuff, "ZwClose");
// 函数地址 + 1个字节保存的就是调用号
code = *(PULONG)(funAddr + 1); // 获取调用号 这样就可以动态HOOK了
return code;
}
#include <ntifs.h>
#include "petools.h"
typedef struct _SSDT {
PULONG funAddrTable; // 函数地址表
PULONG funUseCount; // 函数使用次数
ULONG funNumber; // 函数个数
PULONG funParamTable; // 函数参数字节表
}SSDT, * PSSDT;
// 定义函数指针
typedef NTSTATUS(NTAPI* _NtClose)(HANDLE Handle);
// SSDT表地址在 Kernel32导出变量中 KeServiceDescriptorTable 000000000056A9C0 915
// 而所有的程序都会引入Kernel32 所以我们可以直接使用这个导出变量,就可以拿到SSDT表的地址
EXTERN_C PSSDT KeServiceDescriptorTable;
_NtClose g_OldNtCloseAddr = NULL; // 保存原本的函数地址 HOOK完要给它恢复
PULONG g_funTableAddr = NULL;
// HOOK函数 替换掉NtClose
NTSTATUS NTAPI MyNtClose(HANDLE Handle) {
DbgPrint("SSDT HOOK成功,HOOK NtClose");
return g_OldNtCloseAddr(Handle); // 让原本拿到NtClose完成剩下工作 Hook不能影响原本的函数内容
}
// 安装Hook
NTSTATUS InstallSSDTHook(ULONG code) {
// 我们Hook CloseHandle 对应 -> NtClose
// KeServiceDescriptorTable->funAddrTable 这个地址是不可以修改的 所以我们做一个新的映射
PHYSICAL_ADDRESS pyhAddr = MmGetPhysicalAddress(KeServiceDescriptorTable->funAddrTable); // 获取物理地址
g_funTableAddr = MmMapIoSpace(pyhAddr, PAGE_SIZE, MmNonCached); // 物理地址 映射多大 是否需要缓存
// g_funTableAddr 映射出来的这个线性地址 就可以直接进行修改操作了
g_OldNtCloseAddr = g_funTableAddr[code];
g_funTableAddr[code] = (ULONG)MyNtClose;
return STATUS_SUCCESS;
}
// 卸载Hook
NTSTATUS UnInstallSSDTHook() {
if (g_OldNtCloseAddr != NULL) {
g_funTableAddr[0x32] = g_OldNtCloseAddr; // 修改回去原本的NtClose
g_OldNtCloseAddr = NULL;
MmUnmapIoSpace(g_funTableAddr, PAGE_SIZE); // 然后取消之前的物理地址映射
}
return STATUS_SUCCESS;
}
void DriverUnload(PDRIVER_OBJECT pDriver) {
UnInstallSSDTHook(); // 卸载钩子
DbgPrint("DriverUnload");
}
// eax KiFaseCallEntry SSDT 函数地址表 eax and 0x0FFF 索引 调用API
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath) {
HANDLE hFile = NULL;
CHAR* fileBuff = PeLoadFile(&hFile);
if (fileBuff) { // 加载PE文件
ULONG code = GetServiceCode(fileBuff); // 获取函数调用号 那么现在就可以动态SSDT HOOK了
DbgPrint("动态调用号:%d\n",code);
InstallSSDTHook(code); // 安装钩子
// 关闭文件句柄
ExFreePool(fileBuff);
if(hFile) ZwClose(hFile);
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}