应用层,驱动层,硬件层_windows组件向导在哪里

应用层,驱动层,硬件层_windows组件向导在哪里驱动层与应用层通信是通过DeviceIoControl,首先驱动层要实现:pDriverObject->DriverUnload=MyDriverUnload;pDriverObject->MajorFunction[IRP_MJ_CREATE]=MyCreate;pDriverObject->MajorFunction[IRP_MJ_CLOSE]=MyClose;

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

驱动层与应用层通信是通过DeviceIoControl,

符号定义

#define DEVICE_NAME  L"\\Device\\myDriver"             // Driver Name
#define SYMBOLIC_LINK_NAME  L"\\DosDevices\\myDriver"            // Symbolic Link Name
#define WIN32_LINK_NAME  "\\\\.\\myDriver"                    // Win32 Link Name

//
// Device IO Control Codes
//
#define IOCTL_BASE          0x800
#define MY_CTL_CODE(i)        \
	CTL_CODE                  \
	(                         \
	FILE_DEVICE_UNKNOWN,  \
	IOCTL_BASE + i,       \
	METHOD_BUFFERED,      \
	FILE_ANY_ACCESS       \
	)

// ===========内存数据结构, 不会用来做多端的数据传递======================
// ===========用户自己可以存些需要在内存中传递的数据======================
typedef struct _DEV_EXTENSION {
	INT32              iTest;
	WCHAR              wLastPath[512];
}DEV_EXTENSION, * PDEV_EXTENSION;

driverentry

NTSTATUS DriverEntry(
	IN PDRIVER_OBJECT DriverObject,
	IN PUNICODE_STRING RegistryPath
)
{
	NTSTATUS					status = STATUS_SUCCESS;
	UNICODE_STRING				uszDriverString;
	UNICODE_STRING				uszDeviceString;
	PDEVICE_OBJECT				pDeviceObject = NULL;
	PDEV_EXTENSION              pDevExt = NULL;
	HANDLE                      hThread = NULL;

	DbgPrint("MyDriver DriverEntry\n");

	// Point uszDriverString at the driver name
	RtlInitUnicodeString(&uszDriverString, DEVICE_NAME);

	// Create and initialize device object
	status = IoCreateDevice(
		DriverObject,
		sizeof(DEV_EXTENSION),
		&uszDriverString,
		FILE_DEVICE_UNKNOWN,
		0,
		FALSE,
		&pDeviceObject
	);
	if (!NT_SUCCESS(status))
	{
		DbgPrint("RegistryMonitor: ERROR IoCreateDevice - %08x\n", status);
		return status;
	}

	// 初始化设备扩展
	gpDeviceObject = pDeviceObject;
	pDevExt = (DEV_EXTENSION*)pDeviceObject->DeviceExtension;
	pDevExt->iTest = 0;
	wcscpy_s(pDevExt->wLastPath, sizeof(pDevExt->wLastPath)/sizeof(pDevExt->wLastPath[0]), L"");

	/* Point uszDeviceString at the device name */
	RtlInitUnicodeString(&uszDeviceString, SYMBOLIC_LINK_NAME);

	/* Create symbolic link to the user-visible name */
	status = IoCreateSymbolicLink(&uszDeviceString, &uszDriverString);
	if (!NT_SUCCESS(status))
	{
		DbgPrintEx("RegistryMonitor: ERROR IoCreateSymbolicLink - %08x\n", status);
		IoDeleteDevice(pDeviceObject);
		return status;
	}

	// Load structure to point to IRP handlers
	DriverObject->DriverUnload = UnloadDriver;
	DriverObject->MajorFunction[IRP_MJ_CREATE] = KDispatchCreateClose;
	DriverObject->MajorFunction[IRP_MJ_CLOSE] = KDispatchCreateClose;
	DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KDispatchIoctl;

	return STATUS_SUCCESS;
}

应用层要访问的符号链接是\\.\myDriver

首先驱动层要实现:

pDriverObject->DriverUnload = UnloadDriver;
pDriverObject->MajorFunction[IRP_MJ_CREATE] = KDispatchCreateClose;
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = KDispatchCreateClose;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KDispatchIoctl;

Create和Close函数必须要实现,否则CreateFile的时候可能会报错

NTSTATUS KDispatchCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
	DbgPrint("create or close happen\n");
	Irp->IoStatus.Status = STATUS_SUCCESS;
	Irp->IoStatus.Information = 0;
	IoCompleteRequest(Irp, IO_NO_INCREMENT);
	return STATUS_SUCCESS;
}

卸载例程


void UnloadDriver(IN PDRIVER_OBJECT DriverObject)
{
	UNICODE_STRING  uszDeviceString;
	NTSTATUS        ntStatus;
	RtlInitUnicodeString(&uszDeviceString, SYMBOLIC_LINK_NAME);
	IoDeleteSymbolicLink(&uszDeviceString);
	if (DriverObject->DeviceObject != NULL)
	{
		IoDeleteDevice(DriverObject->DeviceObject);
	}

	DbgPrint("Unload Success \n");
}

其中MyDispatchDeviceControl用来与应用层通过DeviceIoControl通信

NTSTATUS KDispatchIoctl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
	UINT				dwDataWritten = 0;
	NTSTATUS			status = STATUS_INVALID_DEVICE_REQUEST;	 // STATUS_UNSUCCESSFUL
	PIO_STACK_LOCATION	pIrpStack = IoGetCurrentIrpStackLocation(Irp);
	ULONG				uIoControlCode = 0;
	PVOID				pIoBuffer = NULL;
	ULONG				uInSize = 0;
	ULONG				uOutSize = 0;
	int* pReturn = NULL;
	PDEV_EXTENSION   	deviceExtension = (DEV_EXTENSION*)DeviceObject->DeviceExtension;

	NTSTATUS			ntStatus;
	ANSI_STRING			imagePath;						//进程路径
	CHAR				szImageName[300];					//进程镜像名
	HANDLE				pid = PsGetCurrentProcessId();
	ULONG				information = 0;

	// Get the IoCtrl Code
	uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;

	pIoBuffer = Irp->AssociatedIrp.SystemBuffer;
	uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
	uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;


	if (DeviceObject == gpDeviceObject)
	{
		switch (uIoControlCode)
		{
		case MY_CTL_CODE(1):
		{
			DbgPrint("buffer:%p,%d,%d\n", pIoBuffer, uInSize, uOutSize);
			wcscpy_s(gTargetUserSid, sizeof(gTargetUserSid) / sizeof(gTargetUserSid[0]), (WCHAR*)pIoBuffer);
			DbgPrint("get str:%ws, size:%d\n", gTargetUserSid, uInSize);
			wcscpy_s((WCHAR*)pIoBuffer, uOutSize, L"ok");
			information = 6;
			status = STATUS_SUCCESS;
		}
		break;
		default:
		{
			// Invalid code sent
			DbgPrint("[HookDeviceIocontrol] Unknown IOCTL: 0x%X (%04X,%04X)\r\n",
				uIoControlCode,
				DEVICE_TYPE_FROM_CTL_CODE(uIoControlCode),
				IoGetFunctionCodeFromCtlCode(uIoControlCode));
			status = STATUS_INVALID_PARAMETER;
		}
		}


		Irp->IoStatus.Information = information;

		// ***************注意一定要在自己要处理的ctlcode中修改status和information*******
        // ***************不然deviceIoControl会报错************************************
		Irp->IoStatus.Status = status;

		IoCompleteRequest(Irp, IO_NO_INCREMENT);
		return status;

	}
	else
	{
		Irp->IoStatus.Information = 0;

		// Complete the I/O Request
		Irp->IoStatus.Status = STATUS_SUCCESS;

		IoCompleteRequest(Irp, IO_NO_INCREMENT);
		return status;
	}
}

然后应用层要打开驱动层的设备链接符号,来得到句柄,DeviceIoControl的通信依赖句柄

void TestDriver()
{
	BOOL bRet = TRUE;
	DWORD dwReturnSize = 0;
	WCHAR wzIn[MAX_PATH], wzOut[MAX_PATH];
	WCHAR symbolPath[MAX_PATH];
	HANDLE hSymbol = NULL;

    // 访问WIN32_LINK_NAME
	wcscpy_s(symbolPath, sizeof(symbolPath) / sizeof(symbolPath[0]), L"\\\\.\\myDriver");

	wcscpy_s(wzIn, sizeof(wzIn)/sizeof(wzIn[0]), L"hello world_000");

	hSymbol = CreateFileW(
		symbolPath, 
		GENERIC_WRITE | GENERIC_READ,
		FILE_SHARE_READ | FILE_SHARE_WRITE,
		NULL,
		OPEN_EXISTING,
		0,
		NULL);
	if (INVALID_HANDLE_VALUE == hSymbol) {
		WriteLogEx(L"crete file:%s failed, error:%d\n", symbolPath, GetLastError());
		return;
	}

	DWORD dwInBufferSize = (wcslen(wzIn) + 1) * sizeof(WCHAR);
	bRet = DeviceIoControl(hSymbol, MY_CTL_CODE(1), wzIn, dwInBufferSize,
		wzOut, sizeof(wzOut), &dwReturnSize, NULL);

	if (!bRet) {
		WriteLogEx(L"DeviceIoControl:%s,size:%d failed, error:%d\n", symbolPath, dwInBufferSize, GetLastError());
        CloseHandle(hSymbol);
		return;
	}

    CloseHandle(hSymbol);
	WriteLogEx(L"done\n");
}

