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