我正在尝试将代码注入我编写的一个小游戏中,以永久保持健康,我想通过向每次注入10之前将健康设置为render()的游戏中注入代码来实现此目的,从而使游戏从根本上不再结束。


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

我设法找到了存储health的地址(0x0000000140005034)和主循环地址(0x000000014000107D),但是我似乎不知道该怎么做

有人可以给我一个提示,告诉我如何实现此目标吗?

我试图用来将代码注入到我的应用程序中的代码:

#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);
    }
}


这是我在游戏中使用的代码: br />

评论

如果您的最终目标只是自己玩游戏,请考虑使用作弊引擎冻结运行状况计数。无需编写您自己的黑客代码

@JavaMan该项目的整个目标是学习如何做,我想帮助其他游戏的开发者,但我需要首先了解基础知识。如果我真的愿意,我可以简单地更改代码以永不降低运行状况并重新编译它。

#1 楼


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


在IDA中加载可执行文件时,它也会被加载从可执行文件的标头中获取首选映像库。例如,可以通过菜单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,解决问题不需要注入。现在,您可以使用ReadProcessMemoryWriteProcessMemory之类的功能,而无需以小于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注入和函数拦截教程