Search code examples
c++winapimemorystdstring

How do i read a std::string from memory using RPM


Okay, so recently i've been working witth RPM(ReadProccesMemory). But while doing so i bumped into the issue of not being able to read a string the way i wanted it.

This is what i heard/know: I know, when reading a std::string, i get the memory adress of the string OBJECT and not the adress that contains the actual text.I am also aware of "Small String Optimization", and what it does in theory.

I'd like to be able to read the contents of varString(DefaultString) without changing the code of my dummy program(if possible).

The dummy program im reading:

int main() {
    // Variables & Pointers
    int varInt = 123456;
    string varString = "DefaultString";
    cout << sizeof(varString);
    char arrChar[128] = { "Long char array right there ->" };
    int* ptr2int = &varInt;
    int** ptr2ptr = &ptr2int;
    int*** ptr2ptr2 = &ptr2ptr;

    // Printing them out
    while (true){
    cout << "Process ID: " << GetCurrentProcessId() << endl << endl;
    cout << "varInt (0x" << &varInt << ") = " << varInt << endl;
    cout << "varString (" << reinterpret_cast<const void*>(varString.data()) << ") = " << varString << endl;
    cout << "arrChar (0x" << &arrChar << ") = " << arrChar << endl << endl;

    cout << "ptr2int (0x" << &ptr2int << ") = " << &varInt << endl;
    cout << "ptr2ptr (0x" << &ptr2ptr << ") = " << &ptr2int << endl;
    cout << "ptr2ptr2 (0x" << &ptr2ptr2 << ") = " << &ptr2ptr << endl << endl;
    break;
    }

    cin.get();
    return 0;
}

What im currently doing(wont work as intended):

void reading_string(HANDLE handle_procces) {
    uintptr_t memoryAdress_2 = 0x0;
    cout << "Please write down the memory adress of \"varString\" > " << flush;
    cin >> hex >> memoryAdress_2;

    string read_string_object;
    ReadProcessMemory(handle_procces, (LPCVOID)memoryAdress_2, &read_string_object, sizeof(string), NULL);
    cout << "The value of this memory adress is: " << read_string_object << endl;
}


Solution

  • std::string is a container, offset 0x14 is the size of the char array which it manages. If the string is less than 15 characters, the second variable (offset 0x4 or 0x8 depending on x86/x64) is the char array itself. If it's more than 15 characters, this variable turns into a pointer to the char array which is allocated dynamically

    We can use this information to read the string externally, it's a hack but it works

    Here some sample code which shows you how it's done:

    #include <windows.h>
    #include <iostream>
    
    using namespace std;
    
    void ReadExternalString(HANDLE hProc, uintptr_t addr, char* dstArray)
    {
        //Get the size of the array, offset 0x14 is the size of the array
    
        //it's 0x14 on x86
        uintptr_t sizeoffset = 0x14;
    
        //check if x64
        if (sizeof(int*) == 8)
        {
            //assign correct offset
            sizeoffset = 0x18;
        }
    
        uintptr_t arraySize;
    
        ReadProcessMemory(hProc, (BYTE*)(addr + sizeoffset), &arraySize, sizeof(arraySize), 0);
    
        if (arraySize > 15)
        {
            uintptr_t addrOfCharArray;
            //dereference the pointer in the second member variable to get the dynamic address of the array
            ReadProcessMemory(hProc, (BYTE*)(addr + sizeof(void*)), &addrOfCharArray, sizeof(void*), 0);
    
            char buffer[500];
            //Read the array into buffer, +1 to get the null terminator
            ReadProcessMemory(hProc, (BYTE*)(addrOfCharArray), &buffer, arraySize + 1, 0);
    
            //copy the buffer into our ouput argument
            memcpy(dstArray, &buffer, strlen(buffer) + 1);
        }
        else
        {
            ReadProcessMemory(hProc, (BYTE*)(addr + sizeof(void*)), dstArray, arraySize, 0);
        }
    }
    
    std::string ourStringToRead = "Yolo";
    
    int main()
    {
        HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, GetCurrentProcessId());
    
        if (hProcess == NULL)
        {
            cout << "OpenProcess failed. GetLastError = " << dec << GetLastError() << endl;
            system("pause");
            return EXIT_FAILURE;
        }
    
        char* cString = new char[500];
        ZeroMemory(cString, 500);
    
        ReadExternalString(hProcess, (uintptr_t)&ourStringToRead, cString);
    
        cout << "string char array = " << cString << endl;
        system("pause");
    
        return 0;
    }