首先参考文章:如何使用WinDbg和Virtual Box进行Windows驱动debug,搭建开发环境。
创建一个Empty WDM项目,在solution下添加一个MFC项目: 其中FirstDriver是一个简单的WDM驱动项目,而App是一个MFC程序,用于调用驱动。
Source.c是驱动源码:
#include "ntddk.h"
#define DEVICE_SEND CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_WRITE_DATA)
#define DEVICE_REC CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_READ_DATA)
UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\mydevice123");
UNICODE_STRING SymLinkName = RTL_CONSTANT_STRING(L"\\??\\mydevicelink123");
PDEVICE_OBJECT DeviceObject = NULL;
VOID Unload(IN PDRIVER_OBJECT DriverObject) {
IoDeleteSymbolicLink(&SymLinkName);
IoDeleteDevice(DeviceObject);
DbgPrint("Driver unload\r\n");
}
NTSTATUS DispatchPassThru(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status = STATUS_SUCCESS;
switch (irpsp->MajorFunction) {
case IRP_MJ_CREATE:
DbgPrint("Create request\r\n");
break;
case IRP_MJ_CLOSE:
DbgPrint("Close request\r\n");
break;
default:
status = STATUS_INVALID_PARAMETER;
break;
}
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
NTSTATUS DispathDevCTL(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status = STATUS_SUCCESS;
ULONG returnLength = 0;
PVOID buffer = Irp->AssociatedIrp.SystemBuffer;
ULONG inLength = irpsp->Parameters.DeviceIoControl.InputBufferLength;
ULONG outLength = irpsp->Parameters.DeviceIoControl.OutputBufferLength;
WCHAR *demo = L"sample return from driver";
switch (irpsp->Parameters.DeviceIoControl.IoControlCode) {
case DEVICE_SEND:
DbgPrint("Send data is %ws \r\n", buffer);
returnLength = (wcsnlen(buffer, 511) + 1) * 2;
break;
case DEVICE_REC:
wcsncpy(buffer, demo, 511);
returnLength = (wcsnlen(buffer, 511) + 1) * 2;
DbgPrint("receive data is %ws \r\n", buffer);
break;
default:
status = STATUS_INVALID_PARAMETER;
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = returnLength;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload = Unload;
UNICODE_STRING string = RTL_CONSTANT_STRING(L"hello driver\r\n");
DbgPrint("%wZ", &string);
NTSTATUS status = IoCreateDevice(DriverObject, 0, &DeviceName,
FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN,
FALSE, &DeviceObject);
if (!NT_SUCCESS(status)) {
DbgPrint("Create device failed\r\n");
return status;
}
status = IoCreateSymbolicLink(&SymLinkName, &DeviceName);
if (!NT_SUCCESS(status)) {
DbgPrint("create symbolic link failed\r\n");
IoDeleteDevice(DeviceObject);
return status;
}
for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; ++i) {
DriverObject->MajorFunction[i] = DispatchPassThru;
}
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispathDevCTL;
DbgPrint("Driver load\r\n");
return STATUS_SUCCESS;
}
程序完成了几项工作:
- 定义了Device Control Code,读写code分别为0x801和0x802,微软为用户开放了0x800~0xfff的control code;
- 定义了DeviceName和SymbolLinkName,device/symbolink name一旦创建成功,可以通过WinObj查看,这个工具可以查看系统中的Device和用于用户调用(File IO) device别名symbolink name:
- DriverEntry入口函数创建了device和symbolink,并配置了分派函数用于响应用户的各种请求。
- DispatchPassThru函数响应IRP,处理了Device open和close请求。
- DispatchDevCTL函数响应用户的IO请求。具体是指读写请求,这里会用到之前定义的Device Control Code。
- Unload函数删除symbolink和device对象。
MFC应用程序 界面: 一共4个按钮,分别执行device open/close,以及通过IO发送和接收数据。
#include "winioctl.h"
#define DEVICE_SEND CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_WRITE_DATA)
#define DEVICE_REC CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_READ_DATA)
HANDLE devicehandle = NULL;
void CAppDlg::OnBnClickedButton1()
{
devicehandle = CreateFile(L"\\\\.\\mydevicelink123",
GENERIC_ALL, 0, 0,
OPEN_EXISTING,
FILE_ATTRIBUTE_SYSTEM,
0);
if (devicehandle == INVALID_HANDLE_VALUE) {
MessageBox(L"Not valid value", 0, 0);
return;
}
MessageBox(L"valid value", 0, 0);
}
void CAppDlg::OnBnClickedButton2()
{
if (devicehandle != INVALID_HANDLE_VALUE) {
CloseHandle(devicehandle);
}
}
void CAppDlg::OnBnClickedButton3()
{
WCHAR *message = L"send sample from mfc";
WCHAR returnMessage[1024] = { 0 };
ULONG returnLength = 0;
char wr[4] = { 0 };
if (devicehandle != NULL && devicehandle != INVALID_HANDLE_VALUE) {
if (!DeviceIoControl(devicehandle, DEVICE_SEND, message,
(wcslen(message) + 1) * 2,
returnMessage,
1024,
&returnLength,
0)) {
MessageBox(L"Device io control failed", 0, 0);
}
else {
MessageBox(returnMessage, 0, 0);
_itoa_s(returnLength, wr, 10);
MessageBoxA(0, wr, 0, 0);
}
}
}
void CAppDlg::OnBnClickedButton4()
{
WCHAR message[1024] = { 0 };
ULONG returnLength = 0;
if (devicehandle != NULL && devicehandle != INVALID_HANDLE_VALUE) {
if (!DeviceIoControl(devicehandle, DEVICE_REC, NULL,
0,
message,
1024,
&returnLength,
0)) {
MessageBox(L"Device io control failed", 0, 0);
}
else {
MessageBox(message, 0, 0);
}
}
}
需要注意的是,这里要使用DeviceIoControl函数用于和device进行IO控制,所以include头文件winioctl.h,并且Device control code要和WDM驱动里的code一致。 将编译好的driver.sys和App放入虚拟机,以管理员权限打开OSRLOADER,Register Service和Start Service,以管理员权限打开App,点击4个按钮,依次显示如下:
|