Во всех примерах я видел третий параметр или buffer
является целым числом, но я не совсем уверен, что это значение должно представлять или как это полезно в любом случае, поэтому я попытался char
массив и получил случайный мусор, так что мне любопытно, для чего это значение можно использовать и как лучше всего его поместить в контейнер, и зависит ли тип данных от значения, о котором мы спрашиваем.
Посмотрев на мой код, я понял, что допустил действительно глупую ошибку, не инициализируя дескриптор процесса при его объявлении. Вот мой код, хотя и теперь я получаю ERROR_PARTIAL_COPY
, А также dwRead
0. ВТФ
#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;
}
BOOL WINAPI ReadProcessMemory( HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead );
Параметр объявлен как LPVOID
(а typedef
за void*
). Любой указатель может неявно преобразовываться в указатель на пустоту — это означает, что буфер, который вы предоставляете, может фактически быть любым типом, который вам нравится. Единственным ограничением является то, что он должен быть достаточно большим, чтобы держать nSize
байты данных.
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
}
Если вы получаете мусор в возвращаемом буфере, возможно, это связано с тем, что чтение не удалось или вы не прочитали столько данных, сколько вы запрашивали, и вы не проверили возвращаемое значение должным образом. Если функция завершается успешно, lpNumberOfBytesRead
Параметр позволяет узнать, сколько байтов было прочитано успешно.
Если функция возвращает 0
это означает, что он вообще потерпел неудачу — в этом случае содержимое предоставленного вами буфера не определено и не должно использоваться.
Вы должны использовать типы данных, которые функция хочет, чтобы вы использовали.
https://msdn.microsoft.com/en-us/library/windows/desktop/ms680553(v=vs.85).aspx
Что касается третьего параметра, вы можете использовать указатель на любые данные, которые вам нравятся, при условии, что вы соответствующим образом настраиваете четвертый параметр.
например передавая указатель на int в качестве третьего и sizeof(int)
в качестве четвертого параметра может быть совершенно нормально.
Я бы порекомендовал инициализировать данные, которые вы передаете, с нуля (по крайней мере, по причинам отладки) и проверить, какие все данные возвращаются функцией (особенно значение переменной, указатель которой вы передали в качестве 5-й переменной).
Какой тип данных действительно подходит для вашего случая использования, зависит от вас.
Если вы используете char
-array это будет способ сделать это:
#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;
}
}
Поскольку «Дэвид Хеффернан» указал, неясно, что я имел в виду при инициализации буфера нулями, я попытаюсь объяснить:
Это подразумевалось как «подсказка» для отладки, так как я думаю, что легче увидеть, какие данные изменились в отладчике, чем когда вы унифицировали данные.
Это определенно не нужно.
Скажем ваш ReadProcessMemory
Вызов возвращается успешно, и вы хотите проверить данные в буфере.
Вам нужно сначала проверить, сколько байтов было записано в буфер, а затем посмотреть на первый. bytesWritten
байты вашего массива.
Если данные, которые вы передали в качестве буфера, не были инициализированы с 0
может быть труднее найти разрыв между «реальными данными» и просто данными, которые были унифицированы — таким образом это может привести к неправильной интерпретации. Особенно за большие ошибки.
Это был всего лишь инструмент для использования в процессе разработки, чтобы упростить проверку данных и должны быть удалены, как только все работает, как ожидалось.