4

I'm trying to inject code into a small game I wrote to keep the health up forever, I want to do this by injecting code into the game that would set the health to 10 before each render() call, causing the game to in essence never end.


For my project I wrote a small sample game in C++, which I then compiled, and decompiled in IDA.

I managed to to find the address in which the health is being stored (0x0000000140005034), and the main loop address (0x000000014000107D), however I can't seem to figure out how I'd go from a static address in IDA, to an actual address when spawning the process in another C++ program.

Could anyone give me a hint on how I could achieve this?

Here's the code I'm trying to use to inject code into my application:

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

Here's the code I used for my game:

#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;
}
2
  • if your ultimate goal is just to play the game yourself, consider using Cheat Engine to freeze the health count. There is no need write your own hacking code
    – JavaMan
    Commented Feb 18, 2017 at 14:19
  • 1
    @JavaMan the whole goal of the project is to learn how to do it, I want to help build mods for another game but I need to understand the basics first. I could simply change the code to never decrease the health and recompile it if I really wanted to.
    – Paradoxis
    Commented Feb 18, 2017 at 14:22

1 Answer 1

2

how I'd go from a static address in IDA, to an actual address ... in another C++ program

When executable file is loaded in IDA, it is loaded with the preferred image base taken from executable's header. It can be viewed, for example, through menu Edit -> Segments -> Rebase program.... Value there is an image base.

Another way taken from here: go to File -> Script command... or press Shift+F2, select Python as scripting language, type

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

as script body and press Run. You will see current image base in the Output Window.

Either way you will know image base assigned by IDA. In your example it's most likely 0x0000000140000000. Now you can subtract it from the variable address you already know to get variable's offset in the executable (say, we're dealing with health variable stored at 0x0000000140005034):

0x0000000140005034 - 0x0000000140000000 = 0x5034

Now you have to find image base of the target module in another process. In your example it's the image base of the executable itself, not DLL. Process of obtaining the exe image base may be found at stackoverflow through this link.

The code is posted below:

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

Then you should just add the offset to this image base and get address of the variable in the real process.

IMO, injection is unnecessary for solving your issue. You may now use functions like ReadProcessMemory & WriteProcessMemory without injecting into process with interval less than 500 - this way you will update health from the external application more frequently than it will be updated from the game itself.

Another variant is to modify the code (not the variable itself) of your game in runtime. For this you should create your process suspended (or suspend it in runtime). You may learn address of the instruction which updates variable's state the same way as you learned the variable's address. Then you may modify it to the instruction which always sets the variable to some predefined value.

For example, code of health decreasing looks like this on my machine (debug x64):

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

You may modify it like this with the help of already mentioned WriteProcessMemory function:

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

If you still want to play with code injection, here are tutorials on this topic:

Three Ways to Inject Your Code into Another Process

DLL Injection and function interception tutorial

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.