使用CTL_CODE宏定义来定义一个控制码:
#define MY_PORT CTL_CODE(\ FILE_DEVICE_UNKNOWN, \ 0x801, \ METHOD_OUT_DIRECT, \ FILE_ANY_ACCESS) //向内核传递一个用户的等待事件 #define IOCTL_SET_EVENT CTL_CODE(\ FILE_DEVICE_UNKNOWN, \ 0x802, \ METHOD_BUFFERED, \ FILE_ANY_ACCESS)
1.驱动层处理:
在 DriverEntry 入口函数中添加:
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DDKDispatchDvCtl;
全局变量:
struct _MY_FUNCTION_INFO { CHAR FunctionName[150]; ULONG FunctionAddr; }; HANDLE hUserEvent = NULL; PKEVENT pEvent = NULL; _MY_FUNCTION_INFO* InputBuffer = NULL;
DDKDispatchDvCtl 函数:
#pragma PAGEDCODE NTSTATUS DDKDispatchDvCtl ( IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) { KdPrint(("Entry DDKDispatchDvCtl\r\n")); NTSTATUS status; PIO_STACK_LOCATION io_stack; io_stack = IoGetCurrentIrpStackLocation(pIrp); //得到输入缓冲区大小 ULONG cbin = io_stack->Parameters.DeviceIoControl.InputBufferLength; //得到输出缓冲区大小 ULONG cbout = io_stack->Parameters.DeviceIoControl.OutputBufferLength; //得到IOCTL码 ULONG code = io_stack->Parameters.DeviceIoControl.IoControlCode; if (io_stack->MajorFunction == IRP_MJ_DEVICE_CONTROL) { switch (code) { case IOCTL_SET_EVENT: //把传递进来的用户层等待事件取出来 hUserEvent = *(HANDLE *)pIrp->AssociatedIrp.SystemBuffer; //将用户层事件转化为内核等待对象 status = ObReferenceObjectByHandle(hUserEvent, EVENT_MODIFY_STATE, *ExEventObjectType, KernelMode, (PVOID*)&pEvent, NULL); KdPrint(("[IOCTL_SET_EVENT] status = %d\n", status));//status应该为0才对 ObDereferenceObject(pEvent); break; case INITIALIZE_TO_R0_PORT: { //得到 InputBuffer InputBuffer = (_MY_FUNCTION_INFO*)pIrp->AssociatedIrp.SystemBuffer; //获得从应用层传递的信息 KdPrint(("[INITIALIZE_TO_R0_PORT] Initialize Name : %s", InputBuffer->FunctionName)); KdPrint(("[INITIALIZE_TO_R0_PORT] Initialize Addr : 0x%08x", InputBuffer->FunctionAddr)); //获得用户层传入的 输出字符串指针, 就可以任意改变字符串内容 PCHAR OutputChar = (CHAR*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority); strcpy(OutputChar, "DbgkpMarkProcessPeb"); //激活Event事件 KeSetEvent(pEvent, IO_NO_INCREMENT, FALSE); //pIrp->IoStatus.Information 设置为 输出缓冲区大小 cbout, METHOD_OUT_DIRECT模式不用设置,设置为0就行了 pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = STATUS_SUCCESS; KdPrint(("[INITIALIZE_TO_R0_PORT] Success.\r\n")); } break; default: pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = STATUS_INVALID_PARAMETER; break; } } IoCompleteRequest(pIrp, IO_NO_INCREMENT); return pIrp->IoStatus.Status; }
2.用户层处理:
全局变量:
_MY_FUNCTION_INFO myOutputFuncInfo; CHAR myInputFuncName[150] = {0}; HANDLE hDevice = NULL;
void myFunction() { BOOL ret = FALSE; DWORD ret_length = 0; hDevice = CreateFile( L"\\\\.\\Legend1900", //Legend1900为自定义设备名 GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (hDevice == INVALID_HANDLE_VALUE) { OutputDebugString(L"Open Device Fail"); return FALSE; } // // 1. 创建用户层的等待事件,传入内核 // 2. 创建线程,用于监测内核事件的到来 // HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); HANDLE hThread = (HANDLE)_beginthreadex( NULL, 0, R3ToR0ThreadProc, //自定义函数 hEvent, //自定义函数的参数 0, NULL); //先将用户层的等待 Event 传入内核 DeviceIoControl( hDevice, IOCTL_SET_EVENT, &hEvent, sizeof(hEvent), NULL, 0, &ret_length, NULL); strcpy_s(myOutputFuncInfo.FunctionName, "我是传奇."); myOutputFuncInfo.FunctionAddr = 0xFFFFFFFF; ret = DeviceIoControl( hDevice, INITIALIZE_TO_R0_PORT, &myOutputFuncInfo, sizeof(_MY_FUNCTION_INFO), &myInputFuncName, 150, &ret_length, NULL); if (!ret) { OutputDebugString(L"DeviceIoControl Fail"); return FALSE; } }
//通信线程 UINT _stdcall R3ToR0ThreadProc(LPVOID para) { BOOL ret = FALSE; TCHAR FuncName[150] = {0}; while (1) { WaitForSingleObject((HANDLE)para, INFINITE); ResetEvent((HANDLE)para); OutputDebugStringA("In [R3ToR0ThreadProc]"); //将内核文件名(不包含目录) 由char* 转 wchar_t* MultiByteToWideChar( CP_ACP, 0, myInputFuncName, strlen(myInputFuncName) + 1, FuncName, 150); OutputDebugString(FuncName); } return 0; }
输出打印信息为:
注:
与 < METHOD_BUFFERED 缓冲输入输出I/O > 不同的是:
METHOD_BUFFERED方式 , 每次都是 把 myOutputFuncInfo的内容复制到 pIrp->AssociatedIrp.SystemBuffer,
再 修改 pIrp->AssociatedIrp.SystemBuffer, 系统再将 SystemBuffer 里的内容复制到 myInputFuncName.
而:
METHOD_OUT_DIRECT方式, 虽然也是每次都需要把 myOutputFuncInfo 的内容复制到 pIrp->AssociatedIrp.SystemBuffer,
但是 通过 PCHAR OutputChar = (CHAR*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority) ,
将 myInputFuncName 指针 给了 OutputChar, 这样 在任意时刻修改 OutputChar 就相当于修改了 myInputFuncName,
不需要 每次复制来复制去.