2

I have been playing around with memory reading/editing recently and have run into a problem which I think is due to the 64bit application, I have also tried to compile under 64bit. I had no problem with this script using with 32bit apps, however when I try this on Solitaire it fails to get the base address, which then fails to workout the correct offsets ect. Here is the script:

#include "stdafx.h"
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
using namespace std;

DWORD dwGetModuleBaseAddress(DWORD dwProcessID, TCHAR *lpszModuleName)
{
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessID);
    DWORD dwModuleBaseAddress = 0;
    if (hSnapshot != INVALID_HANDLE_VALUE)
    {
        MODULEENTRY32 ModuleEntry32 = { 0 };
        ModuleEntry32.dwSize = sizeof(MODULEENTRY32);
        if (Module32First(hSnapshot, &ModuleEntry32))
        {
            do
            {
                if (_tcscmp(ModuleEntry32.szModule, lpszModuleName) == 0)
                {
                    dwModuleBaseAddress = (DWORD)ModuleEntry32.modBaseAddr;
                    break;
                }
            } while (Module32Next(hSnapshot, &ModuleEntry32));
        }
        CloseHandle(hSnapshot);
    }
    return dwModuleBaseAddress;
}

int main()
{
    DWORD address = 0xBAFA8;
    HWND hwnd = FindWindow(0, L"Solitaire");
    DWORD pid;
    int data = 0;
    int newData = 0;
    if (hwnd)
    {
        GetWindowThreadProcessId(hwnd, &pid);
        HANDLE phandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
        if (phandle)
        {
            DWORD base = dwGetModuleBaseAddress(pid, L"Solitaire.exe");
            cout << "Base: " << (void*)base << endl;

            ReadProcessMemory(phandle, (LPCVOID)(base + address), &data, sizeof(data), 0);
        }
        else {
            cout << "Couldnt get handle" << endl;
        }

    }
    else {
        cout << "Couldn't find window" << endl;
    }
    cin.get();
    return 0;
}

The problem may be that the function I use uses MODULE32, however I have tried other functions (that uses EnumModules) which still fails to return address.

Any ideas how to get base address of 64bit application or to get this script working?

Thanks

enhzflep
  • 12,927
  • 2
  • 32
  • 51
user3366103
  • 51
  • 1
  • 1
  • 8
  • This may be of use (note the discussion that references the bitness of the target program and the seeking program) http://stackoverflow.com/questions/17412545/find-module-handle-in-windows-x64-from-external-process – enhzflep Oct 26 '14 at 11:48
  • 1
    `modBaseAddr` is a pointer. Stop casting it to 32 bit integer. You understand that pointers are 64 bits wide on 64 bit Windows right? You'll not be able to see into 64 bit processes from a 32 bit process running under the 32 bit WOW64 emulator. So running as 64 bit is essential. – David Heffernan Oct 26 '14 at 11:51

3 Answers3

6

Well your code is never going to work successfully because you are talking about 64 bits but you are using DWORD for base address! Solitare might have a 32 bit address but you cannot guarantee that and you should never assume it.

This function works. It requires only the process ID of the process in question, and it assume you want the base address of that process. i.e. not one of its DLLs. If you don't want the owning process then you need to iterate through moduleArray using something like for (int i=0; i<moduleCount; i++ ) { // do something with moduleArray[i] } and then check the module file name.

If you only want the starting process (the executable) you can just make an assumption that it's the first element in the array.

DWORD_PTR GetProcessBaseAddress( DWORD processID )
{
    DWORD_PTR   baseAddress = 0;
    HANDLE      processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID);
    HMODULE     *moduleArray;
    LPBYTE      moduleArrayBytes;
    DWORD       bytesRequired;

    if ( processHandle )
    {
        if ( EnumProcessModules( processHandle, NULL, 0, &bytesRequired ) )
        {
            if ( bytesRequired )
            {
                moduleArrayBytes = (LPBYTE)LocalAlloc( LPTR, bytesRequired );

                if ( moduleArrayBytes )
                {
                    unsigned int moduleCount;

                    moduleCount = bytesRequired / sizeof( HMODULE );
                    moduleArray = (HMODULE *)moduleArrayBytes;

                    if ( EnumProcessModules( processHandle, moduleArray, bytesRequired, &bytesRequired ) )
                    {
                        baseAddress = (DWORD_PTR)moduleArray[0];
                    }

                    LocalFree( moduleArrayBytes );
                }
            }
        }

        CloseHandle( processHandle );
    }

    return baseAddress;
}
Nostromoo
  • 284
  • 1
  • 5
  • 1
    thanks for that working script, i asume this will only work on 64bit and there is no universal solution? – user3366103 Oct 26 '14 at 13:11
  • 1
    I did compile the code as 64 bit. I don't really know what you want to do with the image once you have it. You probably can compile it 32 bit and work on it but you will have to explicitly use 64 bit values for any pointer types like the base address and any offsets. But a 64 bit program could more easily access either 32 bit or 64 bit images. – Nostromoo Oct 26 '14 at 19:37
