驱动开发:内核读写内存多级偏移
创始人
2025-07-01 08:01:38
0

让我们继续在《内核读写内存浮点数》的基础之上做一个简单的延申,如何实现多级偏移读写,其实很简单,读写函数无需改变,只是在读写之前提前做好计算工作,以此来得到一个内存偏移值,并通过调用内存写入原函数实现写出数据的目的。

以读取偏移内存为例,如下代码同样来源于本人的LyMemory读写驱动项目,其中核心函数为WIN10_ReadDeviationIntMemory()该函数的主要作用是通过用户传入的基地址与偏移值,动态计算出当前的动态地址。

函数首先将基地址指向要读取的变量,并将其转换为LPCVOID类型的指针。然后将指向变量值的缓冲区转换为LPVOID类型的指针。接下来,函数使用PsLookupProcessByProcessId函数查找目标进程并返回其PEPROCESS结构体。随后,函数从偏移地址数组的最后一个元素开始迭代,每次循环都从目标进程中读取4字节整数型数据,并将其存储在Value变量中。然后,函数将基地址指向Value和偏移地址的和,以便在下一次循环中读取更深层次的变量。最后,函数将基地址指向最终变量的地址,读取变量的值,并返回。

如下案例所示,用户传入进程基址以及offset偏移值时,只需要动态计算出该偏移地址,并与基址相加即可得到动态地址。

#include 
#include 
#include 

// 普通Ke内存读取
NTSTATUS KeReadProcessMemory(PEPROCESS Process, PVOID SourceAddress, PVOID TargetAddress, SIZE_T Size)
{
    PEPROCESS SourceProcess = Process;
    PEPROCESS TargetProcess = PsGetCurrentProcess();
    SIZE_T Result;
    if (NT_SUCCESS(MmCopyVirtualMemory(SourceProcess, SourceAddress, TargetProcess, TargetAddress, Size, KernelMode, &Result)))
        return STATUS_SUCCESS;
    else
        return STATUS_ACCESS_DENIED;
}

// 读取整数内存多级偏移
/*
  Pid: 目标进程的进程ID。
  Base: 变量的基地址。
  offset: 相对基地址的多级偏移地址,用于定位变量。
  len: 偏移地址的数量。
*/
INT64 WIN10_ReadDeviationIntMemory(HANDLE Pid, LONG Base, DWORD offset[32], DWORD len)
{
    INT64 Value = 0;
    LPCVOID pbase = (LPCVOID)Base;
    LPVOID rbuffer = (LPVOID)&Value;

    PEPROCESS Process;
    PsLookupProcessByProcessId((HANDLE)Pid, &Process);

    for (int x = len - 1; x >= 0; x--)
    {
        __try
        {
            KeReadProcessMemory(Process, pbase, rbuffer, 4);
            pbase = (LPCVOID)(Value + offset[x]);
        }
        __except (EXCEPTION_EXECUTE_HANDLER)
        {
            return 0;
        }
    }

    __try
    {
        DbgPrint("读取基址:%x \n", pbase);
        KeReadProcessMemory(Process, pbase, rbuffer, 4);
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        return 0;
    }

    return Value;
}

// 驱动卸载例程
VOID UnDriver(PDRIVER_OBJECT driver)
{
    DbgPrint("Uninstall Driver \n");
}

// 驱动入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("Hello LyShark \n");

    DWORD PID = 4884;
    LONG PBase = 0x6566e0;
    LONG Size = 4;
    DWORD Offset[32] = { 0 };

    Offset[0] = 0x18;
    Offset[1] = 0x0;
    Offset[2] = 0x14;
    Offset[3] = 0x0c;

    // 读取内存数据
    INT64 read = WIN10_ReadDeviationIntMemory(PID, PBase, Offset, Size);

    DbgPrint("PID: %d 基址: %p 偏移长度: %d \n", PID, PBase, Size);
    DbgPrint("[+] 1级偏移: %x \n", Offset[0]);
    DbgPrint("[+] 2级偏移: %x \n", Offset[1]);
    DbgPrint("[+] 3级偏移: %x \n", Offset[2]);
    DbgPrint("[+] 4级偏移: %x \n", Offset[3]);

    DbgPrint("[ReadMemory] 读取偏移数据: %d \n", read);

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

编译并运行如上这段代码,则可获取到PID=4884的PBase的动态地址中的数据,如下图所示;

至于如何将数据写出四级偏移的基址上面,则只需要取出pbase里面的基址,并通过原函数WIN10_WriteProcessMemory直接写出数据即可,此出的原函数在《内核MDL读写进程内存》中已经做了详细介绍,实现写出代码如下所示;

