之前练习写了个拳皇13修改器,发现需要对远程进程操作,一直需要ReadProcessMemory,发现代码量会变得很多,最近又学到一个代码注入,发现比之前的的要方便很多。关键API是CreateRemoteThread,这个网上有很多介绍,这边就不仔细介绍了。
有2个地方需要注意:平台和发布模式。必须是Release模式下,平台要跟需要注入的程序有关,比如:黑暗之魂重制版是64位的,网易有道词典是32位的。
还有个地方需要注意,FindWindow是可以根据类名或者标题名找到窗口句柄的,但是有的应用程序标题会用到特殊符号,比如黑暗之魂重制版,标题名为“DARK SOULS™: REMASTERED”,根据标题名是查找不到窗口句柄的(拳皇13那种直接标题名的就不用这么麻烦),这个时候就需要用到Microsoft Spy++来查找类名来解决这个问题。
直接Show code,代码注释比较多,详细就不介绍了:
Remote.h:
#pragma once
#include <Windows.h>
typedef int(_stdcall* MessageBoxProc)(HWND hwnd, char* strText, char* strCaption, UINT uType);
struct SRemoteParam
{
byte cmd;
MessageBoxProc pMsgBoxFunc;
char strMsgContent[100];
};
DWORD WINAPI RemoteThreadProc(LPARAM lParam)
{
SRemoteParam* pRemoteParam = (SRemoteParam*)lParam;
if (1 == pRemoteParam->cmd)
pRemoteParam->pMsgBoxFunc(NULL, pRemoteParam->strMsgContent, pRemoteParam->strMsgContent, MB_OK);
return 0;
}
/**
* 用来计算DWORD WINAPI RemoteThreadProc(LPARAM lParam)函数所占内存大小
*/
void AfterRemoteThreadProc()
{
}
CAppHandle.h:
#pragma once
#include <string>
#include <Windows.h>
class CAppHandle
{
public:
...
/**
* 代码注入
* -> IN: SIZE_T nRemoteProcSize - 函数所占内存大小
* void* pRemoteProc - 函数地址
* SIZE_T nRemoteParamSize - 变量所占内存大小
* void* pRemoteParam - 变量地址
*/
void SendSpy(
SIZE_T nRemoteProcSize,
void* pRemoteProc,
SIZE_T nRemoteParamSize = 0,
void* pRemoteParam = nullptr);
private:
...
void FindHandle();
private:
std::string m_strClassName; // 窗口类名
std::string m_strWindowName; // 窗口标题名
HANDLE m_handleApp;
char* m_pRemoteProc; // 注入远程进程函数
char* m_pRemoteParam; // 注入远程进程变量
};
CAppHandle.cpp:
/**
* 代码注入
* -> IN: SIZE_T nRemoteProcSize - 函数所占内存大小
* void* pRemoteProc - 函数地址
* SIZE_T nRemoteParamSize - 变量所占内存大小
* void* pRemoteParam - 变量地址
*/
void CAppHandle::SendSpy(
SIZE_T nRemoteProcSize,
void* pRemoteProc,
SIZE_T nRemoteParamSize,
void* pRemoteParam)
{
{// 注入远程进程的函数
// 在远程进程中分配函数内存
m_pRemoteProc = (char*)VirtualAllocEx(m_handleApp, 0, nRemoteProcSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (nullptr == m_pRemoteProc) return;
// 在远程进程中注入函数
WriteProcessMemory(m_handleApp, m_pRemoteProc, pRemoteProc, nRemoteProcSize, nullptr);
}
if (nRemoteParamSize != 0 && pRemoteParam != nullptr)// 注入远程进程的函数变量
{
// 在远程进程中分配变量内存
m_pRemoteParam = (char*)VirtualAllocEx(m_handleApp, 0, nRemoteParamSize, MEM_COMMIT, PAGE_READWRITE);
if (nullptr == m_pRemoteParam) return;
// 在远程进程中注入变量
WriteProcessMemory(m_handleApp, m_pRemoteParam, pRemoteParam, nRemoteParamSize, nullptr);
}
// 启动远程线程
HANDLE hThread = CreateRemoteThread(m_handleApp,
nullptr,
0,
(LPTHREAD_START_ROUTINE)m_pRemoteProc,
m_pRemoteParam,
0,
nullptr);
if (hThread != NULL)
{
// 等待线程结束
WaitForSingleObject(hThread, INFINITE);
// 获得返回值
DWORD dwReturn;
GetExitCodeThread(hThread, &dwReturn);
CloseHandle(hThread);
}
if (m_pRemoteProc != nullptr)
{
VirtualFreeEx(m_handleApp, m_pRemoteProc, 0, MEM_RELEASE);
m_pRemoteProc = nullptr;
}
if (m_pRemoteParam != nullptr)
{
VirtualFreeEx(m_handleApp, m_pRemoteParam, 0, MEM_RELEASE);
m_pRemoteParam = nullptr;
}
}
void CAppHandle::FindHandle()
{
ReleaseHandle();
HWND hwndWindow = NULL;
if (!m_strClassName.empty())
hwndWindow = FindWindow(m_strClassName.c_str(), nullptr);
else
hwndWindow = FindWindow(nullptr, m_strWindowName.c_str());
if (NULL == hwndWindow) return;
DWORD dwPID = 0;
GetWindowThreadProcessId(hwndWindow, &dwPID);
if (NULL == dwPID) return;
m_handleApp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);
}
Main.cpp:
#include <iostream>
#include "CAppHandle.h"
#include "Remote.h"
int main()
{
std::cout << "Hello, world!" << std::endl;
CAppHandle* pAppHandle = new CAppHandle("DARK SOULS", "网易有道词典");
if (!pAppHandle) return -1;
int nRempteProcSize = (byte*)AfterRemoteThreadProc - (byte*)RemoteThreadProc;
int nRemoteParamSize = sizeof(SRemoteParam);
SRemoteParam param;
param.cmd = 1;
param.pMsgBoxFunc = (MessageBoxProc)MessageBoxA;
sprintf_s(param.strMsgContent, 100, "Hello, wolrd");
while (true)
{
if (pAppHandle->IsAppRunning())
{
std::cout << "SnedSpy: " << std::endl;
pAppHandle->SendSpy(nRempteProcSize, RemoteThreadProc, nRemoteParamSize, ¶m);
}
}
delete pAppHandle;
pAppHandle = nullptr;
return 0;
}
效果: