聊完了CreateFile的调用过程,我们再来看一看三个非常重要的API函数DeviceIoControl、ReadFile、WriteFile:
BOOL WINAPI DeviceIoControl(
_In_ HANDLE hDevice,
_In_ DWORD dwIoControlCode,
_In_opt_ LPVOID lpInBuffer,
_In_ DWORD nInBufferSize,
_Out_opt_ LPVOID lpOutBuffer,
_In_ DWORD nOutBufferSize,
_Out_opt_ LPDWORD lpBytesReturned,
_Inout_opt_ LPOVERLAPPED lpOverlapped);
hDevice—通过CreateFile打开的设备句柄
dwIoControlCode—应用程序调用驱动程序的控制命令
lpInBuffer—应用程序传递到驱动程序数据的缓冲区地址
nInBufferSize—缓冲区长度
nOutBuffer—驱动程序返回数据到应用程序的缓冲区地址
nOutBufferSize—缓冲区长度
lpBytesReturned—驱动程序实际返回的数据长度
lpOverlapped—重叠结构,异步调用函数时使用
DeviceIoControl的调用过程比较简单,API通过系统调用进入内核,内核中两次调用ObReferenceObjectByHandle,一次为了获取句柄对应的设备对象(实际上是文件对象),另一次是为了获取Event句柄对应的事件对象,该对象用于处理用户模式下的同步问题;顺着设备对象的堆叠往上爬,获得了顶层设备对象,之后就准备了一个IRP,通过IoCallDriver调用了驱动程序中的OnMajorDeviceIoControl函数,这些函数前一章已经分析过,理解起来应该不成问题。
BOOL WriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
);
hFile—通过CreateFile打开的设备句柄
lpBuffer—应用程序传递到驱动程序数据的缓冲区地址
nNumberOfBytesToWrite—缓冲区长度
lpNumberOfBytesWriten—实际写操作的长度
lpOverlapped—重叠结构,异步调用函数时使用
BOOL ReadFile(
HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped
);
hFile—通过CreateFile打开的设备句柄
lpBuffer—驱动程序返回数据到应用程序的缓冲区地址
nNumberOfBytesToRead—缓冲区长度
lpNumberOfBytesRead—驱动程序实际返回的数据长度
lpOverlapped—重叠结构,异步调用函数时使用
从上面的函数调用图可以看出:
1, WriteFile、ReadFile、DeviceIoControl三个函数异曲同工,走的路线很相近
2, 在windows内核层,WriteFile生成一个MajorFunction为IRP_MJ_WRITE的IRP
ReadFile生成一个MajorFunction为IRP_MJ_READ的IRP
DeviceIoControl生成一个MajorFunction为IRP_MJ_DEVICE_CONTROL的IRP
3, 在windows内核层,WriteFile函数生成的IO_STACK_LOCATION消息参数联合体需要转换成Write结构体
ReadFile函数生成的IO_STACK_LOCATION消息参数联合体需要转换成Read结构体
DeviceIoControl函数生成的IO_STACK_LOCATION消息参数联合体需要转换成DeviceIoControl结构体
4, 只要驱动支持,WriteFile,ReadFile完全可以由DeviceIoControl替代
参考资料:
1, <<Windows内核情景分析>>