逆向基础-Windows驱动开发【SSDT HOOK】

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;
}

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值