浅谈一下驱动中的IoDeviceControl的双向通信

我们知道,驱动有时候需要和应用层结合

这时候我们就在想,能不能将他们结合起来

可能有时候,初学者只知道如何给应用层->驱动层

不知道驱动层->应用层如何做

这里我们举个简单的例子


要用到的头文件

#include <ntifs.h>
#pragma comment(lib, "bufferoverflowK.lib") 

这里拿获取驱动获取文件路径进行举例并传输给应用层

下面是一个简单的通式 

typedef NTSTATUS(*ZWQUERYINFORMATIONPROCESS)(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength, PULONG ReturnLength);
ZWQUERYINFORMATIONPROCESS ZwQueryInformationProcess = NULL;
NTSTATUS ImagePathConvertToRealPath(LPCWSTR FileName, LPCWSTR OutFileName)
{
	HANDLE	FileHandle;
	OBJECT_ATTRIBUTES ObjectAttribute;
	IO_STATUS_BLOCK IoStackBlock;
	UNICODE_STRING uFileName;
	NTSTATUS	Status;
	PFILE_OBJECT FileObject;
	RtlInitUnicodeString(&uFileName, FileName);
	InitializeObjectAttributes(&ObjectAttribute, &uFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);
	Status = IoCreateFile(&FileHandle, FILE_READ_ATTRIBUTES, &ObjectAttribute, &IoStackBlock, 0, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, 0, 0, CreateFileTypeNone, 0, IO_NO_PARAMETER_CHECKING);
	if (!NT_SUCCESS(Status))
	{
		return Status;
	}
	Status = ObReferenceObjectByHandle(FileHandle, GENERIC_READ, *IoFileObjectType, KernelMode, &FileObject, NULL);
	if (!NT_SUCCESS(Status) || !FileObject || !FileObject->FileName.Length || !FileObject->FileName.Buffer)
	{
		ZwClose(FileHandle);
		return STATUS_INVALID_PARAMETER;
	}
	{
		UNICODE_STRING VolumeName;
		UNICODE_STRING FinalName;
		UINT32	WillFreeVolumeName = TRUE;
		VolumeName.Buffer = NULL;
		VolumeName.Length = 0;
		VolumeName.MaximumLength = 0;
		Status = RtlVolumeDeviceToDosName(FileObject->DeviceObject, &VolumeName);
		if (!NT_SUCCESS(Status))
		{
			RtlInitUnicodeString(&VolumeName, L"\\");
			WillFreeVolumeName = FALSE;
		}
		if (FileObject->FileName.Length + VolumeName.Length >= 1024)
		{
			ObDereferenceObject(FileObject);
			ZwClose(FileHandle);
			if (WillFreeVolumeName && MmIsAddressValid(VolumeName.Buffer))
			{
				ExFreePool(VolumeName.Buffer);
			}
			return STATUS_INVALID_PARAMETER;
		}

		RtlZeroMemory((PVOID)OutFileName, 1024);
		RtlInitUnicodeString(&FinalName, OutFileName);
		FinalName.MaximumLength = 1024;
		if (!NT_SUCCESS(RtlAppendUnicodeStringToString(&FinalName, &VolumeName)))
		{
			ObDereferenceObject(FileObject);
			ZwClose(FileHandle);
			if (WillFreeVolumeName && MmIsAddressValid(VolumeName.Buffer))
			{
				ExFreePool(VolumeName.Buffer);
			}
			return STATUS_INVALID_PARAMETER;
		}

		if (!NT_SUCCESS(RtlAppendUnicodeStringToString(&FinalName, &FileObject->FileName)))
		{
			ObDereferenceObject(FileObject);
			ZwClose(FileHandle);
			if (WillFreeVolumeName && MmIsAddressValid(VolumeName.Buffer))
			{
				ExFreePool(VolumeName.Buffer);
			}
			return STATUS_INVALID_PARAMETER;
		}
		ObDereferenceObject(FileObject);
		ZwClose(FileHandle);
		if (WillFreeVolumeName && MmIsAddressValid(VolumeName.Buffer))
		{
			ExFreePool(VolumeName.Buffer);
		}
		return STATUS_SUCCESS;
	}
}
NTSTATUS GetProcessImagePath(IN  ULONG dwProcessId, OUT PUNICODE_STRING ProcessImagePath)
{
	NTSTATUS Status;
	HANDLE hProcess;
	PEPROCESS pEprocess;
	ULONG returnedLength;
	ULONG bufferLength;
	PVOID buffer;
	PUNICODE_STRING imageName;
	PAGED_CODE();
	if (NULL == ZwQueryInformationProcess)
	{
		UNICODE_STRING routineName;
		RtlInitUnicodeString(&routineName, L"ZwQueryInformationProcess");
		ZwQueryInformationProcess = (ZWQUERYINFORMATIONPROCESS)MmGetSystemRoutineAddress(&routineName);
		if (NULL == ZwQueryInformationProcess)
		{
			return STATUS_INSUFFICIENT_RESOURCES;
		}
	}
	Status = PsLookupProcessByProcessId((HANDLE)dwProcessId, &pEprocess);
	if (!NT_SUCCESS(Status))
		return Status;
	Status = ObOpenObjectByPointer(pEprocess, OBJ_KERNEL_HANDLE, NULL, GENERIC_READ, *PsProcessType, KernelMode, &hProcess);
	if (!NT_SUCCESS(Status))
		return Status;
	Status = ZwQueryInformationProcess(hProcess, ProcessImageFileName, NULL, 0, &returnedLength);
	if (STATUS_INFO_LENGTH_MISMATCH != Status)
	{
		return Status;
	}
	bufferLength = returnedLength - sizeof(UNICODE_STRING);
	if (ProcessImagePath->MaximumLength < bufferLength)
	{
		ProcessImagePath->Length = (USHORT)bufferLength;
		return STATUS_BUFFER_OVERFLOW;
	}
	buffer = ExAllocatePoolWithTag(PagedPool, returnedLength, 'GmcN');
	if (NULL == buffer)
		return STATUS_INSUFFICIENT_RESOURCES;
	Status = ZwQueryInformationProcess(hProcess, ProcessImageFileName, buffer, returnedLength, &returnedLength);
	if (NT_SUCCESS(Status))
	{
		RtlCopyUnicodeString(ProcessImagePath, (PUNICODE_STRING)buffer);
		Status = ImagePathConvertToRealPath(ProcessImagePath->Buffer, ProcessImagePath->Buffer);
	}
	ZwClose(hProcess);
	ExFreePoolWithTag(buffer, 'GmcN');
	return Status;

}

