欢迎光临
我们一直在努力

通过MDL读写进程内存

通过MDL读写进程内存

本文总结于lyshark的《Windows内核安全编程技术实践》。

MDL

Windows采用了分页机制来管理内存。考虑一个虚拟地址,要经过逐级查表,才可以得到物理地址。

逻辑上连续的虚拟页,很大概率会被映射到不连续的物理页中。

假设现在要求对一块内存进行高效的操作(比如IO),频繁地进行查表转换操作效率太低,所以可以把需要用到的页表的物理地址,以链表的形式进行存储,这样就避免了反复查表的开销。

MDL的具体结构Windows并未公开,这里只能参考其他人逆向得到的布局:

typedef struct _MDL {
struct _MDL *Next; // 指向下一个 MDL,形成 MDL 链
CSHORT Size; // 本 MDL 结构的总大小(字节)
CSHORT MdlFlags; // 标志位(见下表)
struct _EPROCESS *Process; // 所属进程(若为用户模式缓冲区)
PVOID MappedSystemVa; // 映射后的系统空间虚拟地址(若已映射)
PVOID StartVa; // 缓冲区起始页的虚拟地址(页对齐)
ULONG ByteCount; // 缓冲区总字节数
ULONG ByteOffset; // 缓冲区在起始页内的字节偏移
} MDL, *PMDL;

引用自Microsoft官方文档链接:

MDL 结构是半不透明的。 驱动程序应仅直接访问此结构的 Next 和 MdlFlags 成员。

通过MDL读取进程内存

主要步骤

1. KeStackAttachProcess() // 附加到目标进程
2. IoAllocateMdl() // 创建 MDL
3. MmProbeAndLockPages() // 探测并锁定页面
4. KeUnstackDetachProcess() // 脱离目标进程
5. MmGetSystemAddressForMdlSafe() // 获取系统空间地址
6. RtlCopyMemory() // 读取数据
7. MmUnlockPages() + IoFreeMdl() // 清理资源

附加到目标进程

首先我们需要切换当前线程的地址空间到目标进程中,因为IoAllocateMdl需要知道目标虚拟地址在哪个进程上下文中,并且我们还要在目标进程的地址空间中验证地址有效性,确保后续的MmProbeAndLockPages能正确访问目标进程的页表。

创建MDL

PMDL pMdl = IoAllocateMdl(
(PVOID)data->pTargetAddr, // 目标虚拟地址
data->dwSize, // 要读取的大小
false, // SecondaryBuffer(通常为false)
false, // ChargeQuota(通常为false)
nullptr // Irp(可选)
);

创建一个 MDL 来描述目标内存区域。

关键:探测并锁定页面

MmProbeAndLockPages(pMdl, UserMode, IoReadAccess);

这是最关键的一步,包含两个操作:

  • 探测(Probe)

    • 验证地址有效性:确保地址在用户模式地址空间(0x00000000-0x7FFFFFFF)
    • 检查访问权限:验证当前是否有读取权限
    • 对齐检查:确保地址和大小符合对齐要求
    • 异常处理:如果地址无效,会抛出异常(代码中缺少 try/except)
  • 锁定(Lock)

    • 确保页面驻留:将相关页面锁定在物理内存中,防止被换出到磁盘
    • 建立物理映射:在 MDL 中记录物理页面信息
    • 增加引用计数:防止页面被释放
  • 参数说明:

    • UserMode:表示地址是用户模式地址

    • IoReadAccess:指定只读访问权限

    脱离目标进程

    MDL 已经捕获了物理页面信息,物理页面被锁定在内存中,不再需要目标进程的地址空间上下文。这样一来,我们就可以减少对目标进程的干扰、允许在脱离状态下长时间访问内存、避免保持附加状态可能导致的死锁问题。

    获取系统空间地址

    PVOID mappedAddress = MmGetSystemAddressForMdlSafe(pMdl, NormalPagePriority);

    目的:将 MDL 描述的物理页面映射到系统空间。

    工作原理:

    • 根据 MDL 中的物理页面信息

    • 创建系统空间的虚拟地址映射

    • 返回一个在内核中可以直接访问的指针

    该函数返回的地址在内核空间中,我们可以直接使用RtlCopyMemory等进行访问。

    示例代码

    #include <ntifs.h>
    #include <windef.h>

    struct Data
    {
    DWORD dwPID;
    PVOID pTargetAddr;

    PBYTE data;
    DWORD dwSize;
    };

    VOID DriverUnload(PDRIVER_OBJECT pDriverObj)
    {
    UNREFERENCED_PARAMETER(pDriverObj);
    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
    "Uninstall Driver Successfully!\\n");
    }

    BOOL MDL_ReadMemory(Data* data)
    {
    PEPROCESS pEp = nullptr;
    NTSTATUS status = PsLookupProcessByProcessId((HANDLE)data->dwPID, &pEp);
    if (!NT_SUCCESS(status) || pEp == nullptr)
    {
    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
    "find process failed!\\n");

    return FALSE;
    }

    // 附加到进程
    KAPC_STATE stack = { 0 };
    KeStackAttachProcess(pEp, &stack);

    // 创建MDL
    PMDL pMdl = IoAllocateMdl((PVOID)data->pTargetAddr, data->dwSize,
    false, false, nullptr);
    if (!pMdl)
    {
    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
    "find process failed!\\n");

    KeUnstackDetachProcess(&stack);
    ObDereferenceObject(pEp);
    return false;
    }

    // 探测并锁定页面
    MmProbeAndLockPages(pMdl, UserMode, IoReadAccess);

    // 取消附加
    KeUnstackDetachProcess(&stack);

    // 获取系统空间映射地址
    PVOID mappedAddress = MmGetSystemAddressForMdlSafe(pMdl, NormalPagePriority);
    if (!mappedAddress)
    {
    MmUnlockPages(pMdl);
    IoFreeMdl(pMdl);
    ObDereferenceObject(pEp);

    return false;
    }

    // 读取数据
    RtlCopyMemory(data->data, mappedAddress, data->dwSize);

    // 清理资源
    MmUnlockPages(pMdl);
    IoFreeMdl(pMdl);
    ObDereferenceObject(pEp);

    return true;
    }

    EXTERN_C NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegPath)
    {
    UNREFERENCED_PARAMETER(pRegPath);

    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
    "Installing Driver…\\n");

    pDriverObj->DriverUnload = DriverUnload;

    Data data;
    data.dwPID = 6776;
    data.pTargetAddr = (PVOID)0x00000056ac965000;
    data.dwSize = 20;

    if (MDL_ReadMemory(&data))
    {
    for (size_t i = 0; i < data.dwSize / sizeof(DWORD); i++)
    {
    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
    "Readed: %x\\n", ((PDWORD)data.data)[i]);
    }
    }
    else
    {
    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
    "Installing Driver…\\n");
    }

    return STATUS_SUCCESS;
    }

    在这里插入图片描述

    赞(0)
    未经允许不得转载:171主机测评 » 通过MDL读写进程内存
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址