驱动开发:内核读写内存多级偏移

简介: 让我们继续在`《内核读写内存浮点数》`的基础之上做一个简单的延申,如何实现多级偏移读写,其实很简单,读写函数无需改变,只是在读写之前提前做好计算工作,以此来得到一个内存偏移值,并通过调用内存写入原函数实现写出数据的目的。以读取偏移内存为例,如下代码同样来源于本人的`LyMemory`读写驱动项目,其中核心函数为`WIN10_ReadDeviationIntMemory()`该函数的主要作用是通过用户传入的基地址与偏移值,动态计算出当前的动态地址。

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

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

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

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

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

// 普通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=4884PBase的动态地址中的数据,如下图所示;

image.png

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

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

// 普通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,实现效果图如下所示;

image.png

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

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

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

// 普通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;
}

运行如上代码将动态计算出目前偏移地址的pbase实际地址,实现效果图如下所示;

image.png

目录
相关文章
|
1月前
|
缓存 Java Android开发
安卓应用开发中的内存优化策略
在移动应用开发领域,性能一直是衡量应用质量的重要指标之一。特别是对于安卓平台,由于设备的硬件配置多样化,内存管理成为开发者面临的重大挑战。本文将深入探讨针对安卓平台的内存优化技巧,包括内存泄漏的预防、合理使用数据结构和算法、以及高效的资源释放机制。通过这些方法,开发者可以显著提升应用的性能和用户体验。
|
1月前
|
监控 算法 Android开发
安卓应用开发中的内存优化策略
【2月更文挑战第30天】随着移动设备性能的不断提升,用户对应用程序的体验要求越来越高。在安卓应用开发中,内存管理是影响应用性能和用户体验的关键因素之一。本文将探讨针对安卓平台的内存优化技巧,包括避免内存泄漏、合理使用数据结构和算法、优化图片资源处理等策略,旨在帮助开发者提升应用性能和稳定性。
19 1
|
3月前
|
存储 程序员
【汇编】内存的读写与地址空间、寄存器及数据存储
【汇编】内存的读写与地址空间、寄存器及数据存储
158 1
【汇编】内存的读写与地址空间、寄存器及数据存储
|
4月前
|
存储 Java C#
C# | 使用Memory<T>高效地读写内存数据
Memory<T>是什么? 它是一种可变大小、可读写的内存块,可以安全地暴露给用户代码进行操作。 为什么要使用Memory<T>? 使用它有许多好处。最主要的是善用它可以提高代码的性能。因为Memory<T>是可变的,所以我们可以直接在内存中操作数据,而不需要进行额外的拷贝操作。 使用Memory<T>还可以减少垃圾回收的压力,因为我们不需要创建新的对象来存储数据。 Memory<T>还可以与Span<T>和ReadOnlySpan<T>类型一起使用,这些类型可以方便地对数据进行访问和操作。
36 0
|
4月前
|
存储 安全 数据安全/隐私保护
3.2 Windows驱动开发:内核CR3切换读写内存
CR3是一种控制寄存器,它是CPU中的一个专用寄存器,用于存储当前进程的页目录表的物理地址。在x86体系结构中,虚拟地址的翻译过程需要借助页表来完成。页表是由页目录表和页表组成的,页目录表存储了页表的物理地址,而页表存储了实际的物理页框地址。因此,页目录表的物理地址是虚拟地址翻译的关键之一。在操作系统中,每个进程都有自己的地址空间,地址空间中包含了进程的代码、数据和堆栈等信息。为了实现进程间的隔离和保护,操作系统会为每个进程分配独立的地址空间。在这个过程中,操作系统会将每个进程的页目录表的物理地址存储在它自己的CR3寄存器中。当进程切换时,操作系统会修改CR3寄存器的值,从而让CPU使用新的页
57 0
3.2 Windows驱动开发:内核CR3切换读写内存
|
5月前
|
存储 安全 API
3.5 Windows驱动开发:应用层与内核层内存映射
在上一篇博文`《内核通过PEB得到进程参数》`中我们通过使用`KeStackAttachProcess`附加进程的方式得到了该进程的PEB结构信息,本篇文章同样需要使用进程附加功能,但这次我们将实现一个更加有趣的功能,在某些情况下应用层与内核层需要共享一片内存区域通过这片区域可打通内核与应用层的隔离,此类功能的实现依附于MDL内存映射机制实现。
57 0
3.5 Windows驱动开发:应用层与内核层内存映射
|
5月前
|
安全 Windows
3.3 Windows驱动开发:内核MDL读写进程内存
MDL内存读写是一种通过创建MDL结构体来实现跨进程内存读写的方式。在Windows操作系统中,每个进程都有自己独立的虚拟地址空间,不同进程之间的内存空间是隔离的。因此,要在一个进程中读取或写入另一个进程的内存数据,需要先将目标进程的物理内存映射到当前进程的虚拟地址空间中,然后才能进行内存读写操作。
44 0
3.3 Windows驱动开发:内核MDL读写进程内存
|
5月前
|
缓存 监控 Anolis
|
1月前
|
存储 JSON 监控
Higress Controller**不是将配置信息推送到Istio的内存存储里面的**。
【2月更文挑战第30天】Higress Controller**不是将配置信息推送到Istio的内存存储里面的**。
14 1
|
2天前
|
存储 算法
【三种方法】求一个整数存储在内存中二进制中的1的个数附两道课外练习题
【三种方法】求一个整数存储在内存中二进制中的1的个数附两道课外练习题
7 0

相关实验场景

更多