内核模式驱动调用ACPI控制方法失败(STATUS_INVALID_PARAMETER)
问题分析与解决方案
错误原因
返回STATUS_INVALID_PARAMETER(-1073741811)的核心问题有两个:
- 目标设备对象错误:你直接获取的是ACPI设备的物理设备对象(PDO),但
IOCTL_ACPI_EVAL_METHOD需要发送给ACPI驱动为该设备创建的功能设备对象(FDO)——PDO仅负责设备枚举,不处理ACPI方法调用请求。 - PDO名称不可靠:
\Device\00000071这类PDO名称是系统动态分配的,重启或设备枚举变更后会失效,硬编码会导致后续设备获取失败。
解决步骤
1. 正确获取ACPI设备的FDO
通过设备的硬件ID(TEST2412)枚举ACPI接口,匹配目标设备并获取FDO,避免依赖动态变化的PDO名称:
NTSTATUS GetAcpiFdo(PDEVICE_OBJECT* pAcpiFdo, PFILE_OBJECT* pFileObject) { GUID acpiGuid = GUID_DEVINTERFACE_ACPI; UNICODE_STRING targetHid; RtlInitUnicodeString(&targetHid, L"TEST2412"); // 获取所有ACPI设备接口列表 PZZWSTR interfaceList = NULL; ULONG listSize = 0; NTSTATUS status = IoGetDeviceInterfaces(&acpiGuid, NULL, 0, &interfaceList, &listSize); if (!NT_SUCCESS(status)) { return status; } // 遍历接口匹配目标HID PUNICODE_STRING currentInterface = (PUNICODE_STRING)interfaceList; while (currentInterface->Length > 0) { // 尝试打开当前接口 status = IoOpenDeviceInterface(currentInterface, FILE_READ_DATA | FILE_WRITE_DATA, NULL, pFileObject, pAcpiFdo); if (NT_SUCCESS(status)) { // 获取设备HID属性验证是否为目标设备 WCHAR hidBuffer[256]; UNICODE_STRING deviceHid; RtlInitUnicodeString(&deviceHid, hidBuffer); status = IoGetDeviceProperty(*pAcpiFdo, DevicePropertyHardwareId, sizeof(hidBuffer), hidBuffer, &listSize); if (NT_SUCCESS(status) && RtlCompareUnicodeString(&deviceHid, &targetHid, TRUE) == 0) { // 找到目标设备,释放列表后返回 ExFreePool(interfaceList); return STATUS_SUCCESS; } // 非目标设备,释放已打开的对象 ObDereferenceObject(*pFileObject); ObDereferenceObject(*pAcpiFdo); } // 移动到下一个接口 currentInterface = (PUNICODE_STRING)((PUCHAR)currentInterface + currentInterface->MaximumLength); } ExFreePool(interfaceList); return STATUS_NOT_FOUND; }
2. 修正ACPI方法调用逻辑
虽然你对MethodNameAsUlong的字节序处理正确(x86小端系统下'ERIF'对应ACPI方法名FIRE),但优化缓冲区初始化和IRP构建细节:
NTSTATUS CallAcpiMethod(PDEVICE_OBJECT pAcpiFdo) { KdPrint(("CallAcpiMethod enter\n")); ACPI_EVAL_INPUT_BUFFER inputBuffer = {0}; ACPI_EVAL_OUTPUT_BUFFER outputBuffer = {0}; IO_STATUS_BLOCK ioStatus; KEVENT event; PIRP irp; // 初始化输入缓冲区:签名+方法名 inputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE; inputBuffer.MethodNameAsUlong = 'ERIF'; // 对应ACPI方法名"FIRE" // 初始化IRP完成事件 KeInitializeEvent(&event, NotificationEvent, FALSE); // 构建ACPI方法调用IRP irp = IoBuildDeviceIoControlRequest( IOCTL_ACPI_EVAL_METHOD, pAcpiFdo, &inputBuffer, sizeof(inputBuffer), &outputBuffer, sizeof(outputBuffer), FALSE, &event, &ioStatus); if (!irp) { KdPrint(("failed to create IRP\n")); return STATUS_INSUFFICIENT_RESOURCES; } // 发送IRP并等待完成 NTSTATUS status = IoCallDriver(pAcpiFdo, irp); if (status == STATUS_PENDING) { KdPrint(("IoCallDriver pending status=%ld\n", status)); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } KdPrint(("CallAcpiMethod exit status=%ld\n", status)); return status; }
3. 权限验证
确保你的内核驱动已正确签名,且拥有访问ACPI设备的权限——未签名驱动在开启安全启动的系统中会被拦截,间接导致参数错误。
内容的提问来源于stack exchange,提问作者Justin Timbersaw
相关产品推荐
相关产品推荐

