10

I'd like to have a function that allows me to read the memory of another process. I was thinking about something like this (pseudo code):

staticAddress = 0x026E0DC4
processId = GetProcessIdByName(processName)
processHandle = GetProcessHandle(processId)
processBaseAddress = GetBaseAddress(processHandle)
addressToRead = processBaseAddress+staticAddress
readValueAsInt = ReadMemoryInt(processHandle, addressToRead)
readValueAsFloat = ReadMemoryFloat(processHandle, addressToRead)
readValueAsString = ReadMemoryString(processHandle, addressToRead)

Would that even be possible? Here is what I got so far:

#include <Windows.h>
#include <conio.h>
#include <tlhelp32.h>
#include <string>
#include <psapi.h>
#pragma comment( lib, "psapi" )

int GetProcessId(char* ProcName) {
    PROCESSENTRY32 pe32;
    HANDLE hSnapshot = NULL;
    pe32.dwSize = sizeof( PROCESSENTRY32 );
    hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );

    if( Process32First( hSnapshot, &pe32 ) ) {
        do {
            if( strcmp( pe32.szExeFile, ProcName ) == 0 )
                break;
        } while( Process32Next( hSnapshot, &pe32 ) );
    }

    if( hSnapshot != INVALID_HANDLE_VALUE )
        CloseHandle( hSnapshot );

    return pe32.th32ProcessID;  
}

int GetModuleBase(HANDLE processHandle, string &sModuleName) 
{ 
   HMODULE *hModules; 
   char szBuf[50]; 
   DWORD cModules; 
   DWORD dwBase = -1; 
   //------ 

   EnumProcessModules(processHandle, hModules, 0, &cModules); 
   hModules = new HMODULE[cModules/sizeof(HMODULE)]; 

   if(EnumProcessModules(processHandle, hModules, cModules/sizeof(HMODULE), &cModules)) { 
      for(int i = 0; i < cModules/sizeof(HMODULE); i++) { 
         if(GetModuleBaseName(processHandle, hModules[i], szBuf, sizeof(szBuf))) { 
            if(sModuleName.compare(szBuf) == 0) { 
               dwBase = (DWORD)hModules[i]; 
               break; 
            } 
         } 
      } 
   } 

   delete[] hModules; 

   return dwBase; 
}


int ReadMemoryInt(HANDLE processHandle, LPCVOID address) {
    //LPVOID buffer = ??;
    //SIZE_T size = ??;
    SIZE_T NumberOfBytesToRead = 4; //??
    ReadProcessMemory(processHandle, address, buffer, size, NumberOfBytesToRead)
    return buffer; //??
}

int ReadMemoryFloat(HANDLE processHandle, LPCVOID address) {
    //LPVOID buffer = ??;
    //SIZE_T size = ??;
    SIZE_T NumberOfBytesToRead = 8; //??
    ReadProcessMemory(processHandle, address, buffer, size, NumberOfBytesToRead)
    return buffer; //??
}

int ReadMemoryString(HANDLE processHandle, LPCVOID address) {
    //LPVOID buffer = ??;
    //SIZE_T size = ??;
    SIZE_T NumberOfBytesToRead = 999; //??
    ReadProcessMemory(processHandle, address, buffer, size, NumberOfBytesToRead)
    return buffer; //??
}

int main()
{
    //read an integer from "Program.exe"+0x05D8A3C4
    int address = 0x05D8A3C4;
    char* processName = "Program.exe";
    int processId = GetProcessId(processName);
    HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, false, processId);
    int processBaseAddress = GetModuleBase(processHandle, (string)"Program.exe";
    LPCVOID actualAddress = processBaseAddress+address;
    int readValue = ReadMemory(processHandle, actualAddress);
    std::cout << readValue << std::endl;
    CloseHandle(processHandle);
    return 0;
}

As you can see form the question marks in the code I'm really unsure about the "buffer" and "size" parameters of ReadProcessMemory. I'd really appreciate it if someone could help me figuring this out.