2

As hinted by David this line is wrong:

dwModuleBaseAddress = (DWORD)ModuleEntry32.modBaseAddr;

since you're casting a 64-bit pointer (on a 64-bit application) to a DWORD truncating the value (screenshot from the debugger, 64 bit app opening a 64 bit process)

enter image description here

You should go for a pointer type (DWORD_PTR could also work for a portable solution).

Marco A.
  • 43,032
  • 26
  • 132
  • 246
  • 2
    Why `ULONGLONG`? Store a pointer in a pointer data type. `unsigned char*` seems pretty hard to beat. – David Heffernan Oct 26 '14 at 12:37
  • 1
    @DavidHeffernan That's the 64 type for the image base. Anyway DWORD_PTR is probably more portable. – Marco A. Oct 26 '14 at 12:43
  • 1
    So I can use the original function by just changing it to cast DWORD_PTR? – user3366103 Oct 26 '14 at 13:10
  • 1
    That will fix this specific truncation problem, yes. DWORD_PTR is defined as 32 bit pointer on 32 bit code and 64 bit pointer on 64 bit code so it should also be portable. I believe other problems will prevent you from opening 64 bit processes from 32 bit apps, but aside from that you should be good. – Marco A. Oct 26 '14 at 13:11
  • 1
    I did change it to cast DWORD_PTR and changed 'DWORD dwModuleBaseAddress = 0;' to DWORD_PTR and still cant get original function to work any idea? – user3366103 Oct 26 '14 at 13:18
-1

Sample console-application based on the examples given by @Nostromoo

Should work on x86/x64

#include "stdafx.h"
#include <windows.h>
#include <psapi.h>
#include <vector>

#define MODULE_NAME L"TestModule"
#define WINDOW_NAME L"TestConsole"

bool GetBaseModuleInfo(MODULEINFO* baseModuleInfo);
HMODULE GetProcessBaseAddressFromProcessHandle(HANDLE processHandle);
bool EnumProcessModulesPlattform(HANDLE processHandle, HMODULE* lphModule, DWORD cb, LPDWORD bytesRequired);
DWORD GetProcessIdentifier(const wchar_t* windowName);

int main()
{
    SetConsoleTitle(WINDOW_NAME);

    // Wait for the title to be set otherwise the window might not be found.
    Sleep(200);

    MODULEINFO baseModuleInfo;
    bool success = GetBaseModuleInfo(&baseModuleInfo);

    if (success == false) {
        wprintf(MODULE_NAME L"main() - GetBaseModuleInfo() failed.\n");
        std::getchar();
        return 1;
    }

        wchar_t buffer[2000];
#ifdef _WIN64
        swprintf_s(buffer, sizeof(buffer) / sizeof(*buffer), MODULE_NAME L"main() - baseAddress: '%llX' size: '%lX'\n", baseModuleInfo.lpBaseOfDll, baseModuleInfo.SizeOfImage);
#elif _WIN32
        swprintf_s(buffer, sizeof(buffer) / sizeof(*buffer), MODULE_NAME L"main() - baseAddress: '%lX' size: '%lX'\n", baseModuleInfo.lpBaseOfDll, baseModuleInfo.SizeOfImage);
#endif
        wprintf(buffer);

    std::getchar();
    return 0;
}

bool GetBaseModuleInfo(MODULEINFO* baseModuleInfo)
{
    DWORD processID = GetProcessIdentifier(WINDOW_NAME);
    if (processID == NULL) {
        wprintf(MODULE_NAME L"GetBaseModuleInfo() - GetProcessIdentifier() returned NULL.\n");
        return false;
    }

    HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID);
    if (processHandle == NULL) {
        wprintf(MODULE_NAME L"GetBaseModuleInfo() - OpenProcess() returned NULL.\n");
        return false;
    }

    HMODULE baseAddress = GetProcessBaseAddressFromProcessHandle(processHandle);
    if (baseAddress == NULL) {
        wprintf(MODULE_NAME L"GetBaseModuleInfo() - GetProcessBaseAddressFromProcessHandle() returned NULL.\n");
        return false;
    }

    bool resultGMI = GetModuleInformation(processHandle, baseAddress, baseModuleInfo, sizeof(*baseModuleInfo));
    // NOTE: GetModuleInformation doesn't fail even if the baseAddress is wrong. Maybe it uses the nearest module?
    if (resultGMI == false) {
        wprintf(MODULE_NAME L"GetBaseModuleInfo() - GetModuleInformation() failed.\n");
        return false;
    }

    CloseHandle(processHandle);

    return true;
}

