修改通过 IDA 找到的地址

逆向工程 艾达 C++ 记忆 注射
2021-06-19 04:56:19

我正在尝试将代码注入我编写的一个小游戏中以保持健康,我想通过将代码注入游戏中来做到这一点,该代码将10在每次render()调用之前设置健康,从而使游戏本质上永不结束。


对于我的项目,我用 C++ 编写了一个小示例游戏,然后在 IDA 中编译和反编译。

我设法找到了health存储0x0000000140005034地址 ( 0x000000014000107D)和主循环地址 ( ),但是我似乎无法弄清楚如何从 IDA 中的静态地址转到实际地址在另一个 C++ 程序中生成进程。

谁能给我一个关于如何实现这一目标的提示?

这是我试图用来将代码注入我的应用程序的代码:

#include <iostream>
#include <Windows.h>

#include "config.h"

STARTUPINFO info = {sizeof(info)};
PROCESS_INFORMATION process;


void injectCodeIntoProccess()
{
    // Stuck here, no idea how I could modify my game's code on every loop iteration.
}


void main()
{
    if (CreateProcess(EXECUTABLE_PATH, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &info, &process)) {
        injectCodeIntoProccess();
        WaitForSingleObject(process.hProcess, INFINITE);
    } else {
        std::cerr << "Error whilst creating process." << std::endl;
        exit(1);
    }
}

这是我用于我的游戏的代码:

#include <iostream>
#include <Windows.h>


// Health variable
// Address (as found in IDA): .data:0x0000000140005034
int health = 10;


// Render the game
//
// In real world scenario's this would be a 3d world or something
// but we're using 'Health: *..' as the game itself.
void render()
{
    std::cout << "Health: ";
    for (int i = 0; i < health; i++) std::cout << "*";
    std::cout << std::endl;
}


// Gets input from the user
//
// If the user presses a spacebar, increase the health
// by one otherwise, decrease the health by one.
void input()
{
    if (GetKeyState(VK_SPACE) < 0) {
        health++;
    } else {
        health--;
    }
}


// Simple game
//
// If the player reaches 0 health game over.
// This is checked every 500ms
int main()
{
    while (health > 0) 
    {
        input();
        render();
        Sleep(500);
    }

    return 0;
}
1个回答

我如何从 IDA 中的静态地址转到另一个 C++ 程序中的实际地址

当在 IDA 中加载可执行文件时,它会加载来自可执行文件头的首选映像库。例如,可以通过 menu 进行查看Edit -> Segments -> Rebase program...值有一个图像库。

从这里采取的另一种方法:转到File -> Script command...或按Shift+F2,选择 Python 作为脚本语言,键入

print "%x" % (idaapi.get_imagebase())

作为脚本正文并按Run您将在输出窗口中看到当前图像库。

无论哪种方式,您都会知道 IDA 分配的图像库。在您的示例中,最有可能是0x0000000140000000. 现在你可以从你已经知道的变量地址中减去它以获得可执行文件中变量的偏移量(比如,我们正在处理health存储在 的变量0x0000000140005034):

0x0000000140005034 - 0x0000000140000000 = 0x5034

现在您必须在另一个进程中找到目标模块的图像库。在您的示例中,它是可执行文件本身的映像库,而不是 DLL。可以通过此链接在 stackoverflow 中找到获取 exe 映像库的过程

代码贴在下面:

#include <windows.h>

#include <Psapi.h>

#include <algorithm>
#include <exception>
#include <iostream>
#include <string>
#include <vector>

// Linking Psapi.lib
#pragma comment(lib, "Psapi.lib")

struct close_on_exit
{
    close_on_exit(HANDLE ptr)
        : ptr_(ptr)
    { };

    ~close_on_exit()
    {
        if (ptr_)
        {
            ::CloseHandle(ptr_);
            ptr_ = nullptr;
        }
    }

private:
    HANDLE ptr_;
};

int main()
{
    //
    // code of creating process here
    //

    DWORD id = process.dwProcessId;  // obtained from PROCESS_INFORMATION structure

    HANDLE process_handle = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);
    close_on_exit free_ptr(process_handle);  // auto release memory on exception

    if (NULL == process_handle)
    {
        throw std::exception("OpenProcess failed");
    }

    DWORD bytes_requested = 0;
    if (::EnumProcessModules(process_handle, NULL, 0, &bytes_requested) == 0)
    {
        throw std::exception("EnumProcessModules failed");
    }

    // Retrieve module handles into array with appropriate size.
    std::vector<HMODULE> module_handles(bytes_requested / sizeof(HMODULE));
    if (::EnumProcessModules(process_handle, module_handles.data(), bytes_requested, &bytes_requested))
    {
        char module_name[MAX_PATH];

        for (auto ci = module_handles.begin(); ci != module_handles.end(); ++ci)
        {
            if (::GetModuleFileNameExA(process_handle, *ci, module_name, sizeof(module_name)))
            {
                MODULEINFO module_info = { 0 };
                if (false == ::GetModuleInformation(process_handle, *ci, &module_info, sizeof(module_info)))
                {
                    throw std::exception("GetModuleInformation failed");
                }

                const std::string exe_ending = ".exe";
                std::string current_module = module_name;

                // Make lower case of module name if it is something like "EXE".
                std::transform(current_module.begin(), current_module.end(), current_module.begin(), ::tolower);

                if (std::equal(exe_ending.rbegin(), exe_ending.rend(), current_module.rbegin()))
                {
                    std::cout << "Process: " << current_module << std::endl;
                    std::cout << " id: " << id << std::endl;
                    std::cout << " image base: 0x" << std::hex <<
                        reinterpret_cast<uintptr_t>(module_info.lpBaseOfDll) << std::endl;

                    break;
                }
            }
            else
            {
                throw std::exception("GetModuleFileNameExA failed");
            }
        }
    }

    return 0;
}

然后你应该只是将偏移量添加到这个图像基数并在实际过程中获取变量的地址。

IMO,不需要注入来解决您的问题。您现在可以使用ReadProcessMemory& 之类的函数WriteProcessMemory而无需以小于间隔的时间注入进程500- 这样您将从外部应用程序更新健康状况比从游戏本身更新健康状况更频繁。

另一种变体是在运行时修改游戏的代码(而不是变量本身)。为此,您应该创建暂停的进程(或在运行时暂停它)。您可以通过与学习变量地址相同的方式学习更新变量状态的指令的地址。然后您可以将其修改为始终将变量设置为某个预定义值的指令。

例如,健康下降的代码在我的机器上看起来像这样(调试 x64):

000000013FD32ACE 8B 05 2C 25 01 00    mov eax,dword ptr [health (013FD45000h)]  
000000013FD32AD4 FF C8                dec eax  

您可以在已经提到的WriteProcessMemory功能的帮助下像这样修改它

000000013FD32ACE B8 10 00 00 00           mov eax,10
000000013FD32AD3 90                       nop
000000013FD32AD4 90                       nop
000000013FD32AD5 90                       nop

如果你仍然想玩代码注入,这里有关于这个主题的教程:

将代码注入另一个进程的三种方法

DLL注入与函数拦截教程