0

I'm trying to read data from another program (that i made myself i'll display the code of the programs) with the memoryadress everything works fine I can read int char and string datas but when I try to read the value of a string i get an error showing on visual studio(Yesterday this wasn't happening) I still get the right output but yet I can't rerun the program with a loop : it says "Violating reading access _Pnext has been 0xDE74C"

enter image description here

-The code of the program i'm trying to read datas from:

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

using namespace std;
int main()
{
const int sizeArrChar = 128;
int varInt = 123456;
string varString = "DefaultString";
char arrChar[sizeArrChar] = "Long char array right there ->";
int *ptr2int = &varInt;
int **ptr2ptr = &ptr2int;
int ***ptr2ptr2 = &ptr2ptr;
for (;;)
{
    cout << "Process ID: " << GetCurrentProcessId() << "\n"<< endl;
    cout << "varInt     (0x" << &varInt << ") = " << varInt << endl;
    cout << "varString  (0x" << &varString << ") = " << varString << endl;
    cout << "arrChar    (0x" << &arrChar << ") = " << arrChar << endl;
    cout << "ptr2int    (0x" << &ptr2int << ") = " << ptr2int << endl;
    cout << "ptr2ptr    (0x" << &ptr2ptr << ") = " << ptr2ptr << endl;
    cout << "ptr2ptr2   (0x" << &ptr2ptr2 << ") = " << ptr2ptr2 << "\n" << endl;
    cout << "Press enter to cout again!" << "\n" << "\n" << endl;
    cout << "---------------------------------------------------------" << endl;

    system("pause");
}

system("pause");
return 0;

}

-The code of the program to read the datas of the program above : (This is only to read string not int or char)

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




int main()
{
using namespace std;
int processNumber;
cout << "Enter the process number" << endl;
cin >> processNumber;
for (;;)
{
    uintptr_t memoryAdress = 0x0;
    cout << "Enter memoryadress" << endl;
    cin >> hex >> memoryAdress;
    cout << hex << memoryAdress << endl;

    HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processNumber);
    if (hProcess == NULL) { // Failed to get a handle
        cout << "OpenProcess failed. GetLastError = " << dec << GetLastError() << endl;
        system("pause");
        return EXIT_FAILURE;
    }

    string intRead;
    ReadProcessMemory(hProcess, (LPCVOID)memoryAdress, &intRead, sizeof(string), NULL);
    cout << "intRead = " << intRead << endl;


    BOOL WINAPI CloseHandle(
        _In_ HANDLE hObject
    );
    system("pause");
}
return 0;
}

Everything works i get the right output but i can only read the data once because of the error occuring so I can't read the data several times in a row and that's the main issue.

Here is the output:

Enter the process number 14788 Enter memoryadress 0x009DFC2C 9dfc2c intRead = DefaultString Appuyez sur une touche pour continuer...

enter image description here

Daniel
  • 2,355
  • 9
  • 23
  • 30
Eliendrel
  • 35
  • 1
  • 6
  • Thanks for the advice but I already know that my other program to read int and char is working fine i can use the loop without any issue I don't need to restart the program to read a value in the memory not like when I try to read a string. – Eliendrel Feb 10 '19 at 13:54
  • `string intRead;` then `ReadProcessMemory(hProcess, (LPCVOID)memoryAdress, &intRead, sizeof(string), NULL);` is not legal. You can't do this with a `std::string`. `std::string` is an object not a simple array of characters. Also at this point its empty. – drescherjm Feb 10 '19 at 13:56
  • You can use a vector for this like in the following link (see buffer in find_locs): https://gist.github.com/Mikulas/2551307 – drescherjm Feb 10 '19 at 14:39
  • Thanks you a lot you're a blessing for mankind this is going to be very helpful! I wish you peace from the deepest of my soul. I'll update the thread once i fixed it – Eliendrel Feb 10 '19 at 14:49

1 Answers1

0

std::string is a container, underlying it is a char array but the container manages it.

When you copy the target string from the target process into your local process using ReadProcessMemory you're also copying pointers from the container that are invalid in your local process. They only pointed to valid memory objects in the target process. These invalid pointers are what's causing your crash.

But it's a complicated issue. If you reverse engineer the string class you will find the first member variable is a pointer. Offset 0x14 is the size of the char array and Offset 0x18 is the "max size of current char array" variable.

If the initial string is less than 15 chars, the second variable (offset 0x4/0x8 depending on architecture) in the container is the char array itself. If it is more than 15 chars, the second variable turns into a pointer to a char array. Every time the string is modified, a new char array is allocated on the heap, and the pointer changes to point at the new array.

Your code appears to work fine at first because your string is less than 15 chars.

So how do we attack this issue?

Read the integer at offset 0x14 that gives us the char array size, if it's less than 15, read from offset 0x4, if it's more than 15, de-reference the pointer and then read from the dynamic address of the char array.

I did not test this on std::wstring, this is just a proof of concept to work with your current problem. I tested this on Windows 10, Visual Studio 2017. It may not work in all circumstances.

#include <windows.h>
#include <iostream>

using namespace std;

void ReadExternalString(HANDLE hProc, uintptr_t addr, char* dstArray)
{

    unsigned int arraySize;
    //Get the size of the array, offset 0x14 is the size of the array
    ReadProcessMemory(hProc, (BYTE*)(addr + 0x14), &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);
    }
}

int main()
{

    int processNumber;
    cout << "Enter the process number" << endl;
    cin >> processNumber;
    for (;;)
    {
        uintptr_t memoryAddress = 0x0;
        cout << "Enter memoryAddress" << endl;
        cin >> hex >> memoryAddress;
        cout << hex << memoryAddress << endl;

        HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processNumber);
        if (hProcess == NULL) { // Failed to get a handle
            cout << "OpenProcess failed. GetLastError = " << dec << GetLastError() << endl;
            system("pause");
            return EXIT_FAILURE;
        }

        char* cString = new char[500];

        ReadExternalString(hProcess, memoryAddress, cString);

        cout << "string char array = " << cString << endl;
        system("pause");
    }
    return 0;
}
GuidedHacking
  • 3,628
  • 1
  • 9
  • 59