#include 
#include 
#include 

// 普通Ke内存读取
NTSTATUS KeReadProcessMemory(PEPROCESS Process, PVOID SourceAddress, PVOID TargetAddress, SIZE_T Size)
{
    PEPROCESS SourceProcess = Process;
    PEPROCESS TargetProcess = PsGetCurrentProcess();
    SIZE_T Result;
    if (NT_SUCCESS(MmCopyVirtualMemory(SourceProcess, SourceAddress, TargetProcess, TargetAddress, Size, KernelMode, &Result)))
        return STATUS_SUCCESS;
    else
        return STATUS_ACCESS_DENIED;
}

// Win10 内存写入函数
BOOLEAN WIN10_WriteProcessMemory(HANDLE Pid, PVOID Address, SIZE_T BYTE_size, PVOID VirtualAddress)
{
    PVOID buff1;
    VOID *buff2;
    int MemoryNumerical = 0;
    KAPC_STATE KAPC = { 0 };

    PEPROCESS Process;
    PsLookupProcessByProcessId((HANDLE)Pid, &Process);

    __try
    {
        //分配内存
        buff1 = ExAllocatePoolWithTag((POOL_TYPE)0, BYTE_size, 1997);
        buff2 = buff1;
        *(int*)buff1 = 1;
        if (MmIsAddressValid((PVOID)VirtualAddress))
        {
            // 复制内存
            memcpy(buff2, VirtualAddress, BYTE_size);
        }
        else
        {
            return FALSE;
        }

        // 附加到要读写的进程
        KeStackAttachProcess((PRKPROCESS)Process, &KAPC);
        if (MmIsAddressValid((PVOID)Address))
        {
            // 判断地址是否可写
            ProbeForWrite(Address, BYTE_size, 1);
            // 复制内存
            memcpy(Address, buff2, BYTE_size);
        }
        else
        {
            return FALSE;
        }
        // 剥离附加的进程
        KeUnstackDetachProcess(&KAPC);
        ExFreePoolWithTag(buff2, 1997);
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        return FALSE;
    }
    return FALSE;
}

// 写入整数内存多级偏移
INT64 WIN10_WriteDeviationIntMemory(HANDLE Pid, LONG Base, DWORD offset[32], DWORD len, INT64 SetValue)
{
    INT64 Value = 0;
    LPCVOID pbase = (LPCVOID)Base;
    LPVOID rbuffer = (LPVOID)&Value;

    PEPROCESS Process;
    PsLookupProcessByProcessId((HANDLE)Pid, &Process);

    for (int x = len - 1; x >= 0; x--)
    {
        __try
        {
            KeReadProcessMemory(Process, pbase, rbuffer, 4);
            pbase = (LPCVOID)(Value + offset[x]);
        }
        __except (EXCEPTION_EXECUTE_HANDLER)
        {
            return 0;
        }
    }

    __try
    {
        KeReadProcessMemory(Process, pbase, rbuffer, 4);
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        return 0;
    }

    // 使用原函数写入
    BOOLEAN ref = WIN10_WriteProcessMemory(Pid, (void *)pbase, 4, &SetValue);
    if (ref == TRUE)
    {
        DbgPrint("[内核写成功] # 写入地址: %x \n", pbase);
        return 1;
    }
    return 0;
}

// 驱动卸载例程
VOID UnDriver(PDRIVER_OBJECT driver)
{
    DbgPrint("Uninstall Driver \n");
}

// 驱动入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("Hello LyShark \n");

    DWORD PID = 4884;
    LONG PBase = 0x6566e0;
    LONG Size = 4;
    INT64 SetValue = 100;

    DWORD Offset[32] = { 0 };

    Offset[0] = 0x18;
    Offset[1] = 0x0;
    Offset[2] = 0x14;
    Offset[3] = 0x0c;

    // 写出内存数据
    INT64 write = WIN10_WriteDeviationIntMemory(PID, PBase, Offset, Size, SetValue);

    DbgPrint("PID: %d 基址: %p 偏移长度: %d \n", PID, PBase, Size);
    DbgPrint("[+] 1级偏移: %x \n", Offset[0]);
    DbgPrint("[+] 2级偏移: %x \n", Offset[1]);
    DbgPrint("[+] 3级偏移: %x \n", Offset[2]);
    DbgPrint("[+] 4级偏移: %x \n", Offset[3]);

    DbgPrint("[WriteMemory] 写出偏移数据: %d \n", SetValue);

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