Forivin
  • 14,780
  • 27
  • 106
  • 199
  • 1
    So many questions marks! What was the question? – harper Oct 26 '13 at 12:28
  • The question is below the code. "Would you help me to complete the code, explain my mistakes and lead me in the right direction?" – Forivin Oct 26 '13 at 12:58
  • OP does not know about memory layout, endianess, and pointer/value inside the memory. Please be polite. @OP: AFAIK this can get You in hard work. – icbytes Oct 26 '13 at 13:54
  • 1
    It sounds like you have some fundamental problems with C++ concepts, like how to pass a pointer to a variable. You should probably become better-conversant in C++ before you try to tackle something as complicated as this. (Also, processes don't have base addresses. Modules have base addresses. A process contains many modules.) – Raymond Chen Oct 26 '13 at 13:58
  • This isn't really appropriate. It looks like you want someone to write your program for you. – David Heffernan Oct 26 '13 at 14:38
  • 2
    @RaymondChen: It is not really especially complicated. E.g. I wrote programs doing this, before I knew what an array is. – BeniBela Oct 26 '13 at 16:06
  • I was talking about the base address of the main module. I already got it to work now and put it in my code above. @David Heffernan take a close look at the code. I already did my best and the code is mostly finished. I just need to know how I can use ReadProcessMemory to read an int/float/string. And I'm definitely not asking you to write a whole program. I'm asking you to complete mine. Or to give me advices how I could do it. Or maybe you find some mistakes I should know about. And yes, I'm a noob I haven't done too much in C++. – Forivin Oct 26 '13 at 16:15

2 Answers2

9

https://github.com/T-vK/Memory-Hacking-Class
Is a pretty simple class to do all that and even more.
Here is a list with all the methods it supports:

GetProcessId()
GetModuleBase()
SetPrivilege()
GetDebugPrivileges()
ReadInt()
GetPointerAddress()
ReadPointerInt()
ReadFloat()
ReadPointerFloat()
ReadText()
ReadPointerText()

Example usage:

#include "Memory.hpp"
using std::string;

int main() {
    char* TARGET_PROCESS_NAME = "League of Legends.exe";

    int GAME_VERSION_MODULE_OFFSET = 0x2A1D738; // [Base address of 'League of Legends.exe']+0x2A1D738 (address of a string containing a version number)

    Memory Memory;
    Memory.GetDebugPrivileges();
    int processId = Memory.GetProcessId(TARGET_PROCESS_NAME);
    HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, false, processId);

    int baseAddress = Memory.GetModuleBase(processHandle, (string)TARGET_PROCESS_NAME);

    int gameVersionAddress = baseAddress + GAME_VERSION_MODULE_OFFSET;

    string gameVersion = Memory.ReadText(processHandle, gameVersionAddress);

    std::cout << "Game version: " << gameVersionAddress << std::endl;

    cin.get();
    return 0;
}

In case you were wondering, yes, I'm the author.

Forivin
  • 14,780
  • 27
  • 106
  • 199
  • 1
    One thing I don't understand in game hacking or in other areas, how the cheats can have the offsets hardcoded? I mean the O.S always loads a random chunk in memory when the game process starts and the addresses should be always different? – redigaffi Aug 21 '19 at 04:58
  • 4
    The base address of the process is always different. The offsets are simply relative to the base address and thus never change (unless the game has updated and then runs new code). – Forivin Aug 21 '19 at 09:09
  • 10
    That moment when it's 2020 and you realize that in 2017 you answered a question that you asked in 2013. – Forivin May 13 '20 at 20:22
  • @Forivin how do you do in order to find the last address of the process you are trying to read? – darclander May 15 '20 at 14:47
  • What do you mean by last address? Generally I use tools like Cheat Engine, OllyDbg and Ida Pro to find an address and then I trace it back to the module to get (for example) a multi-level pointer. If you haven't done this before or don't know assembly well, Cheat Engine is pretty beginner friendly with its pointer scanner. Then it's just a matter of using the code I provided. – Forivin May 16 '20 at 07:52
