-1.植物大战僵尸讲解
植物大战僵尸修改器制作–从入门到入土-软件逆向-看雪-安全社区|安全招聘|kanxue.com
植物大战僵尸逆向解析
今天主要的目的就是讲解一下外挂的基本实现原理
- 实现阳光值无限
- 修改游戏逻辑
- 用c++编写游戏外挂
1.程序的前置知识:
-
程序的基本概念
其实所有的exe,本质来说都是一个文本文件
无论是浏览器还是QQ,微信等等,电脑和手机同理
本质来说都是一个文本文件
类比来说就是一个写满数据的txt文本! -
Cheat Engine一款exe程序文本读取器
既然程序是一个文本,那我们就需要一个文本读取器来帮我们阅读这个文本,因为exe是不断在运行代码的,所有也可以说它是一个动态文本
而Cheat Engine就可以帮我们读取!
Cheat Engine及可以是笔也是眼睛!
2.实战寻找阳光变量的所在位置
通过Cheat Engine找到阳光变量的位置,并且尝试对他进行修改!
成功修改!
将该变量存储下来,并且重新开始一个游戏
发现这个阳光值不可以修改了,需要再次寻找阳光的地址!
这个问题留到最后编写外挂的时候讲解!
3.汇编的前置知识
这里我们又要补充一个概念:程序的本质的本质是什么?
之前我们说"其实所有的exe,本质来说都是一个文本文件"
那”文本文件txt“的本质又是什么呢???
其实”文本文件txt,电脑里的图片,exe“的本质都是0和1,组成的数据矩阵
本质来说就是0和1
知道了什么是代码什么是汇编
在学习汇编前我们需要知道什么是汇编!!!
机器码-》汇编语言-》高级语言
三条基本的汇编指令:
add A,B
sub A,B
mov A, B
4.实战修该游戏代码逻辑
- 修该种植阳光的代码
- 修改收集阳光的代码
5.实战编写植物大战僵尸外挂
6A9EC0 0x768 0x5560
0.单局修改阳光数值
使用cheatengine打开植物大战僵尸
阳光初始值为50,进行第一次搜索,左边的结果有577个!
改变阳光的值继续搜索!
将阳光的值消耗掉,阳光值变为0
再次搜索查看之前的搜索结果是否有值被改变成0,发现有两个!继续
再次收集阳光,数量变成25,再次搜索结果
发现只有一个变量存在!!可以确定这就是目标!
尝试修改阳光的值为999!
手动修改成功!
虽然这里的阳光地址可以修改阳光值,但如果这局游戏结束,再次使用这个地址修改阳光就会失效!
这是由于该地址不是基址会随程序的变化而变化,所以当重启游戏的时候,该地址就会失效需要寻找新的解决方案!
1.全局修改阳光数值
根据[[0.单局修改阳光数值]]获取到的变量地址进行分析,用来推导出阳光变量的基址
选中变量右击查看是什么地方访问了这个地址!
点击完"找出是什么访问了这个地址"后,就会弹出相应的汇编指令,与计数
其中计数会不断变化,按下esc可以暂停计数!
发现共有两条汇编指令在访问阳光的数值!
是一个地址加上偏移的值存储了阳光的值:
0041BAB5 - 03 82 60550000 - add eax,[edx+00005560]
00489825 - 8B 86 60550000 - mov eax,[esi+00005560]
可以得出此时edx或eax的值+0x00005560位置存储的就是阳光的值!
可以获取到edx或eax的值来继续追寻阳光变量的基址!
右击指令查看详细信息,可以找到eax的值0x1076DE98
这个可能与阳光的基址有关,将该值继续使用0x1076DE98搜索
搜索结果有52个,但其中并没有基址所以阳光的基地址可能是二级偏移继续寻找
这个可能与阳光的基址有关,将该值继续使用0x1076DE98搜索
搜索结果有52个,但其中并没有基址所以阳光的基地址可能是二级偏移继续寻找
怀疑这些地址是关键,由于阳光是会被不断访问数值的所以监测一下这些数值被谁访问过!
都一一看过后
发现这个地址最可疑0257B0D8,他这里的0x768,记录一下EDI=0257A970
去内存里搜索看0257A970是不是存储在固定基址里的!’
果然成功找到固定的基址:
编写外挂的代码:
#include <iostream>
#include <windows.h>
#include <thread>
#include "conio.h"
#define Ver Ver2.0
using namespace std;
DWORD DPid{ 0 };
//菜单函数 每次输入完指令都会调用它 重新打印下菜单
void menu() {
cout << "***********************" << endl;
cout << "**** 1、修改阳光值 ****" << endl;
cout << "**** 2、锁定阳光值 ****" << endl;
cout << "**** 0、退出本程序 ****" << endl;
cout << "***********************" << endl;
cout << "请输入相关指令:" << endl;
}
//修改阳光值
void yG(DWORD pid) {
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DPid);
if (hProcess == NULL)
{
printf("你当前尚未进入关卡!请进入游戏关卡:自动获取阳光");
}
DWORD YGJZ{ 0x6A9EC0 }; //静态基址
DWORD YGJZValue{ 0 };
DWORD nSize{ 0 };
BOOL ok = ReadProcessMemory(hProcess, (LPVOID)YGJZ, &YGJZValue, sizeof(DWORD), &nSize);
DWORD YGjz2{ 0x768 }; //一级偏移
DWORD YGjz2Value{ 0 };
BOOL ok2 = ReadProcessMemory(hProcess, (LPVOID)(YGJZValue + YGjz2), &YGjz2Value, sizeof(DWORD), &nSize);
DWORD YGJZ3{ 0x5560 }; //二级偏移
DWORD YGJZ3Value{ 0 };
BOOL OK3 = ReadProcessMemory(hProcess, (LPVOID)(YGjz2Value + YGJZ3), &YGJZ3Value, sizeof(DWORD), &nSize);
cout << "当前阳光值:[ " << YGJZ3Value << " ] \n请输入要修改的阳光值:\n"; //最终及地址
int edit;
cin >> edit;
BOOL wri = WriteProcessMemory(hProcess, (LPVOID)(YGjz2Value + YGJZ3), &edit, sizeof(edit), &nSize);
if (wri == true)
{
cout << "写入成功!" << endl;
}
}
//锁定阳光值
void setsun(DWORD pid) {
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DPid);
if (hProcess == NULL)
{
printf("你当前尚未进入关卡!请进入游戏关卡:自动获取阳光");
}
DWORD YGJZ{ 0x6A9EC0 }; //静态基址
DWORD YGJZValue{ 0 };
DWORD nSize{ 0 };
BOOL ok = ReadProcessMemory(hProcess, (LPVOID)YGJZ, &YGJZValue, sizeof(DWORD), &nSize);
DWORD YGjz2{ 0x768 }; //一级偏移
DWORD YGjz2Value{ 0 };
BOOL ok2 = ReadProcessMemory(hProcess, (LPVOID)(YGJZValue + YGjz2), &YGjz2Value, sizeof(DWORD), &nSize);
DWORD YGJZ3{ 0x5560 }; //二级偏移
DWORD YGJZ3Value{ 0 };
BOOL OK3 = ReadProcessMemory(hProcess, (LPVOID)(YGjz2Value + YGJZ3), &YGJZ3Value, sizeof(DWORD), &nSize);
cout << "当前阳光值:[ " << YGJZ3Value << " ] \n请输入要你要锁定的阳光值:\n"; //最终及地址
int edit;
cin >> edit;
while (true)
{
if (_kbhit()) // 如果有按键被按下
{
if (_getch() == 'q') //如果按下了q键则跳出循环
{
break;
}
}
Sleep(500);
BOOL wri = WriteProcessMemory(hProcess, (LPVOID)(YGjz2Value + YGJZ3), &edit, sizeof(edit), &nSize);
}
}
int main()
{
system("mode con cols=32 lines=18 ");//设置控制台大小
system("color a"); //设置控制台字体颜色
SetConsoleTitle(L"ZomKill Ver2.0"); //设置控制台标题
int z;
HWND hGame = FindWindow(L"MainWindow", L"植物大战僵尸中文版");
int select;
while (true)
{
if (hGame == NULL)
{
cout << "游戏未运行,请打开游戏再运行本辅助\n" << endl;
return 0;
}
else
{
GetWindowThreadProcessId(hGame, &DPid); //拿出进程ID
cout << "+++++++++++++++++++++++++++++\n当前游戏进程ID:" << DPid << endl;
}
menu();
cin >> select;
switch (select)
{
case 1:
yG(DPid);
break;
case 2:
setsun(DPid);
break;
default:
break;
}
system("pause");
system("cls");
}
}