You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用MmCopyMemory从内核写入用户态地址空间失败问题排查

KMDF驱动调用MmCopyMemory触发IRQL_NOT_LESS_OR_EQUAL BSOD的排查与修复

问题根源分析

你的代码存在几个致命问题,直接引发了IRQL_NOT_LESS_OR_EQUAL崩溃:

  • 未校验API返回值:PsLookupProcessByProcessId可能执行失败(比如目标进程已退出),此时process为NULL,后续KeStackAttachProcess会直接触发崩溃。
  • 跨地址空间访问栈内存:Temp是当前线程栈上的数组,调用KeStackAttachProcess切换到目标进程地址空间后,该栈地址在目标进程中完全无效。MmCopyMemory尝试访问这个无效地址时会触发页错误,而如果回调例程(如MyLoadImageNotifyRoutine)运行在DISPATCH_LEVEL及以上IRQL,页错误无法被异常处理捕获,直接导致BSOD。
  • 异常处理无效:__try/__except在DISPATCH_LEVEL及以上IRQL环境下无法正常工作,这类IRQL下的异常会直接引发系统崩溃。

修复方案及代码修正

针对上述问题,给出以下修复步骤及修正后的代码:

  1. 严格校验API返回值:必须检查PsLookupProcessByProcessId的返回状态,失败则直接返回,避免后续非法操作。
  2. 使用非分页内存存储源数据:非分页内存属于系统全局地址空间,在任何进程地址空间中都可访问,彻底避免跨地址空间的无效内存访问问题。
  3. 保留合理的异常处理:LoadImageNotifyRoutine运行在PASSIVE_LEVEL,此时异常处理可正常工作,用于捕获内存访问类错误。

修正后的代码:

VOID WriteToMemoryMappedSection(HANDLE ProcessId, PVOID TargetMemoryAddress)
{
    PEPROCESS process = NULL;
    KAPC_STATE apcState;
    MM_COPY_ADDRESS virtualSourceAddress = { 0 };
    size_t NumberOfBytesTransferred = 0;
    NTSTATUS status = STATUS_SUCCESS;
    // 分配非分页内存存储补丁数据
    PCHAR Temp = (PCHAR)ExAllocatePool2(POOL_FLAG_NON_PAGED, 2, 'Patc');
    if (!Temp)
    {
        DbgPrint("Failed to allocate non-paged pool\n");
        return;
    }
    Temp[0] = 0x90;
    Temp[1] = 0x90;

    // 校验进程查找结果
    status = PsLookupProcessByProcessId(ProcessId, &process);
    if (!NT_SUCCESS(status))
    {
        DbgPrint("PsLookupProcessByProcessId failed: 0x%08X\n", status);
        ExFreePool(Temp);
        return;
    }

    KeStackAttachProcess(process, &apcState);

    virtualSourceAddress.VirtualAddress = Temp;

    __try
    {
        status = MmCopyMemory(
            (PVOID)((ULONG64)TargetMemoryAddress + 0x1000),
            virtualSourceAddress,
            2,
            MM_COPY_MEMORY_VIRTUAL,
            &NumberOfBytesTransferred
        );
        if (!NT_SUCCESS(status))
        {
            DbgPrint("MmCopyMemory failed: 0x%08X\n", status);
        }
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        DbgPrint("Exception occurred: 0x%08X\n", GetExceptionCode());
    }

    KeUnstackDetachProcess(&apcState);
    ObDereferenceObject(process);
    ExFreePool(Temp);
}

// 回调例程声明
VOID MyLoadImageNotifyRoutine(
    _In_opt_ PUNICODE_STRING FullImageName,
    _In_ HANDLE ProcessId,
    _In_ PIMAGE_INFO ImageInfo
);

额外注意事项

  • 目标地址有效性:需确保TargetMemoryAddress + 0x1000是目标进程中已分配且具备可写权限的内存,否则MmCopyMemory仍会失败或触发异常。
  • 池内存管理:使用ExAllocatePool2时需指定唯一的池标签(如示例中的'Patc'),便于调试时追踪内存泄漏问题。
  • IRQL确认:可通过KeGetCurrentIrql()检查当前执行的IRQL,确保所有操作符合对应IRQL的限制(比如PASSIVE_LEVEL下可使用分页内存,但跨地址空间访问仍需谨慎)。

内容的提问来源于stack exchange,提问作者blogger13

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 13:42:48