简单的通过PID获取文件路径

这里有个点就是,我所使用的是VS2019的WDK

而不是VS2022的,有个函数有略微不同

就是

PVOID ExAllocatePoolWithTag(
  [in] __drv_strictTypeMatch(__drv_typeExpr)POOL_TYPE PoolType,
  [in] SIZE_T                                         NumberOfBytes,
  [in] ULONG                                          Tag
);

此函数在Windows10 2004已经弃用了

我们要追求更新的话可以使用另一个函数

DECLSPEC_RESTRICT PVOID ExAllocatePool2(
  POOL_FLAGS Flags,
  SIZE_T     NumberOfBytes,
  ULONG      Tag
);

当然,对于初学者来说,这够了


接下来是主函数 以及通信函数

#define CTL_GETPATHBYPID (DWORD32)CTL_CODE(FILE_DEVICE_UNKNOWN,0xDD1,METHOD_BUFFERED,FILE_ALL_ACCESS)
UNICODE_STRING symbol_link;
UNICODE_STRING device_name;
PDEVICE_OBJECT device;
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING theRegistryPath)
{
	NTSTATUS status;
	int i = 0;
	UNICODE_STRING device_name = RTL_CONSTANT_STRING(L"\\Device\\GmcNt");
	status = IoCreateDevice(DriverObject, NULL, &device_name, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &device);
	for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
		DriverObject->MajorFunction[i] = OnMaJor;
	if (!NT_SUCCESS(status))
	{
		DbgPrint("Failed\n");
		return status;
	}
	if (IoIsWdmVersionAvailable(1, 0x10))
	{
		RtlInitUnicodeString(&symbol_link, L"\\DosDevices\\Global\\GmcNtSymbol");
	}
	else
	{
		RtlInitUnicodeString(&symbol_link, L"\\DosDevices\\GmcNtSymbol");
	}
	IoCreateSymbolicLink(&symbol_link, &device_name);

	DriverObject->DriverUnload = DriverUnload;						//DriverUnLoad 例程
	DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IoControl; //IoControl 例程注册
	return STATUS_SUCCESS;
}
void DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
	IoDeleteDevice(DriverObject->DeviceObject);
}
NTSTATUS OnMaJor(PDEVICE_OBJECT device, PIRP irp)
{
	irp->IoStatus.Information = 0;
	irp->IoStatus.Status = STATUS_SUCCESS;
	IoCompleteRequest(irp, IO_NO_INCREMENT);
	return STATUS_SUCCESS;
}
NTSTATUS IoControl(PDEVICE_OBJECT DriverObject, PIRP Irp)
{
	PIO_STACK_LOCATION Irpsp = IoGetCurrentIrpStackLocation(Irp);
	DWORD32 Code = Irpsp->Parameters.DeviceIoControl.IoControlCode;
	DWORD32 In_Size = Irpsp->Parameters.DeviceIoControl.InputBufferLength;
	DWORD32 Out_Size = Irpsp->Parameters.DeviceIoControl.OutputBufferLength;
	PVOID Buffer = Irp->AssociatedIrp.SystemBuffer;
	NTSTATUS Status;
	INT Res;
	Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
	Irp->IoStatus.Information = 0;

	//===========================================//
	if (Code == CTL_GETPATHBYPID)
	{
		if (MmIsAddressValid(Buffer))
		{
			UNICODE_STRING Uni = { 0 };
			Uni.MaximumLength = 1024;
			Uni.Buffer = Buffer;
			Res = GetProcessImagePath(*(PULONG)Buffer, &Uni);
			Irp->IoStatus.Status = Res;
			Irp->IoStatus.Information = 1024;
		}
	}
	//============================================//
Exit:
	//============================================//	
	IoCompleteRequest(Irp, IO_NO_INCREMENT);
	return Irp->IoStatus.Status;
}