运行如上代码将在0x6566e0所在的基址上,将数据替换为100,实现效果图如下所示;

那么如何实现读写内存浮点数,字节集等多级偏移呢?

其实我们可以封装一个WIN10_ReadDeviationMemory函数,让其只计算得出偏移地址,而所需要写出的类型则根据自己的实际需求配合不同的写入函数完成,也就是将两者分离开,如下则是一段实现计算偏移的代码片段,该代码同样来自于本人的LyMemory驱动读写项目;

#include 
#include 
#include 

// 普通Ke内存读取
NTSTATUS KeReadProcessMemory(PEPROCESS Process, PVOID SourceAddress, PVOID TargetAddress, SIZE_T Size)
{
    PEPROCESS SourceProcess = Process;
    PEPROCESS TargetProcess = PsGetCurrentProcess();
    SIZE_T Result;
    if (NT_SUCCESS(MmCopyVirtualMemory(SourceProcess, SourceAddress, TargetProcess, TargetAddress, Size, KernelMode, &Result)))
        return STATUS_SUCCESS;
    else
        return STATUS_ACCESS_DENIED;
}

// 读取多级偏移内存动态地址
DWORD64 WIN10_ReadDeviationMemory(HANDLE Pid, LONG Base, DWORD offset[32], DWORD len)
{
    INT64 Value = 0;
    LPCVOID pbase = (LPCVOID)Base;
    LPVOID rbuffer = (LPVOID)&Value;

    PEPROCESS Process;
    PsLookupProcessByProcessId((HANDLE)Pid, &Process);

    for (int x = len - 1; x >= 0; x--)
    {
        __try
        {
            KeReadProcessMemory(Process, pbase, rbuffer, 4);
            pbase = (LPCVOID)(Value + offset[x]);
        }
        __except (EXCEPTION_EXECUTE_HANDLER)
        {
            return 0;
        }
    }

    return pbase;
}

// 驱动卸载例程
VOID UnDriver(PDRIVER_OBJECT driver)
{
    DbgPrint("Uninstall Driver \n");
}

// 驱动入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("Hello LyShark \n");

    DWORD PID = 4884;
    LONG PBase = 0x6566e0;
    LONG Size = 4;

    DWORD Offset[32] = { 0 };

    Offset[0] = 0x18;
    Offset[1] = 0x0;
    Offset[2] = 0x14;
    Offset[3] = 0x0c;

    // 写出内存数据
    DWORD64 offsets = WIN10_ReadDeviationMemory(PID, PBase, Offset, Size);

    DbgPrint("PID: %d 基址: %p 偏移长度: %d \n", PID, PBase, Size);
    DbgPrint("[+] 1级偏移: %x \n", Offset[0]);
    DbgPrint("[+] 2级偏移: %x \n", Offset[1]);
    DbgPrint("[+] 3级偏移: %x \n", Offset[2]);
    DbgPrint("[+] 4级偏移: %x \n", Offset[3]);

    DbgPrint("[CheckMemory] 计算偏移地址: %x \n", offsets);

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

相关内容

热门资讯

PHP新手之PHP入门 PHP是一种易于学习和使用的服务器端脚本语言。只需要很少的编程知识你就能使用PHP建立一个真正交互的...
网络中立的未来 网络中立性是什... 《牛津词典》中对“网络中立”的解释是“电信运营商应秉持的一种原则,即不考虑来源地提供所有内容和应用的...
各种千兆交换机的数据接口类型详... 千兆交换机有很多值得学习的地方,这里我们主要介绍各种千兆交换机的数据接口类型,作为局域网的主要连接设...
粉嫩如何诠释霸道 东芝M805... “霸道粉”是个什么玩意东芝M805拿过来的时候,笔者扑哧笑了,不是笑这款笔记本,而是笑这款产品的颜色...
什么是大数据安全 什么是大数据... 在《为什么需要大数据安全分析》一文中,我们已经阐述了一个重要观点,即:安全要素信息呈现出大数据的特征...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
全面诠释网络负载均衡 负载均衡的出现大大缓解了服务器的压力,更是有效的利用了资源,提高了效率。那么我们现在来说一下网络负载...
如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
30分钟搞定iOS自定义相机 最近公司的项目中用到了相机,由于不用系统的相机,UI给的相机切图,必须自定义才可以。就花时间简单研究...
Intel将Moblin社区控... 本周二,非营利机构Linux基金会宣布,他们将担负起Moblin社区的管理工作,而这之前,Mobli...