Is there any method to readprocessmemory into a string and print it in console?

170 views Asked by At

I already did this by a simple method:

#include <iostream>
#include <Windows.h>
#include <string>

using namespace std;

wstring astr2wstr(std::string & string_a){
    int length = MultiByteToWideChar(CP_UTF8, 0, string_a.c_str(), -1, NULL, 0);

    wchar_t* temp = new wchar_t[length];
    MultiByteToWideChar(CP_UTF8, 0, string_a.c_str(), -1, temp, length);

    wstring string_w = temp;
    delete[] temp;
    return string_w;
}

int main() {
    DWORD pid;
    cout<<"Enter the PID: ";
    cin>>dec>>pid;
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    if (hProcess == NULL) {
        cout << "OpenProcess failed. GetLastError = " << dec << GetLastError() << endl;
        system("pause");
        return EXIT_FAILURE;
    }
    uintptr_t Target = 0x7994848;
    string Rec;
    SIZE_T bytesRead = 0;
    ReadProcessMemory(hProcess, (LPCVOID)Target, &Rec, sizeof(Rec), NULL);
    wstring mywstr = astr2wstr(Rec);
    wcout<<mywstr<<endl;
}

After I run it I got nothing in console...

And I want to read that from this code:

#include <iostream>
#include <String>
#include <Windows.h>
#define CHAR_ARRAY_SIZE 128

using namespace std;

int main(){
    int varInt = 123456;
    string varString = "DefaultString";
    const char arrChar[CHAR_ARRAY_SIZE] = "Long char array right there ->";
    int* ptr2int = &varInt;
    int** ptr2ptr = &ptr2int;
    int*** ptr2ptr2 = &ptr2ptr;
    do{
        cout<<"Process ID: "<<dec<<GetCurrentProcessId()<<endl<<endl;
        cout<<"varInt(0x"<<hex<<(uintptr_t)ptr2int<<") = "<<dec<<varInt<<" "<<endl<<endl;
        wcout<<"varString(0x"<<(uintptr_t)&varString<<") = "<<varString<<" "<<endl<<endl;
        cout<<"arrChar(0x"<<(uintptr_t)&arrChar<<") = "<<arrChar<<" "<<endl<<endl;
        cout<<"ptr2int(0x"<<(uintptr_t)&ptr2int<<") = "<<ptr2int<<" "<<endl<<endl;
        cout<<"ptr2ptr(0x"<<(uintptr_t)&ptr2ptr<<") = "<<ptr2ptr<<" "<<endl<<endl;
        cout<<"ptr2ptr(0x"<<(uintptr_t)&ptr2ptr2<<") = "<<ptr2ptr2<<" "<<endl<<endl;
        cout << "Press ENTER to refresh." << endl;
        getchar();
        cout<<"*==========================================================*"<<endl<<endl;
    } while(true);
    return EXIT_SUCCESS;
}

Target is varString.

If there is any method to readprocessmemory into string, guide me and I'll appreciate it! Otherwise would you tell me what is the problem?

To read a string from other application by ReadProcessMemory Function!

1

There are 1 answers

0
Jerry Coffin On

It's possible to do this, but (as suggested in the comments) you need to account for the possibility of short string optimization, and act appropriately. Even at best, it's pretty fragile--you basically need to know at least some of the internals of the string type in question to make it work.

So, to make it work, you start by reading the raw data for the string itself from the target. Then, you figure out whether its using short string optimization. If it is, you have the data in what you just read, and you can stuff it into a string.

Otherwise, you use the string's .data() member to get the address (in the target process) of the actual string data. Then call ReadProcessMemory again to get a copy of that.

Oh, one other detail: if you just try to create a string, and read bytes from the other process into it, when that string goes out of scope and gets destroyed, its dtor will try to free an address that refers to the other process, so we have to prevent that or mayhem will result.

Here's some demo code that works for Microsoft's compiler. I've added another string to demonstrate it working with both SSO and non-SSO strings.

Here's the (somewhat simplified) target program:

// target.cpp
#include <iostream>
#include <string>
#include <Windows.h>

using namespace std;

int main() {

    string varString = "defaultString";
    string varString2 = "Much Too Large of a string to fit into a short string optimized string";

    do {
        cout<<"Process ID: "<<dec<<GetCurrentProcessId()<<"\n\n";
        cout<<"varString: 0x"<<(void *)&varString <<'\n';
        cout << "varString2:  0x" << (void *)&varString2 << "\n";

        getchar();
        cout<<"*==========================================================\n\n";
    } while(true);
}

And the Spy program:

#include <iostream>
#include <Windows.h>
#include <string>
#include <iomanip>

using namespace std;

std::string readString(HANDLE hProcess, uintptr_t Target) {
    // allocate raw memory we can treat like a string when we want, but also
    // delete the memory without trying to destroy the string, since it's not 
    // really valid.
    string *Rec = (std::string *)::operator new(sizeof std::string);

    SIZE_T bytesRead = 0;
    if (!ReadProcessMemory(hProcess, (LPCVOID)Target, Rec, sizeof(*Rec), &bytesRead))
    {
        std::cerr << "Read: " << bytesRead << " bytes\n";
        std::cerr << "ReadProcessMemory failed\n";
        std::cerr << "Code: " << GetLastError() << "\n";
        ::operator delete(Rec);
        return "";
    }

    std::size_t length = Rec->size();

    std::string data;

    // Figure out whether it's using SSO, and act accordingly:
    if (length < 16) {
        data.assign((char const *)Rec, length);
    } else {
        data.resize(length + 1);
        void *loc = (void *)Rec->data();
        ReadProcessMemory(hProcess, loc, data.data(), length + 1, NULL);
    }


    // delete the memory *without* trying to destroy the string,
    // since it's not a valid string in this process.
    ::operator delete(Rec);
    return data;
}

int main() {
    DWORD pid;
    cout<<"Enter the PID: ";
    cin>>dec>>pid;
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    if (hProcess == NULL) {
        cout << "OpenProcess failed. GetLastError = " << dec << GetLastError() << endl;
        system("pause");
        return EXIT_FAILURE;
    }

    // test with SSO string
    uintptr_t Target1 = 0x1AF6F8;

    // and non-SSO string
    uintptr_t Target2 = 0x1AF6D8;

    std::cout << readString(hProcess, Target1) << "\n";
    std::cout << readString(hProcess, Target2) << "\n";
}

Now, go forth and...find a way to do the job some other way than using fragile, ugly, error-prone code like this.