大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺
驱动层与应用层通信是通过DeviceIoControl,
符号定义
#define DEVICE_NAME L"\\Device\\myDriver" // Driver Name
#define SYMBOLIC_LINK_NAME L"\\DosDevices\\myDriver" // Symbolic Link Name
#define WIN32_LINK_NAME "\\\\.\\myDriver" // Win32 Link Name
//
// Device IO Control Codes
//
#define IOCTL_BASE 0x800
#define MY_CTL_CODE(i) \
CTL_CODE \
( \
FILE_DEVICE_UNKNOWN, \
IOCTL_BASE + i, \
METHOD_BUFFERED, \
FILE_ANY_ACCESS \
)
// ===========内存数据结构, 不会用来做多端的数据传递======================
// ===========用户自己可以存些需要在内存中传递的数据======================
typedef struct _DEV_EXTENSION {
INT32 iTest;
WCHAR wLastPath[512];
}DEV_EXTENSION, * PDEV_EXTENSION;
driverentry
NTSTATUS DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
NTSTATUS status = STATUS_SUCCESS;
UNICODE_STRING uszDriverString;
UNICODE_STRING uszDeviceString;
PDEVICE_OBJECT pDeviceObject = NULL;
PDEV_EXTENSION pDevExt = NULL;
HANDLE hThread = NULL;
DbgPrint("MyDriver DriverEntry\n");
// Point uszDriverString at the driver name
RtlInitUnicodeString(&uszDriverString, DEVICE_NAME);
// Create and initialize device object
status = IoCreateDevice(
DriverObject,
sizeof(DEV_EXTENSION),
&uszDriverString,
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&pDeviceObject
);
if (!NT_SUCCESS(status))
{
DbgPrint("RegistryMonitor: ERROR IoCreateDevice - %08x\n", status);
return status;
}
// 初始化设备扩展
gpDeviceObject = pDeviceObject;
pDevExt = (DEV_EXTENSION*)pDeviceObject->DeviceExtension;
pDevExt->iTest = 0;
wcscpy_s(pDevExt->wLastPath, sizeof(pDevExt->wLastPath)/sizeof(pDevExt->wLastPath[0]), L"");
/* Point uszDeviceString at the device name */
RtlInitUnicodeString(&uszDeviceString, SYMBOLIC_LINK_NAME);
/* Create symbolic link to the user-visible name */
status = IoCreateSymbolicLink(&uszDeviceString, &uszDriverString);
if (!NT_SUCCESS(status))
{
DbgPrintEx("RegistryMonitor: ERROR IoCreateSymbolicLink - %08x\n", status);
IoDeleteDevice(pDeviceObject);
return status;
}
// Load structure to point to IRP handlers
DriverObject->DriverUnload = UnloadDriver;
DriverObject->MajorFunction[IRP_MJ_CREATE] = KDispatchCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = KDispatchCreateClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KDispatchIoctl;
return STATUS_SUCCESS;
}
应用层要访问的符号链接是\\.\myDriver
首先驱动层要实现:
pDriverObject->DriverUnload = UnloadDriver;
pDriverObject->MajorFunction[IRP_MJ_CREATE] = KDispatchCreateClose;
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = KDispatchCreateClose;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KDispatchIoctl;
Create和Close函数必须要实现,否则CreateFile的时候可能会报错
NTSTATUS KDispatchCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
DbgPrint("create or close happen\n");
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
卸载例程
void UnloadDriver(IN PDRIVER_OBJECT DriverObject)
{
UNICODE_STRING uszDeviceString;
NTSTATUS ntStatus;
RtlInitUnicodeString(&uszDeviceString, SYMBOLIC_LINK_NAME);
IoDeleteSymbolicLink(&uszDeviceString);
if (DriverObject->DeviceObject != NULL)
{
IoDeleteDevice(DriverObject->DeviceObject);
}
DbgPrint("Unload Success \n");
}
其中MyDispatchDeviceControl用来与应用层通过DeviceIoControl通信
NTSTATUS KDispatchIoctl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
UINT dwDataWritten = 0;
NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; // STATUS_UNSUCCESSFUL
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(Irp);
ULONG uIoControlCode = 0;
PVOID pIoBuffer = NULL;
ULONG uInSize = 0;
ULONG uOutSize = 0;
int* pReturn = NULL;
PDEV_EXTENSION deviceExtension = (DEV_EXTENSION*)DeviceObject->DeviceExtension;
NTSTATUS ntStatus;
ANSI_STRING imagePath; //进程路径
CHAR szImageName[300]; //进程镜像名
HANDLE pid = PsGetCurrentProcessId();
ULONG information = 0;
// Get the IoCtrl Code
uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
pIoBuffer = Irp->AssociatedIrp.SystemBuffer;
uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
if (DeviceObject == gpDeviceObject)
{
switch (uIoControlCode)
{
case MY_CTL_CODE(1):
{
DbgPrint("buffer:%p,%d,%d\n", pIoBuffer, uInSize, uOutSize);
wcscpy_s(gTargetUserSid, sizeof(gTargetUserSid) / sizeof(gTargetUserSid[0]), (WCHAR*)pIoBuffer);
DbgPrint("get str:%ws, size:%d\n", gTargetUserSid, uInSize);
wcscpy_s((WCHAR*)pIoBuffer, uOutSize, L"ok");
information = 6;
status = STATUS_SUCCESS;
}
break;
default:
{
// Invalid code sent
DbgPrint("[HookDeviceIocontrol] Unknown IOCTL: 0x%X (%04X,%04X)\r\n",
uIoControlCode,
DEVICE_TYPE_FROM_CTL_CODE(uIoControlCode),
IoGetFunctionCodeFromCtlCode(uIoControlCode));
status = STATUS_INVALID_PARAMETER;
}
}
Irp->IoStatus.Information = information;
// ***************注意一定要在自己要处理的ctlcode中修改status和information*******
// ***************不然deviceIoControl会报错************************************
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
else
{
Irp->IoStatus.Information = 0;
// Complete the I/O Request
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
}
然后应用层要打开驱动层的设备链接符号,来得到句柄,DeviceIoControl的通信依赖句柄
void TestDriver()
{
BOOL bRet = TRUE;
DWORD dwReturnSize = 0;
WCHAR wzIn[MAX_PATH], wzOut[MAX_PATH];
WCHAR symbolPath[MAX_PATH];
HANDLE hSymbol = NULL;
// 访问WIN32_LINK_NAME
wcscpy_s(symbolPath, sizeof(symbolPath) / sizeof(symbolPath[0]), L"\\\\.\\myDriver");
wcscpy_s(wzIn, sizeof(wzIn)/sizeof(wzIn[0]), L"hello world_000");
hSymbol = CreateFileW(
symbolPath,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
if (INVALID_HANDLE_VALUE == hSymbol) {
WriteLogEx(L"crete file:%s failed, error:%d\n", symbolPath, GetLastError());
return;
}
DWORD dwInBufferSize = (wcslen(wzIn) + 1) * sizeof(WCHAR);
bRet = DeviceIoControl(hSymbol, MY_CTL_CODE(1), wzIn, dwInBufferSize,
wzOut, sizeof(wzOut), &dwReturnSize, NULL);
if (!bRet) {
WriteLogEx(L"DeviceIoControl:%s,size:%d failed, error:%d\n", symbolPath, dwInBufferSize, GetLastError());
CloseHandle(hSymbol);
return;
}
CloseHandle(hSymbol);
WriteLogEx(L"done\n");
}
遇到的坑:
1.CreatFile失败
因为没有实现
DriverObject->MajorFunction[IRP_MJ_CREATE] = KDispatchCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = KDispatchCreateClose;
2.deviceIocontrol返回失败,可是驱动层明明收到数据了
因为没有把返回值status调整为STATUS_SUCCESS,只是等于了一个初始化不会STATUS_SUCCESS的值
3.DeviceIoControl传到驱动层了,但是没有读到数据
一开始是读到了的,后来因为排查问题的时候以为是CtlCode的问题,于是随意调整了一下MY_CTL_CODE的第三个参数METHOD_BUFFERED,凑巧发现这个值影响到驱动层读取DeviceIoControl传递的数据,搜索一番发现微软官方是有定义的
Defining I/O Control Codes – Windows drivers | Microsoft Docsh
Method与内存传输的关系
1..METHOD_BUFFERED:缓冲区模式
inbuffer的内容被复制到IRP中的pIrp->AssociatedIrp.SystemBuffer复制的长度是DeviceIoControl中指定的nInBufferSize。
驱动返回数据时,也是向pIrp->AssociatedIrp.SystemBuffer中写入,操作系统会将数据复制到DeviceIoControl的outBuffer,复制的字节数是pIrp->IoStatus.Information, 这个数值由驱动指定。
派遣函数读取关键信息的代码如下:
uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
pIoBuffer = Irp->AssociatedIrp.SystemBuffer;
uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
设置返回数据的代码如下
wcscpy_s((WCHAR*)pIoBuffer, uOutSize, L"ok");
Irp->IoStatus.Information = 6;
// Complete the I/O Request
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
2 METHOD_IN_DIRECT与METHOD_OUT_DIRECT 直接内存模式
与缓冲模式一样,DeviceIoControl中的inBuffer数据被复制到IRP中的pIrp->AssociatedIrp.SystemBuffer,复制的长度是DeviceIoControl指定的nInBufferSize。
直接内存模式中,操作系统会将DeviceIoControl指定的输出缓冲区锁定,然后在内核模式地址下重新映射一段地址。
派遣函数中IRP中的pIrp->MdlAddress记录DeviceIoControl指定的输出缓冲区。派遣函数应该使用MmGetSystemAddressForMdlSafe将这段内存映射到内核模式下的内存地址。
得到输入输出缓冲区的大小以及IOCTL的方式与缓冲区模式相同。
另外需要注意CTL_CODE设置的权限问题,若以只读方式打开设备,METHOD_IN_DIRECT的IOCTL操作会失败。
派遣函数中处理直接内存模式:
//显示输入缓冲区数据
UCHAR* InputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
for (ULONG i=0;i<cbin;i++)
{
DbgPrint("%X\n",InputBuffer[i]);
}
//pIrp->MdlAddress为DeviceIoControl输出缓冲区地址相同
DbgPrint("User Address:0X%08X\n",MmGetMdlVirtualAddress(pIrp->MdlAddress));
返回数据
UCHAR* OutputBuffer = (UCHAR*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority);
//InputBuffer被映射到内核模式下的内存地址,必定在0X80000000-0XFFFFFFFF之间memset(OutputBuffer,0xAA,cbout);
3:METHOD_NEITHER :Neither模式
因为此模式直接访问用户模式地址,这是很危险的,所以此模式很少被用到。
使用用户模式地址必须保证调用DeviceIoControl 的线程与派遣函数运行在同一个线程上下文中。
派遣函数得到输入缓冲区的方式与前两种不同,此模式是通过IO堆栈的stack->Parameters.DeviceIoControl.Type3InputBuffer;得到输入缓冲区。
驱动通过pIrp->UserBuffer得到输出缓冲区。
得到输入输出缓冲区的长度与IOCTL的方式与前两种相同。
由于驱动程序的派遣函数不能保证传递进来的用户地址是合法地址,所以要对传入的用户模式地址进行可读写判断。这就需要ProbeForRead函数和ProbeForWrite函数与_try _execpt 结合使用。
下面是驱动派遣函数中Neither模式
//显示输入缓冲区数据
UCHAR* UserInputBuffer = (UCHAR*)stack->Parameters.DeviceIoControl.Type3InputBuffer;
KdPrint(("UserInputBuffer:0X%0X\n",UserInputBuffer));
//得到用户模式地址
PVOID UserOutputBuffer = pIrp->UserBuffer;
KdPrint(("UserOutputBuffer:0X%0X\n",UserOutputBuffer));
__try
{
KdPrint(("Enter __try block\n"));
//判断指针是否可读
ProbeForRead(UserInputBuffer,cbin,4);
//显示输入缓冲区内容
for (ULONG i=0;i<cbin;i++)
{
KdPrint(("%X\n",UserInputBuffer[i]));
}
//判断指针是否可写
ProbeForWrite(UserOutputBuffer,cbout,4);
//操作输出缓冲区
memset(UserOutputBuffer,0xAA,cbout);
//如果在上面引发异常,所以以后语句不会被执行!
pIrp->IoStatus.Information = cbout;
KdPrint(("Leave __try block\n"));
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
KdPrint(("Catch the exception\n"));
KdPrint(("The program will keep going\n"));
status = STATUS_UNSUCCESSFUL;
}
pIrp->IoStatus.Information = cbout;
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/196464.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...