一、说明
本章来介绍一下内存的操作,这个程序参数修改器的其中的一个原理就是读写内存的值,
分四步:
- 从选择的进程的内存中读取我们需要的值
- 通过官方软件(例如扫雷程序)改变其值
- 再次从选择的进程的内存中取修改过的值
- 直到没有无法缩小范围,调用api修改内存
关键函数两个:
ReadProcessMemory 读取内存
WriteProcessMemory 写入内存(需要关闭360等安全软件)
二、新建Search类
该类主要有两个功能:
- 搜索 (分为首次搜索和再次搜索,其中再次搜索是在首次搜索的地址中进行二次搜索,从而缩小范围)
- 读写 (分为读和写两部分,分别调用小节一中提到的两个内存函数)
除了上述两个功能外,因为搜索是一个过程,时间可能很长,因此使用进度条来加以提示,其方式为使用回调函数
三、搜索
3.1 首次搜索
template<typename T>
bool FindFirstEx(DWORD dwProcessId, DWORD dwBegin, DWORD dwEnd, T value){
m_arList.clear();
// 1. 打开进程
if (!OpenProcess(dwProcessId)) {
return false;
}
// 2. 获取目标值长度
const size_t len = sizeof(value);
const void* pValue = &value;
MEMORY_BASIC_INFORMATION mbi;
DWORD dwBaseAddress = dwBegin;
// 3. 循环遍历内存
do
{
// 3.1 如果内存查找失败,跳过继续
if (0 == VirtualQueryEx(m_hProcess, (LPVOID)dwBaseAddress, &mbi, sizeof(mbi))) {
dwBaseAddress += m_dwPageSize;
continue;
}
dwBaseAddress = (DWORD)mbi.BaseAddress + mbi.RegionSize;
// 3.2 回调函数,仅用来作为进度条与搜索结束的标志
if (!m_pGoonFirst || !m_pGoonFirst(m_pArgsFirst, dwEnd - dwBegin, dwBaseAddress - dwBegin)) {
return false;
}
// 3.3 判数内存属性
if (mbi.State != MEM_COMMIT || (mbi.Protect != PAGE_READWRITE &&
mbi.Protect != PAGE_READONLY &&
mbi.Protect != PAGE_EXECUTE_READ &&
mbi.Protect != PAGE_EXECUTE_READWRITE)) {
//跳过未分配或不可读的区域
continue;
}
// 3.4 读取内容
DWORD dwReadSize;
char* Buf = new char[mbi.RegionSize];
if (ReadProcessMemory(m_hProcess, (LPVOID)mbi.BaseAddress, Buf, mbi.RegionSize, &dwReadSize) == 0) {
delete[] Buf;
CloseHandle(m_hProcess);
return false;
}
DWORD dwBaseAddr = (DWORD)mbi.BaseAddress;
for (size_t i = 0; i < mbi.RegionSize - len; ++i) {
void* p = &Buf[i];
if (memcmp(p, pValue, len) == 0) {
m_arList.push_back(dwBaseAddr + i);
}
}
// 3.5 删除内存区域
delete[] Buf;
} while (dwBaseAddress < dwEnd);
return true;
}
3.2 再次搜索
template<typename T>
bool Search::FindNext(T value)
{
// 1. 目标值的长度
const size_t len = sizeof(value);
const void* pValue = &value;
// 2. 遍历上次查找到的地址
list<DWORD> dwTemp;
size_t index = 0;
for (auto addr : m_arList) {
// 2.1 回调函数判断是否OK
if (m_pGoonNext && !m_pGoonNext(m_pArgsNext, m_arList.size(), index++)) {
return false;
}
WaitForIdle();
// 2.2 读取内存
T readValue;
if (!ReadProcessMemory(m_hProcess, (LPCVOID)addr, &readValue, len, NULL)) {
continue;
}
// 2.3 比较目标值
if (0 == memcmp(pValue, &readValue, len)) {
// 值相等, 保留
dwTemp.push_back(addr);
}
}
// 3. 保存本次结果
m_arList = dwTemp;
return !m_arList.empty();
}
四、读写内存
4.1 读取内存
template<typename T>
bool Search::Read(DWORD dwAddr, T& val)
{
if (ReadProcessMemory(m_hProcess, (LPCVOID)dwAddr, &val, sizeof(val), NULL)) {
return true;
}
return false;
}
4.2 写入内存
注:
- 不是所有的内存都可以成功写入
- 需要关闭360等安全软件
template<typename T>
bool Search::Write(DWORD dwAddr, T val)
{
if (WriteProcessMemory(m_hProcess, (LPVOID)dwAddr, &val, sizeof(val), nullptr)) {
AfxMessageBox(_T("写入数据成功!"));
return true;
}
DWORD ret = GetLastError();
AfxMessageBox(_T("写入数据失败!"));
return false;
}
五、search完整代码
5.1 头文件
.h文件,注意:模板函数不能写到源文件中
#pragma once
#include <windows.h>
#include <list>
#include <algorithm>
using namespace std;
class Search
{
public:
Search();
~Search();
public:
template<typename T>
bool FindFirstEx(DWORD dwProcessId, DWORD dwBegin, DWORD dwEnd, T value);
template<typename T>
bool FindNextEx(T value);
template<typename T>
bool Read(DWORD dwAddr, T& val);
template<typename T>
bool Write(DWORD dwAddr, T val);
bool RemoteCall(unsigned char code[], size_t len, unsigned char para[], size_t paraLen);
virtual void SetCallbackFirst(bool(__stdcall* pGoonFirst)(void* pArgs, size_t nAddrCount, size_t index), void* pArgs);
virtual void SetCallbackNext(bool(__stdcall* pGoonNext)(void* pArgs, size_t nAddrCount, size_t index), void* pArgs);
virtual const list<DWORD>& GetResults() const;
private:
HANDLE m_hProcess{ INVALID_HANDLE_VALUE }; // 初始化目标进程句柄
list<DWORD> m_arList; // 搜索的结果
DWORD m_dwPageSize; // 一页内存大小
void* m_pArgsFirst{ nullptr }; // 首次调用回调函数扫描的指针
void* m_pArgsNext{ nullptr }; // 再次调用回调函数扫描的指针
private:
/**
* 打开进程句柄相关
*/
bool OpenProcess(DWORD dwProcessId); // 打开进程
bool IsValidHandle(); // 进程句柄是否合法有效
bool SafeCloseHandle(); // 安全关闭句柄
/**
* 搜索比较相关
*/
template<typename T>
bool FindFirst(DWORD dwProcessId, DWORD dwBegin, DWORD dwEnd, T value); // 首次查找
template<typename T>
bool FindNext(T value); // 再次查找
void WaitForIdle(); // 信息处理
/**
* 回调相关
*/
typedef bool(__stdcall* PFUN_CALLBACK)(void* pArgs, size_t nPageCount, size_t index); // 回调函数
PFUN_CALLBACK m_pGoonFirst{ nullptr }; // 首次扫描 回调函数
PFUN_CALLBACK m_pGoonNext{ nullptr }; // 下次扫描 回调函数
};
template<typename T>
bool Search::FindFirstEx(DWORD dwProcessId, DWORD dwBegin, DWORD dwEnd, T value)
{
m_arList.clear();
return FindFirst(dwProcessId, dwBegin, dwEnd, value);
}
template<typename T>
bool Search::FindNextEx(T value)
{
return FindNext(value);
}
template<typename T>
bool Search::Read(DWORD dwAddr, T& val)
{
if (ReadProcessMemory(m_hProcess, (LPCVOID)dwAddr, &val, sizeof(val), NULL)) {
return true;
}
return false;
}
template<typename T>
bool Search::Write(DWORD dwAddr, T val)
{
if (WriteProcessMemory(m_hProcess, (LPVOID)dwAddr, &val, sizeof(val), nullptr)) {
AfxMessageBox(_T("写入数据成功!"));
return true;
}
DWORD ret = GetLastError();
AfxMessageBox(_T("写入数据失败!"));
return false;
}
template<typename T>
bool Search::FindFirst(DWORD dwProcessId, DWORD dwBegin, DWORD dwEnd, T value)
{
// 1. 打开进程
if (!OpenProcess(dwProcessId)) {
return false;
}
// 2. 获取目标值长度
const size_t len = sizeof(value);
const void* pValue = &value;
MEMORY_BASIC_INFORMATION mbi;
DWORD dwBaseAddress = dwBegin;
// 3. 循环遍历内存
do
{
// 3.1 如果内存查找失败,跳过继续
if (0 == VirtualQueryEx(m_hProcess, (LPVOID)dwBaseAddress, &mbi, sizeof(mbi))) {
dwBaseAddress += m_dwPageSize;
continue;
}
dwBaseAddress = (DWORD)mbi.BaseAddress + mbi.RegionSize;
// 3.2 回调函数
if (!m_pGoonFirst || !m_pGoonFirst(m_pArgsFirst, dwEnd - dwBegin, dwBaseAddress - dwBegin)) {
return false;
}
// 3.3 判数内存属性
if (mbi.State != MEM_COMMIT || (mbi.Protect != PAGE_READWRITE &&
mbi.Protect != PAGE_READONLY &&
mbi.Protect != PAGE_EXECUTE_READ &&
mbi.Protect != PAGE_EXECUTE_READWRITE)) {
//跳过未分配或不可读的区域
continue;
}
// 3.4 读取内容
DWORD dwReadSize;
char* Buf = new char[mbi.RegionSize];
if (ReadProcessMemory(m_hProcess, (LPVOID)mbi.BaseAddress, Buf, mbi.RegionSize, &dwReadSize) == 0) {
delete[] Buf;
CloseHandle(m_hProcess);
return false;
}
DWORD dwBaseAddr = (DWORD)mbi.BaseAddress;
for (size_t i = 0; i < mbi.RegionSize - len; ++i) {
void* p = &Buf[i];
if (memcmp(p, pValue, len) == 0) {
m_arList.push_back(dwBaseAddr + i);
}
}
// 3.5 删除内存区域
delete[] Buf;
} while (dwBaseAddress < dwEnd);
return true;
}
template<typename T>
bool Search::FindNext(T value)
{
// 1. 目标值的长度
const size_t len = sizeof(value);
const void* pValue = &value;
// 2. 遍历上次查找到的地址
list<DWORD> dwTemp;
size_t index = 0;
for (auto addr : m_arList) {
// 2.1 回调函数判断是否OK
if (m_pGoonNext && !m_pGoonNext(m_pArgsNext, m_arList.size(), index++)) {
return false;
}
WaitForIdle();
// 2.2 读取内存
T readValue;
if (!ReadProcessMemory(m_hProcess, (LPCVOID)addr, &readValue, len, NULL)) {
continue;
}
// 2.3 比较目标值
if (0 == memcmp(pValue, &readValue, len)) {
// 值相等, 保留
dwTemp.push_back(addr);
}
}
// 3. 保存本次结果
m_arList = dwTemp;
return !m_arList.empty();
}
5.2 源文件
.cpp文件
#include "pch.h"
#include "Search.h"
Search::Search()
{
// 获得一页内存的大小
SYSTEM_INFO info;
GetSystemInfo(&info);
m_dwPageSize = info.dwPageSize;
}
Search::~Search()
{
// 关闭进程句柄
SafeCloseHandle();
}
bool Search::RemoteCall(unsigned char code[], size_t len, unsigned char para[], size_t paraLen)
{
if (!IsValidHandle()) {
return false;
}
// 申请代码内存
PVOID mFuncAddr = ::VirtualAllocEx(m_hProcess, NULL, len, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (nullptr == mFuncAddr) {
return false;
}
// 申请函数参数内存
PVOID ParamAddr = ::VirtualAllocEx(m_hProcess, NULL, len, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (nullptr == ParamAddr) {
return false;
}
// 写入代码,和参数
//this->Write((DWORD)mFuncAddr, code, len);
//this->Write((DWORD)ParamAddr, para, paraLen);
// 创建远程线程
DWORD dwThreadId;
HANDLE hThread = ::CreateRemoteThread(m_hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)mFuncAddr, ParamAddr, 0, &dwThreadId);
// 等待执行完毕
if (hThread && hThread != INVALID_HANDLE_VALUE) {
WaitForSingleObject(hThread, 1000);
}
// 释放内存
VirtualFreeEx(m_hProcess, mFuncAddr, len, MEM_RELEASE);
VirtualFreeEx(m_hProcess, ParamAddr, paraLen, MEM_RELEASE);
return true;
}
void Search::SetCallbackFirst(bool(__stdcall* pGoonFirst)(void* pArgs, size_t nAddrCount, size_t index), void* pArgs)
{
m_pGoonFirst = pGoonFirst;
m_pArgsFirst = pArgs;
}
void Search::SetCallbackNext(bool(__stdcall* pGoonNext)(void* pArgs, size_t nAddrCount, size_t index), void* pArgs)
{
m_pGoonNext = pGoonNext;
m_pArgsNext = pArgs;
}
const std::list<DWORD>& Search::GetResults() const
{
return m_arList;
}
bool Search::OpenProcess(DWORD dwProcessId)
{
// 1. 判断进程是否已经打开
if (IsValidHandle()) {
SafeCloseHandle();
}
// 2. 打开进程
m_hProcess = ::OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE |
PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD |
PROCESS_QUERY_INFORMATION,
FALSE, dwProcessId);
// 3. 检查是否成功打开
if (IsValidHandle()) {
return true;
}
else {
SafeCloseHandle();
return false;
}
}
bool Search::IsValidHandle()
{
return (m_hProcess && m_hProcess!=INVALID_HANDLE_VALUE);
}
bool Search::SafeCloseHandle()
{
if (CloseHandle(m_hProcess)) {
m_hProcess = INVALID_HANDLE_VALUE;
return true;
}
return false;
}
void Search::WaitForIdle()
{
MSG msg;
while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}