HMODULE GetProcessBaseAddressFromProcessHandle(HANDLE processHandle)
{
    DWORD bytesRequired;
    if (EnumProcessModulesPlattform(processHandle, NULL, 0, &bytesRequired) == false) {
        wprintf(MODULE_NAME L"GetProcessBaseAddressFromProcessHandle() - EnumProcessModules() error.\n");
        return NULL;
    }

    if (bytesRequired == NULL) {
        wprintf(MODULE_NAME L"GetProcessBaseAddressFromProcessHandle() - EnumProcessModules() returned 0 bytesRequired.\n");
        return NULL;
    }

    unsigned int moduleCount = bytesRequired / sizeof(HMODULE);

    std::vector<HMODULE> hModules(moduleCount, 0);
    if (EnumProcessModulesPlattform(processHandle, &hModules[0], bytesRequired, &bytesRequired) == false) {
        wprintf(MODULE_NAME L"GetProcessBaseAddressFromProcessHandle() - EnumProcessModules(moduleArray) error.\n");
        return NULL;
    }

    // The first item is always the baseModule.
    HMODULE baseAddress = hModules[0];
    return baseAddress;
}

bool EnumProcessModulesPlattform(HANDLE processHandle, HMODULE* lphModule, DWORD cb, LPDWORD bytesRequired)
{
#ifdef _WIN64
    bool returnValue = EnumProcessModulesEx(processHandle, lphModule, cb, bytesRequired, LIST_MODULES_64BIT);
#elif _WIN32
    bool returnValue = EnumProcessModulesEx(processHandle, lphModule, cb, bytesRequired, LIST_MODULES_32BIT);
#endif

    DWORD lastError = GetLastError();
    if (lastError != 0) {
        wprintf(MODULE_NAME L"EnumProcessModulesPlattform() - lastError != 0 EnumProcessModulesEx().\n");
        return false;
    }

    return returnValue;
}

DWORD GetProcessIdentifier(const wchar_t* windowName)
{
    HWND windowHandle = FindWindow(NULL, windowName);
    if (windowHandle == NULL) {
        wprintf(MODULE_NAME L"GetProcessIdentifier() - Could not find hwnd of '%s'.\n", windowName);
        return NULL;
    }

    DWORD processID;
    GetWindowThreadProcessId(windowHandle, &processID);
    return processID;
}
Sebastian Ax
  • 1,240
  • 12
  • 11
  • 1
    how think - what will be value of `sizeof(baseModuleInfo.lpBaseOfDll)` ? based on what ? are it not defined already at compile time ? – RbMm Jul 22 '18 at 14:07
  • 1
    @RbMm If it is compiled on x86 it is 4 byte and if it is compiled on x64 i is 8 byte. It is defined at compile time... and it could be do with defines but that part is just to show how the function is used anyway. – Sebastian Ax Jul 22 '18 at 14:18
  • 1
    yes. so this at first must be selected not in runtime but in compile time by `#if` block. at second if you compile as 32bit code - you not get 64bit module base address. at third your solution not efficient and very not the best – RbMm Jul 22 '18 at 14:20
  • 1
    I don't understand what you mean with 'at second if you compile as 32bit code - you not get 64bit module base address'. Well efficient: I dont expect this to run very often in a program so its efficient enough. – Sebastian Ax Jul 22 '18 at 14:27
  • 1
    i mean that 32bit code can not got base address of 64bit module. `EnumProcessModules` fail in this case – RbMm Jul 22 '18 at 14:29
  • 1
    HMODULE changes it size depending on if it is compiled on x86 or x64 so i dont see why it should fail. And also as far as i can tell it runs fine when i compile it with x86 or x64 setting. – Sebastian Ax Jul 22 '18 at 14:37
  • 1
    Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/176515/discussion-between-sebastian-ax-and-rbmm). – Sebastian Ax Jul 22 '18 at 14:38