7

Here is an example for your ReadMemoryInt() function:

int ReadMemoryInt(HANDLE processHandle, LPCVOID address) {
    int buffer = 0;
    SIZE_T NumberOfBytesToRead = sizeof(buffer); //this is equal to 4
    SIZE_T NumberOfBytesActuallyRead;
    BOOL err = ReadProcessMemory(processHandle, address, &buffer, NumberOfBytesToRead, &NumberOfBytesActuallyRead);
    if (err || NumberOfBytesActuallyRead != NumberOfBytesToRead)
      /*an error occured*/ ;
    return buffer; 
}

The & mean that the address of the variable is passed instead its value.

And in ReadMemoryString() you cannot know the actual size you need to read, you could either read a big block (size 999) or read many little blocks till you get one containing \0.

And if you want to know if it works, you can start it in a debugger and look if the values you expect are returned.

Waqar
  • 8,558
  • 4
  • 35
  • 43
BeniBela
  • 16,412
  • 4
  • 45
  • 52
  • Thank you, I just tested it and it returned 0 when it should return 500. :( http://250kb.de/u/131026/j/xh4b0Wap0bTC.jpg http://pastebin.com/vp2fk8TU – Forivin Oct 26 '13 at 17:08
  • 1
    Check what `GetLastError` returns. You should always check it when an winapi function fails. Are you using Windows 7? Then you might need to adjust your privileges (see the [PriviledgeCheck](http://msdn.microsoft.com/en-us/library/windows/desktop/ms680553%28v=vs.85%29.aspx) function in the comments) – BeniBela Oct 26 '13 at 17:34
  • I'm on Windows 8, GetLastError() returns 299: ERROR_PARTIAL_COPY (Only part of a ReadProcessMemory or WriteProcessMemory request was completed.) I will check the previledge function out. Thanks. – Forivin Oct 26 '13 at 17:41
  • Okay I tested the function PriviledgeCheck(). But I had to replace ErrorExit() with printf() and remove the last printf because it was calling SetPrivilege() which does not exist... However, it always printed "Nope, Try again. Attempting to get it..." – Forivin Oct 26 '13 at 18:48
  • did you run it as administrator? – BeniBela Oct 26 '13 at 18:55
  • 1
    wait, I did not really read your comment. You *removed SetPrivilege*? Then of course it does not set the privilege... There is a [SetPrivilege example](http://msdn.microsoft.com/en-us/library/windows/desktop/aa446619%28v=vs.85%29.aspx) in the msdn – BeniBela Oct 26 '13 at 20:30
  • Right below the includes #pragma comment(lib, "cmcfg32.lib"), where do I get this cmcfg32.lib? It doesn't seem to be a default one. (I'm really sorry for asking so many questions. :/ – Forivin Oct 26 '13 at 21:12
  • you could have googled for [that](http://stackoverflow.com/questions/2523099/where-is-cmcfg32-lib) – BeniBela Oct 26 '13 at 21:44
  • Okay, I just removed the line and it compiles fine. I'm calling SetPrivilege(mainToken, SE_DEBUG_NAME, true) now and it actually returns true. So if I'm correct my program has the required priviledges now. But it still returns 0 when it should return 500.. http://pastebin.com/1m97Cr3Y – Forivin Oct 26 '13 at 22:38
  • 1
    how strange. What does `GetLastError` return directly after ReadProcessMemory? You call SetPrivilege before OpenProcess? – BeniBela Oct 26 '13 at 23:19
  • I still got 299 after readprocessmemory. But nvm, it was a stupid mistake, I passed "address" instead of "actualAddress" to ReadMemoryInt(). Thank you so much for taking so much time on helping me! I really appreciate it! – Forivin Oct 26 '13 at 23:36