跳至主要內容

注入MFCdll

chanchaw大约 4 分钟cpp

概述

向目标进程注入 MFC DLL 是游戏外挂的一贯做法,图形界面方便用户使用

注入器

制作控制台应用程序用于注入之后制作的 MFC DLL 客户端,下面是控制台应用程序的完整代码。逻辑是先在目标进程中申请指定大小的内存空间,然后向其中写入 dll 文件的路径,最终通过 CreateRemoteThread 调用申请的内存空间 - 使用 LoadLibraryA 以加载库文件的方式 - 即运行注入的 dll

// A038Injector.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <Windows.h>

char windowClassName[] = "MainWindow";// pvz 窗口类
char windowTitle[] = R"(植物大战僵尸中文版)";// pvz 窗口标题
char dllPath[] = R"(D:\source\cpp\windowsapi\Debug\A038PVZMFCdll.dll)";// dll路径

int main()
{
    std::cout << "PVZ Injector,向 PVZ 中注入MFC DLL\n";

	// 使用窗口类、窗口标题获取窗口句柄
	HWND hwnd = FindWindowA(windowClassName, windowTitle);// 获取窗口句柄	
	printf("窗口句柄:%p\r\n", hwnd);

	// 根据窗口句柄获取进程PID
	DWORD pid = 0;// 目标进程PID
	GetWindowThreadProcessId(hwnd, &pid);// 通过窗口句柄获取窗口进程PID
	printf("目标进程PID:%d\r\n", pid);

	// 获取目标进程句柄
	HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);// 通过PID获取目标进程句柄
	printf("进程句柄:%p\r\n", handle);

	/**
	 * 向目标进程中申请内存空间,下面逐个介绍参数:
	 * 1. handle: 目标进程句柄
	 * 2. NULL:
	 * 3. 4 * 1024:申请的内存空间大小,32位系统会保证4K对齐,所以一次最少申请4K=4*1024
	 * 4. MEM_COMMIT:申请的内存的性质,这里表示物理内存或者页面内存
	 * 5. PAGE_EXECUTE_READWRITE:申请的内存空间可读写
	 */
	void *mem = VirtualAllocEx(handle, NULL, 10 * 1024, MEM_COMMIT, PAGE_EXECUTE_READWRITE /*PAGE_EXECUTE_READWRITE=0x40,表示这块内存区域可读写,可执行*/);
	if (mem == NULL) {
		printf("在目标进程申请内存失败,无法注入代码!!!");
		return 0;
	}
	printf("已在目标进程申请内存空间:%p\r\n", mem);

	/**
	 * 向申请的内存区域写入准备好的自定义代码 - 将上面的裸函数写入目标进程中自己申请的内存区域
	 * 向IDE中编译的控制台应用程序中注入代码后通过 `x32dbg.exe` 查看申请的内存区域的首地址
	 * 有可能看到 jmp 代码,则需要 vs2017 中修改 C++ 优化为禁用状态
	 * 查看图片:https://www.xdfznh.club/kbp/cpp/编译cpp程序禁用优化.png
	 * 第三个参数 dllPath 在案例 “远程线程注入代码” 中是裸函数,这里填写自定义的dll绝对路径
	 * 后面的 CreateRemoteThread 通过 LoadLibraryA 调用申请内存,即调用了自定义 dll
	 */
	bool retWriteProcessMemory = WriteProcessMemory(handle, mem, dllPath, 0x300, 0);
	printf("向申请的内存空间注入MFC DLL收到的响应:%d\r\n", retWriteProcessMemory);

	// 调用申请内存中注入的DLL
	::CreateRemoteThread(handle, 0, 0, (LPTHREAD_START_ROUTINE)LoadLibraryA, mem, 0, 0);

	system("pause");
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件

MFC DLL

创建MFC DLL项目

注入MFCDLL01创建MFCDLL项目
注入MFCDLL01创建MFCDLL项目

添加对话框资源

按照下面步骤为项目添加对话框资源,并新增关联类 CDialogMainCls
注入MFCDLL02添加对话框资源 然后在项目同名的入库文件 A038PVZMFCdll01.cpp 中初始化并显示对话框

// CA038PVZMFCdll01App 初始化
#include "CDialogMainCls.h"

BOOL CA038PVZMFCdll01App::InitInstance()
{
	CWinApp::InitInstance();

	CDialogMainCls dialogCls;
	dialogCls.DoModal();

	return TRUE;
}

非阻塞显示对话框

采用创建子线程的方式显示对话框就不会阻塞 mfc dll 线程的继续执行

// 唯一的 CA038PVZMFCdll01App 对象

CA038PVZMFCdll01App theApp;


// CA038PVZMFCdll01App 初始化
#include "CDialogMainCls.h"

void showDialogDo() {
	CDialogMainCls dialogCls;
	dialogCls.DoModal();
}

DWORD WINAPI showDialog(LPVOID paramPoint) {
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	showDialogDo();
	FreeLibraryAndExitThread(theApp.m_hInstance,777);// 退出对话框后结束
	// FreeLibrary(theApp.m_hInstance);
	return 1;
}

BOOL CA038PVZMFCdll01App::InitInstance()
{
	CWinApp::InitInstance();
	::CreateThread(0, 0, showDialog, 0, 0, 0);
	return TRUE;
}