外挂开发教程:监控游戏外挂的本质与实现方法

2024-08-05

在数字时代,游戏已超越娱乐范畴,被赋予了艺术性、科技性和哲学思辨性的附加价值。今天的主题颇具挑战性且引人入胜——如何通过修改游戏内存以达到难以想象的超级秒杀效果?这不仅涉及超凡技术,亦体现对游戏世界深入洞察的妙用。

什么是内存改写?

游戏外挂

首先需明确何为内存改写:即通过定位并调整计算机内存中游戏数据位置,实现所需数值的替换,此过程犹如科幻片中的黑科技。然而,这种手法确属游戏破解者的惯用手段之一,他们通过此类方法来左右游戏内各项参数,如无敌、无限资源等。

While(1){
If(植物大战僵尸打开){
可以点击按钮
}else{
不可以点击按钮
}
Sleep(1000);//为了不让其太过于频繁,此处隔着1秒检测一次
}

接下来,我们将深入探讨实际操作步骤。首先,必须确定游戏的内存地址,这需要借助专业软件并具备一定技术基础。成功定位地址后,便可着手进行修改。然而,值得注意的是,内存修改具有潜在危险性,若操作不当,可能引发游戏崩溃或计算机系统故障等问题。

BOOL CPVZCheaterDlg::OnInitDialog()此为初始化对话框
// TODO:  在此添加额外的初始化代码
CreateThread();创建线程,返回handle句柄,通过线程句柄可以操控线程
CreateThread(
    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
    _In_ SIZE_T dwStackSize,
    _In_ LPTHREAD_START_ROUTINE lpStartAddress,		函数地址,我们可以右击转到定义看此参数含义
    _In_opt_ __drv_aliasesMem LPVOID lpParameter,
    _In_ DWORD dwCreationFlags,
    _Out_opt_ LPDWORD lpThreadId
);
HANDLE WINAPI CreateThread
( 
  __in_opt   LPSECURITY_ATTRIBUTES lpThreadAttributes, // 指向SECURITY_ATTRIBUTES 的指针,为新线程指定安全描述 
  __in       SIZE_T dwStackSize, // 初始化线程堆栈尺寸 
  __in       LPTHREAD_START_ROUTINE lpStartAddress, //线程函数所指向的地址起始函数   
__in_opt   LPVOID lpParameter, // 给线程函数传递的参数   
__in       DWORD dwCreationFlags, // 有关线程的标志  
 __out_opt  LPDWORD lpThreadId //系统分配给线程的ID
 );  

内存地址的秘密

	//子线程句柄
	HANDLE m_monitorThread;
m_monitorThread = CreateThread(NULL, NULL, func, NULL, NULL, NULL);
Func返回的应该是下面的类型值需要在全局区声明
 DWORD (WINAPI *PTHREAD_START_ROUTINE)(
    LPVOID lpThreadParameter
);
//用来监控游戏的线程
DWORD WINAPI monitorThreadFunc(LPVOID lpThreadParameter){
return NULL;
}
FindWindow();//查看窗口是否打开,两个参数窗口类名,窗口名称
FindWindowW(
    _In_opt_ LPCWSTR lpClassName,
_In_opt_ LPCWSTR lpWindowName);

内存地址作为游戏数据在计算机内存中的藏身之处,其重要性不言而喻。其与每个数据对应,犹如人们的住所地址般独特且关键。游戏运行中所包含的各种关键元素,如角色状态,游戏进程等均存储其中。

	//当游戏窗口关闭后,不仅禁止点击还要将之前打钩的去掉
	g_dlg->m_bnKill.SetCheck(FALSE);
	g_dlg->m_bnSun.SetCheck(FALSE);

要定位此类地址,必须借助专业级别的内存编辑器。此设备可助您捕获并定位游戏内存内欲修改之数据的具体地址。一旦找准此位置,便可着手执行“魔幻操作”—直接输入所需数值至指定地址,从而引发游戏内部对应数据的变化。

游戏外挂

CTR+G	找到修改内存的地址
00566D10  |.  89B5 C8000000 mov dword ptr ss:[ebp+0xC8],esi       

改写全局变量与局部变量

00566D06      2B7424 20     sub esi,dword ptr ss:[esp+0x20]

固定变量,亦称全局变量,犹如游戏世界之"公产",一经设定,地址恒定不变,不受重启影响;反之,动态变量,即局部变量,为"私有财产",其生命周期局限于特定函数调用期间,调用完毕即告消亡。

00566D06      2BF6          sub esi,esi   
00566D08      90            nop
00566D09      90            nop

鉴于全局变量拥有恒定不变的地址,故其值变更相对容易实现。只需获知该变量的地址信息,便可随时随地进行相应调整。然而,对于局部变量来说,由于其地址处于动态变化状态,因此其值变更归于复杂且需运用相关技术与工具有效解决。

00566D06      2B742420    

虚拟内存与物理内存

00566D06      2BF69090      