遇到的坑:

1.CreatFile失败

因为没有实现
DriverObject->MajorFunction[IRP_MJ_CREATE] = KDispatchCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = KDispatchCreateClose;

2.deviceIocontrol返回失败,可是驱动层明明收到数据了

因为没有把返回值status调整为STATUS_SUCCESS,只是等于了一个初始化不会STATUS_SUCCESS的值

3.DeviceIoControl传到驱动层了,但是没有读到数据

一开始是读到了的,后来因为排查问题的时候以为是CtlCode的问题,于是随意调整了一下MY_CTL_CODE的第三个参数METHOD_BUFFERED,凑巧发现这个值影响到驱动层读取DeviceIoControl传递的数据,搜索一番发现微软官方是有定义的

Defining I/O Control Codes – Windows drivers | Microsoft Docsh

Method与内存传输的关系

1..METHOD_BUFFERED:缓冲区模式

inbuffer的内容被复制到IRP中的pIrp->AssociatedIrp.SystemBuffer复制的长度是DeviceIoControl中指定的nInBufferSize。

驱动返回数据时,也是向pIrp->AssociatedIrp.SystemBuffer中写入,操作系统会将数据复制到DeviceIoControl的outBuffer,复制的字节数是pIrp->IoStatus.Information, 这个数值由驱动指定。

派遣函数读取关键信息的代码如下:

	uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;

	pIoBuffer = Irp->AssociatedIrp.SystemBuffer;
	uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
	uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;

设置返回数据的代码如下


		wcscpy_s((WCHAR*)pIoBuffer, uOutSize, L"ok");
		Irp->IoStatus.Information = 6;

		// Complete the I/O Request
		Irp->IoStatus.Status = STATUS_SUCCESS;

		IoCompleteRequest(Irp, IO_NO_INCREMENT);
		return status;

2 METHOD_IN_DIRECT与METHOD_OUT_DIRECT  直接内存模式
与缓冲模式一样,DeviceIoControl中的inBuffer数据被复制到IRP中的pIrp->AssociatedIrp.SystemBuffer,复制的长度是DeviceIoControl指定的nInBufferSize。

直接内存模式中,操作系统会将DeviceIoControl指定的输出缓冲区锁定,然后在内核模式地址下重新映射一段地址。

派遣函数中IRP中的pIrp->MdlAddress记录DeviceIoControl指定的输出缓冲区。派遣函数应该使用MmGetSystemAddressForMdlSafe将这段内存映射到内核模式下的内存地址。

得到输入输出缓冲区的大小以及IOCTL的方式与缓冲区模式相同。

另外需要注意CTL_CODE设置的权限问题,若以只读方式打开设备,METHOD_IN_DIRECT的IOCTL操作会失败。

派遣函数中处理直接内存模式:

//显示输入缓冲区数据
 

UCHAR* InputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
for (ULONG i=0;i<cbin;i++)
{
    DbgPrint("%X\n",InputBuffer[i]);
}
//pIrp->MdlAddress为DeviceIoControl输出缓冲区地址相同
DbgPrint("User Address:0X%08X\n",MmGetMdlVirtualAddress(pIrp->MdlAddress));

返回数据

UCHAR* OutputBuffer = (UCHAR*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority);
//InputBuffer被映射到内核模式下的内存地址,必定在0X80000000-0XFFFFFFFF之间memset(OutputBuffer,0xAA,cbout);

3:METHOD_NEITHER :Neither模式
因为此模式直接访问用户模式地址,这是很危险的,所以此模式很少被用到。

使用用户模式地址必须保证调用DeviceIoControl 的线程与派遣函数运行在同一个线程上下文中。

派遣函数得到输入缓冲区的方式与前两种不同,此模式是通过IO堆栈的stack->Parameters.DeviceIoControl.Type3InputBuffer;得到输入缓冲区。

驱动通过pIrp->UserBuffer得到输出缓冲区。

得到输入输出缓冲区的长度与IOCTL的方式与前两种相同。

由于驱动程序的派遣函数不能保证传递进来的用户地址是合法地址,所以要对传入的用户模式地址进行可读写判断。这就需要ProbeForRead函数和ProbeForWrite函数与_try _execpt 结合使用。

