代码之家  ›  专栏  ›  技术社区  ›  Hamid

使用与先前打开完全相同的地址结构启动进程

  •  0
  • Hamid  · 技术社区  · 7 年前

    是否可以在windows中使用与上次打开进程完全相同的地址结构启动进程?

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

    1 回复  |  直到 7 年前
        1
  •  1
  •   GuidedHacking    7 年前

    不是说这是不可能的,但由于进程将使用的动态内存分配例程包括新操作符和malloc(),这实际上是太多的工作。此外,当可执行文件导入的DLL加载到内存中时,它们具有首选的imagebase,但如果该地址已被使用,则操作系统会将其加载到不同的内存位置。此外,可以在进程上启用地址空间布局随机化(ASLR),这是一种将代码段的内存地址随机化的安全措施。

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

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

    作弊引擎内置了这三个功能。将地址保存到表中时,通常会将其保存为模块+相对偏移量。您可以用指针扫描地址并将其另存为多级指针,也可以自己反转指针并手动将其放入表中。模式扫描是通过使用CE脚本实现的,您可以将其放在作弊表中。

    在这种情况下,ammo变量可能是一个“静态地址”,这意味着它相对于模块的基址。您可能会在作弊引擎中看到它被列为“client.dll+0xDEADCODE”。您只需在运行时获取模块的基址并添加相对偏移量。

    如果您想在C++中进行外部黑客攻击,可以这样开始。

    在外部黑客中,可以通过遍历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;
    

    如果地址不是“静态”的,您可以找到指向它的指针,指针的基址必须是静态的,然后您只需按照上面的指南,取消对指针每一级的引用并添加偏移量。

    当然,我也有一个函数:)

    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教程,看看它是如何完成的,更好地用视频格式进行解释。