阐述内存改写原理之时,虚拟内存与物理内存之定义至关重要。虚拟内存乃操作系统为每位程序构建之“伪装”,使其误以为占据完整内存空间;物理内存即为大众熟知之内存条,承载着计算机实际可用之内存资源。

在32位操作系统环境下,每个应用或进程均能拥有4GB虚拟内存,即使电脑实际物理内存仅为4GB。此项机制有效提升了内存利用率,同时,因其调整修改更为繁杂而有趣。

00566D07     742420 	//原版不需要秒杀僵尸
00566D07     F69090   //需要秒杀僵尸

实战演示:改写阳光值

下面就以具体情境“修改《植物大战僵尸》中阳光值”为例进行审视。首要任务便是探寻阳光值在内存中的定位信息。此时需借助强力的内存扫描工具,对比游戏内光能与内存中的数值关系,从而锁定阳光值的确切位置。

在确认最佳位置后,即可启动调整程序。只需将所需阳光值输入至该地址,便能立即体验到游戏中阳光数值的变化。此功能如同赋予您非凡力量,使您轻松掌控游戏环境。

static HANDLE g_processHandle;//定义一个全局句柄,可以右击OpenProcess转到定义,发现返回是HANDLEWINAPI,OpenProcess,不需要指针
//这里封装的是全局函数
// 将某个值写入植物大战僵尸内存(后面的可变参数是地址链,要以-1结尾)
void WriteMemory(void *value, DWORD valueSize, ...)				//第一个参数传数据,第二个是数据有多大,如数据只有3个字节,...是个地址链
{
	if (value == NULL || valueSize == 0 || g_processHandle == NULL) return;
	DWORD tempValue = 0;
	va_list addresses;
	va_start(addresses, valueSize);
	DWORD offset = 0;
	DWORD lastAddress = 0;
	while ((offset = va_arg(addresses, DWORD)) != -1)
	{
		lastAddress = tempValue + offset;
		::ReadProcessMemory(g_processHandle, (LPCVOID)lastAddress, &tempValue, sizeof(DWORD), NULL);
	}
	va_end(addresses);
	::WriteProcessMemory(g_processHandle, (LPVOID)lastAddress, value, valueSize, NULL);
}
//下面是重载的函数比较简单,告诉写数据,数据占的字节,数据所在地址
void WriteMemory(void *value, DWORD valueSize, DWORD address) {
	WriteMemory(value, valueSize, address, -1);
}
	//下面三句代码放在此处是,肯定发现了游戏的窗口,句柄不为空,则进程句柄也不为空
			//获得植物大战僵尸的进程id
			DWORD processPid;
			GetWindowThreadProcessId(windowHandle, &processPid);//此处通过植物大战僵尸的窗口句柄拿到进程id
			//获得植物大战僵尸的进程句柄
			g_processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processPid);//PROCESS_ALL_ACCESS访问内存,传权限访问内存数据,
			//此时进程句柄拿到了,我们就可以秒杀僵尸了等操作
void CPVZCheaterDlg::OnBnClickedKill()
{
	if (m_bnKill.GetCheck()){//需要秒杀僵尸
		BYTE data[] = {0xF6,0x90,0x90};//F69090 ,此处详情看Word,这是我们要写进内存的数据
		WriteMemory(data,sizeof(data),0x00566D07);//写入内存,需要进程句柄,具体内容如上所示,参数比较多相对复杂,这里直接封装了一些写内存的在上面
		//注意上面的写内存函数是我们自定义封装好的WriteMemory,而不是系统的WriteProcessMemory,在程序开头有,这里只需传三个参数,地址
	}
	else{//不需要秒杀僵尸
		BYTE data[] = { 0x74, 0x24, 0x20 };//742420 ,此处详情看Word,这是我们要写进内存的数据
		WriteMemory(data, sizeof(data), 0x00566D07);
	}
}

风险与道德

#include 
using namespace std;
int g_age;
int main(){
	int a = 10;
	g_age = 20;
	//局部变量地址变来变去
	//cout << &a << endl;//00AFFA1C	00A3FBE0
	/*cout << &g_age << endl;*/
	getchar();
	return 0;
}

尽管内存改写颇具吸引力,然而其潜在风险与伦理问题我们亦需审慎对待。首先,不恰当的操作有可能导致游戏崩溃或计算机故障。其次,此类行为恐将影响游戏公正性,损害其他玩家的游戏体验。

在享受内存改写之乐时,务必恪守行规,尊崇其他玩家。如此方能于游戏天地享受到真正的愉悦体验。

结语

    7: 	int a = 10;
0076530E  mov         dword ptr [ebp-8],0Ah  
     8: 
     9: 	g_age = 20;
00765315  mov         dword ptr ds:[0076F354h],14h  

通过游戏内存改编,玩家可体验别样玩法并洞悉游戏内在机理。此非单纯技术考验,实为对游戏世界之深度解读与研究。期望此次分享能给予您新思路及乐趣。试问诸位:内存改编应视为正当游戏策略,抑或严加制止?请于评论区各抒己见,展开探讨!

登录/注册