相信大家都看得懂 这下面是逐行分析

  1. if (MmIsAddressValid(Buffer)):这是一个条件语句,调用MmIsAddressValid函数来检查指针Buffer指向的内存地址是否有效。MmIsAddressValid是Windows内核中的一个函数,用于验证地址是否可以在当前上下文中安全地访问。如果地址无效,访问它可能会导致系统崩溃。
  2. UNICODE_STRING Uni = { 0 };:声明并初始化一个UNICODE_STRING结构体UniUNICODE_STRING是Windows内核中用来处理Unicode字符串的一种结构。这里将所有成员初始化为0意味着长度等信息被设置为初始状态。
  3. Uni.MaximumLength = 1024;:设置UniMaximumLength成员,即这个Unicode字符串的最大长度为1024字节。
  4. Uni.Buffer = Buffer;:设置UniBuffer成员,使其指向此前检查有效性的Buffer,假设此Buffer是一个指向路径字符串的指针。
  5. Res = GetProcessImagePath(*(PULONG)Buffer, &Uni);:调用一个名为GetProcessImagePath的函数,这个函数的功能是获取一个进程的图像路径(可能是可执行文件的完整路径)。这个函数接受两个参数:第一个参数是通过将Buffer指针转换为PULONG类型指针并解引用来获取的值,第二个参数是指向之前声明的UNICODE_STRING结构的指针。变量Res用来接收函数的返回值,可能代表状态或错误码。
  6. Irp->IoStatus.Status = Res;:设置I/O请求数据包(IRP)的IoStatus.Status字段为Res。IRP是内核和驱动程序之间传递信息的数据结构,在这里代表着操作的状态。
  7. Irp->IoStatus.Information = 1024;:设置IRP的IoStatus.Information字段为1024,通常这个值用来表示操作返回了多少信息,比如读写了多少字节

大概就这么多了,希望此文章对初学者有所帮助

作者bilibili

0殺神在心0的个人空间-0殺神在心0个人主页-哔哩哔哩视频 (bilibili.com)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值