下面是驱动派遣函数中Neither模式

//显示输入缓冲区数据

UCHAR* UserInputBuffer = (UCHAR*)stack->Parameters.DeviceIoControl.Type3InputBuffer;
KdPrint(("UserInputBuffer:0X%0X\n",UserInputBuffer));
 
//得到用户模式地址
PVOID UserOutputBuffer = pIrp->UserBuffer;
KdPrint(("UserOutputBuffer:0X%0X\n",UserOutputBuffer));
 
__try
{
    KdPrint(("Enter __try block\n"));
 
    //判断指针是否可读
    ProbeForRead(UserInputBuffer,cbin,4);
    //显示输入缓冲区内容
    for (ULONG i=0;i<cbin;i++)
    {
        KdPrint(("%X\n",UserInputBuffer[i]));
    }
 
    //判断指针是否可写
    ProbeForWrite(UserOutputBuffer,cbout,4);
 
    //操作输出缓冲区
    memset(UserOutputBuffer,0xAA,cbout);
 
    //如果在上面引发异常,所以以后语句不会被执行!
    pIrp->IoStatus.Information = cbout;
 
    KdPrint(("Leave __try block\n"));
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
    KdPrint(("Catch the exception\n"));
    KdPrint(("The program will keep going\n"));
    status = STATUS_UNSUCCESSFUL;
}
 
pIrp->IoStatus.Information = cbout;

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/196464.html原文链接:https://javaforall.net

(0)
上一篇 2025年9月26日 下午2:15
下一篇 2025年9月26日 下午2:43


相关推荐

  • Linux系统查看当前Cuda版本

    Linux系统查看当前Cuda版本1 方法一 nvcc V V 要大写 输出的 release10 0 时实际的 cuda 版本 2 方法二 nvidia smi 其中 DriverVersio 是 NVIDIA 驱动版本 CUDAVersion 是指和驱动对应的 CUDA 版本 所以可以根据对应的 cuda 版本可以安装新的 cuda 另外 cuda 是向下兼容的 所以可以安装更高一点的 cuda

    2026年3月26日
    1
  • 猿创征文|点亮JAVA技术之灯(线程篇)「建议收藏」

    猿创征文|点亮JAVA技术之灯(线程篇)「建议收藏」线程安全就是说多线程访问同一段代码,不会产生不确定的结果。又是一个理论的问题,各式各样的答案有很多,我给出一个个人认为解释地最好的:如果你的代码在多线程下执行和在单线程下执行永远都能获得一样的结果,那么你的代码就是线程安全的。(1)不可变像String、Integer、Long这些,都是final类型的类,任何一个线程都改变不了它们的值,要改变除非新创建一个,因此这些不可变对象不需要任何同步手段就可以直接在多线程环境下使用(2)绝对线程安全不管运行时环境如何,调用者都不需要额外的同步措施。……….

    2025年8月23日
    10
  • HTTP 500 Internal Server Error[通俗易懂]

    HTTP 500 Internal Server Error[通俗易懂]HTTP500InternalServerError

    2022年7月14日
    19
  • 后端神器Dataway入门

    后端神器Dataway入门文章目录 Dataway 框架概述 Hasor 简介 Dataway 接口配置服务 Dataway 介绍 Dataway 的主打场景 Dataway 的技术架构 DataQL 聚合查询引擎 DataQL 介绍 SpringBoot 整合 Dataway 引入依赖启用 Hasor 启用 Dataway 初始化必要的表 MySQL 初始化数据源启动工程配置接口 Dataway 框架概述 Hasor 简介 Hasor 是由多个不同系列框架组合而成的一个框架体系 这些子框架的能力涵盖了 IoC Aop WebMVC 数据库以及其他的方方面面 这一切的基础要归功于 Haso

    2026年3月17日
    1
  • laravel 开发api(里的繁体字)

    本例子使用Laravel5.2版本这里记录的是较为灵活的方案,不考虑使用swaggervel,具体使用参考一下步骤:安装依赖swagger-phpcomposer require zircote/swagger-php创建SwaggerControllerphp artisan make:controller SwaggerController在Swa

    2022年4月15日
    48
  • Siege压测使用和说明

    Siege压测使用和说明原文地址 http blog csdn net shero zsmj article details siege 是一个压力测试和评测工具 可以根据配置对一个 WEB 站点进行多用户的并发访问 记录每个用户所有请求过程的相应时间 并在预定义并发量下重复进行 siege 可以从预定义列表中获取随机的 URL 所以 siege 可用于仿真用户请求负载 一 siege 工具安装

    2026年3月19日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号