我们知道,驱动有时候需要和应用层结合
这时候我们就在想,能不能将他们结合起来
可能有时候,初学者只知道如何给应用层->驱动层
不知道驱动层->应用层如何做
这里我们举个简单的例子
要用到的头文件
#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;
}
相信大家都看得懂 这下面是逐行分析
if (MmIsAddressValid(Buffer))
:这是一个条件语句,调用MmIsAddressValid
函数来检查指针Buffer
指向的内存地址是否有效。MmIsAddressValid
是Windows内核中的一个函数,用于验证地址是否可以在当前上下文中安全地访问。如果地址无效,访问它可能会导致系统崩溃。UNICODE_STRING Uni = { 0 };
:声明并初始化一个UNICODE_STRING
结构体Uni
。UNICODE_STRING
是Windows内核中用来处理Unicode字符串的一种结构。这里将所有成员初始化为0
意味着长度等信息被设置为初始状态。Uni.MaximumLength = 1024;
:设置Uni
的MaximumLength
成员,即这个Unicode字符串的最大长度为1024字节。Uni.Buffer = Buffer;
:设置Uni
的Buffer
成员,使其指向此前检查有效性的Buffer
,假设此Buffer
是一个指向路径字符串的指针。Res = GetProcessImagePath(*(PULONG)Buffer, &Uni);
:调用一个名为GetProcessImagePath
的函数,这个函数的功能是获取一个进程的图像路径(可能是可执行文件的完整路径)。这个函数接受两个参数:第一个参数是通过将Buffer
指针转换为PULONG
类型指针并解引用来获取的值,第二个参数是指向之前声明的UNICODE_STRING
结构的指针。变量Res
用来接收函数的返回值,可能代表状态或错误码。Irp->IoStatus.Status = Res;
:设置I/O请求数据包(IRP)的IoStatus.Status
字段为Res
。IRP是内核和驱动程序之间传递信息的数据结构,在这里代表着操作的状态。Irp->IoStatus.Information = 1024;
:设置IRP的IoStatus.Information
字段为1024,通常这个值用来表示操作返回了多少信息,比如读写了多少字节
大概就这么多了,希望此文章对初学者有所帮助
作者bilibili