#1 - 2023-10-9 22:48
石原英里華(常盤るる) (来如遠致)
近日,在我针对一款游戏Read-Only部分的程序或Memory,渴望使其变为Read/Write Memory。因为种种原因,最好也最为可行的办法是建立一个Kernel Driver,使用Memory Descriptor List将其映射为可修改的Memory。
其程式逻辑为
一、阅取ini File,获得程式PID(进程ID)
二、读取目标程式Base address与Section Size
三、完成MDL(Memory Descriptor List)映射。

然而在编译完成后,使用DebugView程式监视Kernel,无法找到任何Ouput Messages。
(注:此点已经解决。KdPrint需要在Debug模式下完成compile才会正常Print。另外,DebugView需要勾选所有Kernel相关选项)
另外,使用sc delete <ServiceName>删除Service后,无法再次create或是start。其均会显示The specified service has been marked for deletion,依据stackoverflow的某些建议无果。需要对PC进行Restart。

如果方便,可以帮助我审视一下代码吗?
一切程式源码与可利用程式可以在此找到。
https://icedrive.net/s/SDkiPQwVuvxgwC8CTatPSjhPNkzV
Disable DSE来自Unknowncheats,它使您可以运行未经签名的Kernel Driver。
如果您需要调试,我可以直接协助您编译新的Kernel Driver。
#2 - 2023-10-9 22:50
(来如遠致)
我修改了Debugging的思路。先测试MDL部分程式码能否正常运行(address与size被嵌入程式码内),再测试另外的部分。这一部分也是最为重要的部分。
我所渴望修改的游戏或程式并非完全的网络游戏,其数据也并非通过Server端进行处理。我所渴望修改的并非为普通的Memory,而是受保护的程式Memory。例如处理扣减HP的程式,例如CMP金钱是否逾越最大值的程式。
测试时,Kernel Driver可以正常运行。然而我似乎仍然无法修改Read-Only Memory。我想这是源于部分理解错误。
例如,Address应该填入何种值?我通过Cheat Engine找到程式/游戏受保护部分的部分Address,然后将其填入程式码中。然后通过Cheat Engine的Memory Viewer对值进行修改。Cheat Engine Debugger全程运行于Kernel Mode。且Extra Settings中选项均有勾选。选项为Query memory region routines与Read/Write Process Memory,甚至是Process Watcher。我是否应该尝试DBVM状态下修改?
例如,此处使用的关键function MmMapLockedPagesSpecifyCache 与MmProtectMdlSystemAddress,它们可以将映射后的Read-Only Memory更正为Read/Write Memory。我可能没有正确理解“映射Pages”的正确含义?
例如,这是一种使Kernel Meamory得到修改的方法。我是否应该使用KeStackAttachProcess与User Mode进程连接?
以下是程式码。

Test1.cpp
#include "Test1.h"
#include <ntddk.h>
#include <wdm.h>
#include <stdlib.h>
#include <ntstrsafe.h>  

NTSTATUS make_page_writable()
{
    // Hardcoded values for address and size
    PVOID address = (PVOID)0x7FF75C38576A;  // replace with the actual address you want
    ULONG size = 4096;                // replace with the actual size you want

    // Create a memory descriptor list to map read-only (RX) memory as read-write
    auto mdl = IoAllocateMdl(address, size, FALSE, FALSE, NULL);
    if (mdl == NULL)
    {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    // Make memory pages resident in RAM and make sure they won't get paged out
    MmProbeAndLockPages(mdl, KernelMode, IoReadAccess);

    // Map the physical page described by mdl to the virtual memory
    auto RWMapping = MmMapLockedPagesSpecifyCache(mdl, KernelMode, MmNonCached, NULL, FALSE, NormalPagePriority);
    if (RWMapping == NULL)
    {
        MmUnlockPages(mdl);
        IoFreeMdl(mdl);
        return STATUS_INTERNAL_ERROR;
    }

    // Set new mapping page protection to read-write
    auto status = MmProtectMdlSystemAddress(mdl, PAGE_READWRITE);
    if (!NT_SUCCESS(status))
    {
        MmUnmapLockedPages(RWMapping, mdl);
        MmUnlockPages(mdl);
        IoFreeMdl(mdl);
        return status;
    }

    // Cleanup
    MmUnmapLockedPages(RWMapping, mdl);
    MmUnlockPages(mdl);
    IoFreeMdl(mdl);

    return STATUS_SUCCESS;
}

VOID UnloadDriver(_In_ PDRIVER_OBJECT DriverObject) {
    UNREFERENCED_PARAMETER(DriverObject);
    KdPrint(("Driver unloaded.\n"));
}

extern "C"
{
    DRIVER_INITIALIZE DriverEntry;
    _Use_decl_annotations_
        NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
    {
        UNREFERENCED_PARAMETER(RegistryPath);
        // Set our unload function
        DriverObject->DriverUnload = UnloadDriver;

        return STATUS_SUCCESS;
    }
}
#3 - 2023-10-9 23:22
ChatGPT:
你的代码中提供了对一个DriverEntry的实现,其目的是从一个INI文件中读取进程ID(PID),并获取进程的基地址和段大小。首先,我会对你的代码进行一些评论和建议,然后再解答你提到的问题。

代码审视
INI文件路径: 你定义了一个宏 INI_FILE_PATH,它指向你的INI文件路径。但在示例中,你直接为PID赋值了一个常数,没有实际从文件中读取它。
ReadPidFromIni函数: 这个函数初始化了文件路径和对象属性,但并没有真正打开文件并从中读取PID。你需要用ZwCreateFile/ZwReadFile等函数完成实际读取的操作。
GetProcessInfo函数: 同样,你使用了假设值作为基地址和段大小,而没有真正地获取目标进程的这些信息。
关于KdPrint输出不显示在DebugView中的问题:
请确保你的DebugView工具以管理员权限运行。
在使用DebugView之前,请确保启用“Capture Kernel”模式。
在你的代码中,确保你的消息具有足够的可见性。例如:KdPrint(("---------- DriverEntry: Driver loaded. ----------\n"));
确保你的驱动程序实际上被加载了。
关于"service has been marked for deletion"的问题:
这个错误通常发生在尝试删除一个服务并立即再次创建它时。
关闭所有引用或使用该服务的句柄或进程。
确保没有任何打开的服务控制管理器的实例(SCM)。例如:关闭“服务”窗口或其他使用SCM的应用程序。
如果上述方法不奏效,重新启动确实是一种方式。
对于你的代码,建议:

完成从INI文件读取PID的逻辑。
完成从PID获取进程信息的逻辑。
确保驱动正确加载,可以使用工具如OSR Driver Loader或者手动创建服务加载驱动。
如果你需要关于如何从文件中读取数据或如何从PID获取进程信息的指导,请告诉我
#3-1 - 2023-10-10 00:19
Leitmotif
没用 不用看了(bgm38)
#4 - 2023-10-10 00:24
(nya)
有点硬核,期待大佬出现
#5 - 2023-10-10 14:01
不熟NT 但你这问法感觉你根本没懂你在写什么 作业?
#5-1 - 2023-10-10 19:45
石原英里華(常盤るる)
真是相当遗憾!
相比于NT,前置的知识应该是Kernel的内存与User的内存是何种关系。这一点我的确没有任何了解。
#5-2 - 2023-10-11 15:24
可憐ソース
石原英里華(常盤るる) 说: 真是相当遗憾!
相比于NT,前置的知识应该是Kernel的内存与User的内存是何种关系。这一点我的确没有任何了解。
x86架构上 虚拟(virtual)内存是从物理(physical)内存映射(map)出来的 映射的基本单位就是页(page) 映射结构是存在页表(page table)中的 你用的MDL里面是存的一部分的映射关系
一个物理内存页可以映射到多个不同的虚拟内存地址 可以在多个页表中映射到不同的位置 用户进程和你的内核驱动不一定用同一个页表
我理解你想把某个进程的代码页映射到你驱动程序的虚拟内存空间里 大概是要拿到MDL再映射
了解x86 paging怎么工作看 intel sdm vol 3 chap 4: https://www.intel.com/content/ww ... ical/intel-sdm.html
NT内核怎么操作内存查microsoft的文档
一般概念上的分页怎么工作 去修一门操作系统的课程
#5-3 - 2023-10-11 20:30
石原英里華(常盤るる)
可憐ソース 说: x86架构上 虚拟(virtual)内存是从物理(physical)内存映射(map)出来的 映射的基本单位就是页(page) 映射结构是存在页表(page table)中的 你用的MDL里面是存的一...
十分感谢。我已于今日完成编译。
#6 - 2023-10-10 14:19
(bgm38) 首先,看不懂
(bgm38) 其次,内核态的内存地址和用户态不是一一对应的,你需要找到用户态那个内存在内核态实际映射的值
(bgm38) 第三,上面是我瞎说的,我不会
#6-1 - 2023-10-10 19:46
石原英里華(常盤るる)
原来如此!十分感谢点拨!