启动一个与之前打开的地址结构完全相同的进程

starting a process with exactly the same address structure as previous openning

是否可以在windows中启动一个进程,其地址结构与之前打开进程的地址结构完全相同?

为了阐明这个问题的目的,我应该提到我使用 cheatengine (http://www.cheatengine.org/) 来作弊一些游戏!它包括多次迭代以找到参数(例如弹药)并将其冻结。但是,每次重启游戏,由于游戏的内存结构发生了变化,我需要重新经历一次耗时的迭代。所以,如果有一种方法可以使用与以前完全相同的内存结构启动游戏,我就不需要进行迭代了。

并不是说这是不可能的,但由于进程将使用的动态内存分配例程(包括 new 运算符和 malloc()),这实际上工作量太大。此外,当 executable 导入的 DLL 被加载到内存中时,它们有一个首选的映像库,但如果该地址已被使用,OS 会将其加载到不同的内存位置。此外,可以在进程上启用地址 Space 布局随机化 (ASLR),这是一种随机化代码段内存地址的安全措施。

您的问题的解决方案比您所要求的要容易得多。要击败上述动态内存分配,您仍然可以使用以下方法解析变量的正确地址:

  • 模块基础的相对偏移量
  • 多级指针
  • 模式扫描

Cheat Engine 内置了所有这 3 个功能。当您将地址保存到 table 时,通常会将其保存为模块 + 相对偏移量。您可以指针扫描地址并将其保存为多级指针或自己反转指针并手动将其放入table。模式扫描是通过使用 CE 脚本实现的,您可以将其放在 Cheat Table.

在这种情况下,弹药变量可能是 "static address",这意味着它是相对于模块基址的。您可能会在 Cheat Engine 中看到它作为 "client.dll + 0xDEADCODE" 列出。您只需在运行时获取模块的基地址并添加相对偏移量。

如果您想在 C++ 中进行外部 hack,您可以像这样开始。

在外部 hack 中,您通过遍历 ToolHelp32Snapshot 来执行此操作:

uintptr_t GetModuleBase(const wchar_t * ModuleName, DWORD ProcessId) {
    // This structure contains lots of goodies about a module
    MODULEENTRY32 ModuleEntry = { 0 };
    // Grab a snapshot of all the modules in the specified process
    HANDLE SnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ProcessId);

    if (!SnapShot)
        return NULL;

    // You have to initialize the size, otherwise it will not work
    ModuleEntry.dwSize = sizeof(ModuleEntry);

    // Get the first module in the process
    if (!Module32First(SnapShot, &ModuleEntry))
        return NULL;

    do {
        // Check if the module name matches the one we're looking for
        if (!wcscmp(ModuleEntry.szModule, ModuleName)) {
            // If it does, close the snapshot handle and return the base address
            CloseHandle(SnapShot);
            return (DWORD)ModuleEntry.modBaseAddr;
        }
        // Grab the next module in the snapshot
    } while (Module32Next(SnapShot, &ModuleEntry));

    // We couldn't find the specified module, so return NULL
    CloseHandle(SnapShot);
    return NULL;
}

要获取进程 ID,您需要执行以下操作:

bool GetPid(const wchar_t* targetProcess, DWORD* procID)
{
    HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (snap && snap != INVALID_HANDLE_VALUE)
    {
        PROCESSENTRY32 pe;
        pe.dwSize = sizeof(pe);
        if (Process32First(snap, &pe))
        {
            do
            {
                if (!wcscmp(pe.szExeFile, targetProcess))
                {
                    CloseHandle(snap);
                    *procID = pe.th32ProcessID;
                    return true;
                }
            } while (Process32Next(snap, &pe));
        }
    }
    return false;
}

使用我的示例,您可以组合这些函数并执行:

DWORD procId;
GetPid(L"game.exe", &procId);
uintptr_t modBaseAddr = GetModuleBase(L"client.dll", procId);
uintptr_t ammoAddr = modBaseAddr + 0xDEADCODE;

如果地址不是"static"你可以找到一个指向它的指针,指针的基地址必须是静态的然后你只要按照上面的指南,取消引用指针的每一层并添加偏移量。

当然我也有一个功能:)

uintptr_t FindDmaAddy(HANDLE hProcHandle, uintptr_t BaseAddress, uintptr_t Offsets[], int PointerLevel)
{
    uintptr_t pointer = BaseAddress;
    uintptr_t pTemp;

    uintptr_t pointerAddr;
    for (int i = 0; i < PointerLevel; i++)
    {
        if (i == 0)
        {
            ReadProcessMemory(hProcHandle, (LPCVOID)pointer, &pTemp, sizeof(pTemp), NULL);
        }
        pointerAddr = pTemp + Offsets[i];

        ReadProcessMemory(hProcHandle, (LPCVOID)pointerAddr, &pTemp, sizeof(pTemp), NULL);
    }
    return pointerAddr;
}

我强烈建议您观看一些 Youtube 教程以了解它是如何完成的,最好以视频格式进行解释。