METHOD_OUT_DIRECT 缓冲输入直接输出I/O

使用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,

不需要 每次复制来复制去.

 

posted @ 2015-01-19 23:01  银河彼岸  阅读(702)  评论(0编辑  收藏  举报