在Windows系统下,每个进程都拥有独立的资源空间,采用的是虚拟地址。
简单来说,即使你知道另一个进程的某个变量的内存地址,你却没办法使用别的进程来直接访问的。
哪怕这个地址的是通过 malloc() 等分配的动态内存也不行的。
一般而言,我们通过给指定进程注入我们指定的动态库,使其在同一个进程中,然后在通过指定地址访问到某个变量,读取或修改其值。
通常这样的注入的方式有两种:
1.由指定进程被动的加载该动态库(钩子注入)。
2.由指定进程主动的加载该动态库(远线程注入)。
Windows消息机制:由系统生成消息,然后颁发给指定应用程序,应用程序来处理。
我们可以给指定的线程,安装一个钩子程序,来监听这个程序的消息。
这话怎么理解?一听就迷糊了…
我说的简单一点:我们可以编写 一个函数,去监听 指定线程的 Windows消息。
什么个原理?
我们编写一个 动态库,然后去给指定的线程安装一个挂钩函数,当产生消息的时候,这个指定的线程就要调用我们指定的 钩子函数,如果它们是不同的进程,这个是办法进行调用的,所以操作系统会将该动态库加载到指定的线程中去,这样就达到了我们的目的,注入动态库。
以植物大战僵尸为例子
代码实现
DLL动态库代码
.h 文件
#pragma once
#include<Windows.h>
#define EXPORT __declspec(dllexport)
/*
\ brief 键盘钩子过程
*/
LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam);
/*
\ brief 安装键盘钩子
*/
bool EXPORT SetHook(DWORD pThreadId);
.cpp 文件
#include "Hook.h"
/*
\ brief 键盘消息钩子函数(必须是__stdcall 调用)
\ param code 消息的信息
\ param wParam 生成击键消息的键的虚拟键代码
\ param lParam 先前的键状态标志和过渡状态标志
*/
LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam)
{
// 如果是wParam和lParam的参数包含关于一个按键消息的信息
// 如果 按下的是 Home 键
// 如果 按键状态是激活
if ((code == HC_ACTION) && (wParam == VK_HOME) && GetKeyState(VK_HOME)) {
// 植物大战僵尸 阳光基址 [[006A9EC0]+768]+5560
size_t address = 0x006A9EC0;
address = *reinterpret_cast<size_t*>(address) + 0x768;
address = *reinterpret_cast<size_t*>(address) + 0x5560;
size_t* pointer = reinterpret_cast<size_t*>(address);
// 将阳光修改为 9999
*pointer = 9999;
}
// 传递给下一个挂钩
return CallNextHookEx(nullptr, code, wParam, lParam);
}
/*
\ brief 安装钩子
\ param 指定的线程
\ return 成功返回true,失败返回false
*/
bool EXPORT SetHook(DWORD pThreadId)
{
/*
\ brief 安装消息钩子
\ param KeyboardProc 钩子过程函数
\ param 钩子过程所在的DLL模块句柄
\ param 挂钩的线程
\ return 成功返回挂钩过程的句柄,失败返回NULL
*/
return SetWindowsHookExW(WH_KEYBOARD,
KeyboardProc,
GetModuleHandleW(TEXT("Hook.dll")),
pThreadId);
}
DLL动态库重点项目属性
注入代码
Hook.h
#pragma once
#include<Windows.h>
#define IMPORT __declspec(dllimport)
//LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam);
bool IMPORT SetHook(DWORD pThreadId);
main.cpp
#include<iostream>
#include<Windows.h>
#include"Hook.h" // 引入 动态库的头文件
using namespace std;
int main(int argc, char* argv[])
{
// 获取游戏窗口句柄
HWND mHwnd = FindWindowW(nullptr, TEXT("植物大战僵尸中文版"));
if (mHwnd == NULL) {
cout << "error FindWindowW" << endl;
system("pause");
return 0;
}
DWORD pId;
// 获取 游戏进程和线程ID
DWORD tId = GetWindowThreadProcessId(mHwnd, &pId);
// 安装钩子,传入线程的ID
if (SetHook(tId)==false) {
cout << "error " << endl;
}
else {
cout << "yes ok " << endl;
}
// 卡住程序,不让程序退出。
system("pause");
}
重点截图
先启动植物大战僵尸和然后注入程序(不要让程序退出)
点击植物大战僵尸,然后按下 Home 键(不要让程序退出)
文章时间2019年12月18日15:49:25