玩玩DriverWorks(二)-讀寫數(shù)據(jù)
這兩天怎么老想玩DriverWorks!!!
今天抽了一個小時寫個小驅(qū)動練習(xí)一下KIrp和KMemory配合訪問三種IO方式中的數(shù)據(jù).KIrp是對IRP請求對象的包裝,它有很多成員函數(shù)用于操作IRP結(jié)構(gòu).
1.Buffered IO:
在Buffered IO方式中IO管理器會分配一塊堆內(nèi)存然后把用戶態(tài)緩沖區(qū)數(shù)據(jù)copy進(jìn)來再傳給驅(qū)動程序,而輸出時IO管理器會把堆內(nèi)存中的數(shù)據(jù)copy回用戶態(tài)緩沖區(qū).
我們使用下面這樣的代碼來得到輸入/輸出緩沖區(qū)和大小
KIrp I;
....
ULONG inputSize = I.IoctlInputBufferSize();
ULONG outputSize = I.IoctlOutputBufferSize();
PVOID inputBuffer = I.IoctlBuffer();
PVOID outputBuffer = I.IoctlBuffer();
一目了然吧!
我想要介紹一下這幾個函數(shù),但你實際上可以跳過下面的一小段因為用DriverWorks根本用不著知道這些細(xì)節(jié).
IoctlBuffer,IoctlInputBufferSize,IoctlOutputBufferSize函數(shù)實現(xiàn)非常簡單:
inline PVOID& KIrp::IoctlBuffer(void)
{
return m_Irp->AssociatedIrp.SystemBuffer;
}
inline ULONG& KIrp::IoctlOutputBufferSize(EStackLocation s)
{
ValidateStackLocation(s);
if (s == CURRENT)
return IoGetCurrentIrpStackLocation(m_Irp)->
Parameters.DeviceIoControl.OutputBufferLength;
else
return IoGetNextIrpStackLocation(m_Irp)->
Parameters.DeviceIoControl.OutputBufferLength;
}
inline ULONG& KIrp::IoctlInputBufferSize(EStackLocation s)
{
ValidateStackLocation(s);
if (s == CURRENT)
return IoGetCurrentIrpStackLocation(m_Irp)->
Parameters.DeviceIoControl.InputBufferLength;
else
return IoGetNextIrpStackLocation(m_Irp)->
Parameters.DeviceIoControl.InputBufferLength;
}
在IoctlOutputBufferSize和IoctlInputBufferSize中S有一個默認(rèn)參數(shù)CURRENT,上面的代碼就是這樣調(diào)用它們的.可以看到其實是對IO_STACK_LOCATION結(jié)構(gòu)直接的操作.
2.Direct IO:
Direct IO方式中IO管理器分將用戶態(tài)緩沖區(qū)映射到核心態(tài)(地址不一樣,但其實是一塊內(nèi)存),并鎖定內(nèi)存不要分頁機(jī)制將內(nèi)存交換到外存中.然后IO管理器將緩沖區(qū)在核心態(tài)的的地址傳給驅(qū)動程序.Direct IO的分成METHOD_IN_DIRECT和METHOD_OUT_DIRECT
2.1 在METHOD_IN_DIRECT中我們使用這樣的代碼來訪問緩沖區(qū)及大小
KIrp I;
....
KMemory Mem(I.Mdl());
PUCHAR pBuffer = (PUCHAR) Mem.MapToSystemSpace();
ULONG writeSize = I.WriteSize();
ULONG bytesSent = 0;
2.2 在METHOD_OUT_DIRECT中要像這樣來訪問緩沖區(qū)及大小
KIrp I;
....
KMemory Mem(I.Mdl());
PUCHAR pBuffer = (PUCHAR) Mem.MapToSystemSpace();
ULONG readSize = I.ReadSize();
ULONG bytesRead = 0;
I.Mdl函數(shù)是這樣實現(xiàn)的:
inline PMDL& KIrp::Mdl(void)
{
return m_Irp->MdlAddress;
}
它直接返回IRP結(jié)構(gòu)的MdlAddress字段,而MapToSystemSpace的實現(xiàn)代碼是這樣的:
inline PVOID KMemory::MapToSystemSpace(void)
{
return GetSystemAddressForMdl(m_pMdl);
}
它調(diào)用下面這個函數(shù),這里我不太明白為什么不直接調(diào)用MmGetSystemAddressForMdl
inline PVOID GetSystemAddressForMdl(PMDL pMdl)
{
CSHORT canFail; // original fail flag in MDL
if(!(pMdl->MdlFlags &
(MDL_MAPPED_TO_SYSTEM_VA | MDL_SOURCE_IS_NONPAGED_POOL))) {
// get the current fail flag
canFail = (CSHORT)(pMdl->MdlFlags & MDL_MAPPING_CAN_FAIL);
// set 'mapping can fail' so we don't bugcheck
// if MmMapLockedPages fails
pMdl->MdlFlags |= MDL_MAPPING_CAN_FAIL;
// try to map the buffer
pMdl->MappedSystemVa =
(PVOID)(((ULONG_PTR)MmMapLockedPages(pMdl, KernelMode)) |
MmGetMdlByteOffset(pMdl));
// set the original flag back in the MDL
if(!canFail) {
pMdl->MdlFlags &= ~MDL_MAPPING_CAN_FAIL;
}
}
return pMdl->MappedSystemVa;
}
3 Neither IO:
在Neither IO,IO管理器直接把用戶態(tài)的緩沖區(qū)地址和大小傳給驅(qū)動,不做任何處理.這是很危險的,通常情況下,我們需要寫一些小的助手驅(qū)動以進(jìn)入Ring0或是調(diào)用一些底層功能,如果可以肯定驅(qū)動的派遣函是運行在被動級并且在我們自己指定程序的進(jìn)程上下文里,那么用用這種方式也無所謂.除此以外盡可能不用這樣的IO方式.通常需要使用這樣的代碼來訪問緩沖區(qū)和大小:
KIrp I;
....
ULONG inputSize = I.IoctlInputBufferSize();
ULONG outputSize = I.IoctlOutputBufferSize();
PVOID inputBuffer = I.IoctlType3InputBuffer();
PVOID outputBuffer = I.UserBuffer();
IoctlType3InputBuffer和UserBuffer的實現(xiàn)代碼:
inline PVOID& KIrp::IoctlType3InputBuffer(EStackLocation s)
{
ValidateStackLocation(s);
if (s == CURRENT)
return IoGetCurrentIrpStackLocation(m_Irp)->
Parameters.DeviceIoControl.Type3InputBuffer;
else
return IoGetNextIrpStackLocation(m_Irp)->
Parameters.DeviceIoControl.Type3InputBuffer;
}
inline PVOID& KIrp::UserBuffer(void)
{
return m_Irp->UserBuffer;
}
4. 現(xiàn)在可以總結(jié)一下,根據(jù)這些我們可以學(xué)到在DDK層面上應(yīng)該怎么操作這些IO方式中的緩沖區(qū):
4.1 在驅(qū)動的派遣函數(shù)中可以使用IoGetCurrentIrpStackLocation得到IO_STACK_LOCATION可以根據(jù)它的Control字段確定是什么派遣函數(shù)(因為可以將多個派遣函數(shù)地址指向一個派遣函數(shù)來處理多個請求).在Control等于IRP_MJ_DEVICE_CONTROL時來要根據(jù)DeviceIoControl的子字段IoControlCode來確定是什么控制代碼.
4.2 對于Buffered IO和Neither IO可以使用它的DeviceIoControl字段和三個子字段來訪問緩沖區(qū)和大小參數(shù):
typedef struct _IO_STACK_LOCATION {
....
union {
...
struct {
ULONG OutputBufferLength;
ULONG InputBufferLength;
ULONG IoControlCode;
PVOID Type3InputBuffer;
} DeviceIoControl;
...
}
....
} IO_STACK_LOCATION, *PIO_STACK_LOCATION;
4.3 對于Direct IO則要使用MmGetSystemAddressForMdl來將IRP結(jié)構(gòu)的MdlAddress(內(nèi)存描述表)字段映射到內(nèi)核地址空間并鎖定,得到一個內(nèi)核空間地址才能進(jìn)一步操作.Direct IO方式中的輸入輸出大小參數(shù)由IO_STACK_LOCATION的Read.Length和Write.Length指示
typedef struct _IO_STACK_LOCATION {
....
union {
...
struct {
ULONG Length;
ULONG Key;
LARGE_INTEGER ByteOffset;
} Read;
struct {
ULONG Length;
ULONG Key;
LARGE_INTEGER ByteOffset;
} Write;
...
}
....
} IO_STACK_LOCATION, *PIO_STACK_LOCATION;
說不看庫源代碼的又忍不住開始分析,呵呵!其實可以把DriverWorks的對象類搞清楚怎么用就可以了.如果是想順便把自己的DDK開發(fā)能力也提高一下的話,庫的源代碼也可以參考.
有了昨天的DriverWorks基本介紹,我想下面的代碼應(yīng)該不是很難懂吧!
//Interface.h
#ifndef __INTERFACE_H__
#define __INTERFACE_H__
#define DEVICE_NAME L"IoDemoDevice"
#define FILE_DEVICE_IODEMO 0x8000
#define IOCTL_DO_BUFFERED_IO \
CTL_CODE(FILE_DEVICE_IODEMO,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define IOCTL_DO_DIRECT_IN \
CTL_CODE(FILE_DEVICE_IODEMO,0x801,METHOD_IN_DIRECT,FILE_ANY_ACCESS)
#define IOCTL_DO_DIRECT_OUT \
CTL_CODE(FILE_DEVICE_IODEMO,0x802,METHOD_OUT_DIRECT,FILE_ANY_ACCESS)
#define IOCTL_DO_NEITHER_IO \
CTL_CODE(FILE_DEVICE_IODEMO,0x803,METHOD_NEITHER,FILE_ANY_ACCESS)
#endif
//IODemo.cpp
#define VDW_MAIN
#define DRIVER_FUNCTION_UNLOAD
#define DRIVER_FUNCTION_CREATE
#define DRIVER_FUNCTION_CLOSE
#define DRIVER_FUNCTION_CLEANUP
#define DRIVER_FUNCTION_DEVICE_CONTROL
#include "vdw.h"
#include "Interface.h"
class IoDemoDriver:public KDriver
{
SAFE_DESTRUCTORS;
public:
virtual NTSTATUS DriverEntry(IN PUNICODE_STRING RegistryPath);
virtual VOID Unload();
};
DECLARE_DRIVER_CLASS(IoDemoDriver,NULL)
class IoDemoDevice:public KDevice
{
SAFE_DESTRUCTORS;
public:
IoDemoDevice();
DEVMEMBER_DISPATCHERS;
};
IoDemoDevice::IoDemoDevice():KDevice()
{
if (!NT_SUCCESS(m_ConstructorStatus))
{
DbgPrint(__FUNCTION__": Failed to create device MyDevice");
return;
}
}
NTSTATUS IoDemoDevice::Create(KIrp I)
{
DbgPrint(__FUNCTION__":IRP 0x%08X\n",I);
I.Information()=0;
I.Complete(STATUS_SUCCESS);
return STATUS_SUCCESS;
}
NTSTATUS IoDemoDevice::Close(KIrp I)
{
DbgPrint(__FUNCTION__":IRP 0x%08X\n",I);
I.Information()=0;
I.Complete(STATUS_SUCCESS);
return STATUS_SUCCESS;
}
NTSTATUS IoDemoDevice::CleanUp(KIrp I)
{
DbgPrint(__FUNCTION__":IRP 0x%08X\n",I);
I.Information()=0;
I.Complete(STATUS_SUCCESS);
return STATUS_SUCCESS;
}
NTSTATUS IoDemoDevice::DeviceControl(KIrp I)
{
DbgPrint(__FUNCTION__":IRP 0x%08X\n",I);
switch(I.IoctlCode())
{
case IOCTL_DO_BUFFERED_IO:
{
DbgPrint(__FUNCTION__":IOCTL_DO_BUFFERED_IO\n",I);
ULONG inputSize = I.IoctlInputBufferSize();
ULONG outputSize = I.IoctlOutputBufferSize();
PVOID inputBuffer = I.IoctlBuffer();
PVOID outputBuffer = I.IoctlBuffer();
//顯示出傳進(jìn)來的字符串
if(inputSize)
DbgPrint("inputBuffer:%s",(char*)inputBuffer);
//返回一個字符串
char chDoBufferedIO[]="DO_BUFFERED_IO";
strncpy((char*)outputBuffer,chDoBufferedIO,outputSize);
I.Information()=strlen(chDoBufferedIO);
}
break;
case IOCTL_DO_DIRECT_IN:
{
DbgPrint(__FUNCTION__":IOCTL_DO_DIRECT_IN\n",I);
KMemory Mem(I.Mdl());
PUCHAR pBuffer = (PUCHAR) Mem.MapToSystemSpace();
ULONG writeSize = I.WriteSize();
ULONG bytesSent = 0;
if(writeSize)
DbgPrint("pBuffer:%s",(char*)pBuffer);
I.Information()=0;
}
break;
case IOCTL_DO_DIRECT_OUT:
{
DbgPrint(__FUNCTION__":IOCTL_DO_DIRECT_OUT\n",I);
KMemory Mem(I.Mdl());
PUCHAR pBuffer = (PUCHAR) Mem.MapToSystemSpace();
ULONG readSize = I.ReadSize();
ULONG bytesRead = 0;
if(readSize)
{
char chDirectOut[]="DO_DIRECT_OUT";
strncpy((char*)pBuffer,chDirectOut,readSize);
I.Information()=strlen(chDirectOut);
}
}
break;
case IOCTL_DO_NEITHER_IO:
{
DbgPrint(__FUNCTION__":IOCTL_DO_NEITHER_IO\n",I);
ULONG inputSize = I.IoctlInputBufferSize();
ULONG outputSize = I.IoctlOutputBufferSize();
PVOID inputBuffer = I.IoctlType3InputBuffer();
PVOID outputBuffer = I.UserBuffer();
//顯示出傳進(jìn)來的字符串
if(inputSize)
DbgPrint("inputBuffer:%s",(char*)inputBuffer);
//返回一個字符串
char chDoNeitherIo[]="DO_NEITHER_IO";
strncpy((char*)outputBuffer,chDoNeitherIo,outputSize);
I.Information()=strlen(chDoNeitherIo);
}
break;
default:
I.Information()=0;
I.Complete(STATUS_INVALID_DEVICE_REQUEST);
return STATUS_INVALID_DEVICE_REQUEST;
}
I.Complete(STATUS_SUCCESS);
return STATUS_SUCCESS;
}
NTSTATUS IoDemoDriver::DriverEntry(IN PUNICODE_STRING RegistryPath)
{
NTSTATUS status = STATUS_SUCCESS;
DbgPrint(__FUNCTION__":RegistryPath:%S\n",RegistryPath->Buffer);
IoDemoDevice* pDevice = new (
DEVICE_NAME,
FILE_DEVICE_UNKNOWN,
DEVICE_NAME,
0,
DO_DIRECT_IO
)
IoDemoDevice();
if (pDevice == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
}
else
{
status = pDevice->ConstructorStatus();
if (!NT_SUCCESS(status))
{
delete pDevice;
}
else
DbgPrint(__FUNCTION__":Created IoDemoDevice!\n");
}
return status;
}
VOID IoDemoDriver::Unload()
{
DbgPrint(__FUNCTION__);
KDriver::Unload();
}
最后可以用我的小工具測試一下,,Load驅(qū)動后可以點Content按鈕編輯數(shù)據(jù)包,,也可以查看驅(qū)動返回的數(shù)據(jù),,顯示方式是十六進(jìn)制,注意根據(jù)IO控制代碼的定義選擇不同的Method,DeviceId和ControlCode組合方式.個人感覺這個小工具還是比較好用的,,呵呵!