Reputation: 23
In all the examples I've seen the third parameter or the buffer
is an integer but I'm not quite sure wtf this value is supposed to represent or how it's useful in anyway so I tried a char
array and got random garbage so I'm curious as to what this value can be used for and the best container to put it in and if the data type is dependent on the value we're inquiring about.
After looking at my code I realized I made a really stupid mistake by not initializing the process handle when declaring it. Here is my code though and now I'm getting ERROR_PARTIAL_COPY
. And dwRead
is 0. wtf
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
system("tasklist");
SYSTEM_INFO SysInfo;
GetSystemInfo(&SysInfo);
DWORD proc_Id = 0;
cin >> proc_Id;
HANDLE hProc = INVALID_HANDLE_VALUE;
char value[500];
SIZE_T dwRead;
hProc = OpenProcess(PROCESS_ALL_ACCESS, TRUE, proc_Id);
if(hProc == NULL)
{
cout << "Error when trying to retrieve process handle" << endl;
}
void *baseAddr = (void*)hProc;
if(VirtualAllocEx(hProc, NULL, SysInfo.dwPageSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READ) == NULL)
{
cout << "VirtualAllocEx error" << endl;
}
if(ReadProcessMemory(hProc, baseAddr, &value, sizeof(value), &dwRead) == 0)
{
cout << "ReadProcessMemory failed: " << GetLastError() << endl;
}
cout << "Value is: " << value << endl;
cout << "Amount read successfully: " << dwRead << endl;
}
Upvotes: 0
Views: 2196
Reputation: 5680
You should use the data types the function wants you to use.
https://msdn.microsoft.com/en-us/library/windows/desktop/ms680553(v=vs.85).aspx
As for the third parameter you can use a pointer to any kind of data you like as long as you adjust the fourth parameter accordingly.
e.g. passing a pointer to an int as third and sizeof(int)
as fourth parameter might be perfectly fine.
I would recommend to initialize the data your are passing with zero (at least for debugging reasons) and check what all data returned by the function (especially the value of the variable which's pointer you passed as 5th variable.)
Which datatype really suits your use case is up to you.
In case you use a char
-array this would be a way to do it:
#include <iostream>
#include <Windows.h>
int main()
{
HANDLE hProcess = NULL; //to be initialized
LPCVOID lpBaseAddress = NULL; //to be initialized
char buffer[256];
SIZE_T bufferSize = sizeof(buffer);
SIZE_T bytesRead = 0;
if (::ReadProcessMemory(hProcess, lpBaseAddress, buffer, bufferSize, &bytesRead)) // read 256 chars
{
std::cout << "ReadProcessMemory succeeded by reading " << bytesRead << " bytes";
//Do something with the buffer....
}
else
{
char messageBuffer[256];
DWORD messageBufferSize = sizeof(messageBuffer);
DWORD lastError = ::GetLastError();
DWORD realMessageSize = ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
lastError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
messageBuffer,
messageBufferSize,
NULL);
//If FormatMessageA fails it returns 0. So we make sure that the string is empty
messageBuffer[realMessageSize] = 0;
std::cerr << "ReadProcessMemory failed with " << lastError << ": " << messageBuffer;
}
}
Because "David Heffernan" pointed out it is unclear what I meant with initializing the buffer with zeros I'll try to explain:
This was meant as a "hint" for debugging as I think it is easier to see what data changed in the debugger than when you have unitialized data. It is definitely not needed.
Let's say your ReadProcessMemory
call comes back successfully and you want to check the data in the buffer.
You'll need to check how many bytes were written to the buffer first and then look at the first bytesWritten
bytes of your array.
If the data you passed as buffer wasn't initialized with 0
it may be harder to find the break between "real data" and just data that was unitialized - thus it may lead to misinterpretation. Especially for large errors.
This was only meant as some kind of tool to use during the development process to have it easier to check the data and should be removed once everything works as expected.
Upvotes: 0
Reputation: 37132
BOOL WINAPI ReadProcessMemory( HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead );
The parameter is declared as LPVOID
(a typedef
for void*
). Any pointer can implicitly convert to a pointer-to-void - meaning that the buffer you provide can actually be any type you like. The only restriction is that it has to be large enough to hold nSize
bytes of data.
SIZE_T dwRead;
int iValue;
if (ReadProcessMemory(hProcess, lpAddress, &iValue, sizeof(iValue), &dwRead)) // read an int
{
if (dwRead == sizeof(iValue))
{
// got int successfully
}
}
char buf[256];
if (ReadProcessMemory(hProcess, lpAddress, buf, sizeof(buf), &dwRead)) // read 256 chars
{
// got dwRead bytes successfully
}
If you're getting garbage in the returned buffer it's probably because the read failed, or didn't read as much data as you requested, and you didn't check the return value properly. If the function succeeds the lpNumberOfBytesRead
parameter lets you find out how many bytes were read successfully.
If the function returns 0
it means it failed altogether - in this case, the contents of the buffer you provided are undefined and should not be used.
Upvotes: 1