免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!
本次游戏没法给
内容参考于:微尘网络安全
工具下载:
链接:https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd=6tw3
提取码:6tw3
复制这段内容后打开百度网盘手机App,操作更方便哦
上一个内容:50.x86游戏实战-XXX副本内瞬图
上一个内容里找出了副本内切换地图的代码,接下把它封装到C++里,之后再找返回城镇的方式
新加副本内瞬移函数声明
代码
然后新加一个按钮
它的属性
双击按钮让它生成点击事件处理函数
副本内瞬移按钮的点击事件处理函数里的内容
然后副本内瞬移搞好了,接下来再搞返回城镇
打开游戏,然后打开OD附加到游戏,然后来到公共call
下图通过返回城镇
然后CTRL+F9,来到下图,可以看到下图位置它没有调用0x1127EC0,这为什么?
然后在下图位置打断点,看看它为什么没有0x1127EC0函数
断点住
然后按F7,看到它是通过jmp进入的0x1127EC0函数
然后接下来开始模拟这些代码,所以来到Dbgview.exe看这些发包函数的参数
然后发现模拟的代码调用完没有用,没法返回城镇,所以要找别的
pushad push 0x7E mov ecx, 0x1AEB6E4 mov ecx, [ecx] mov eax, 0x1127D60 call eax push 0x0 mov ecx, 0x1AEB6E4 mov ecx, [ecx] mov eax, 0x11285B0 call eax push 0x0 mov ecx, 0x1AEB6E4 mov ecx, [ecx] mov eax, 0x11285B0 call eax push 0x0 mov ecx, 0x1AEB6E4 mov ecx, [ecx] mov eax, 0x11285B0 call eax push 0x0 mov ecx, 0x1AEB6E4 mov ecx, [ecx] mov eax, 0x11285B0 call eax push 0x0 mov ecx, 0x1AEB6E4 mov ecx, [ecx] mov eax, 0x11285B0 call eax push 0x0 mov ecx, 0x1AEB6E4 mov ecx, [ecx] mov eax, 0x11285B0 call eax mov ecx, 0x1AEB6E4 mov ecx, [ecx] mov eax, 0x1127EC0 call eax popad
再次来到OD,来到公共call通过返回城镇触发断点,然后CTRL+F9
然后CTRL+F9
然后CTRL+F9
然后CTRL+F9
到了第五层,是call eax,不是call地址了,所以这里就可以停下CTRL+F9了,可以回头看了,找一个参数少好调用的函数
首先观察第4层
它有一个参数,然后它还有一个ecx,接下来找ecx
经过观察ecx的值应该来自于下图红框位置调用的函数
然后在下图红框位置打断点
然后返回城镇触发断点
然后取消断点
然后按F7
然后一路F8,主要观察eax的值在哪来的,然后看到eax的值来自于0x1A3CBF4
然后经过多次断点观察,它的入参一直是0x138D,所以可以模拟代码了
模拟的代码,它就可以正常返回城镇了
pushad mov ecx,0x1A3CBF4 mov ecx,[ecx] push 0x138D mov eax,0x4E9740 call eax popad
然后写到C++代码里
首先创建一个返回城镇的按钮
它的属性
双击下图红框生成点击事件处理函数
创建的函数
然后声明返回城镇的函数
然后右击函数名
然后选择下图红框
然后实现内容
然后点击事件处理函数
DXXDlg.cppw文件的内容:新加OnBnClickedButton10函数(副本内瞬移按钮点击事件处理函数)、OnBnClickedButton11函数(返回城镇按钮点击事件处理函数)
// DXXDlg.cpp: 实现文件
//
#include "pch.h"
#include "WCDXX.h"
#include "afxdialogex.h"
#include "DXXDlg.h"
#include "MyStrust.h"
#include "json.h"
#include "CLVEH.h"
// DXXDlg 对话框
IMPLEMENT_DYNAMIC(DXXDlg, CDialogEx)
DXXDlg::DXXDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_DIALOG1, pParent)
{
OutputDebugStringA("执行流程-执行DXXDlg构造函数流程1");
}
DXXDlg::~DXXDlg()
{
OutputDebugStringA("执行流程-执行DXXDlg析构函数流程1");
}
void DXXDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(DXXDlg, CDialogEx)
ON_BN_CLICKED(IDC_BUTTON1, &DXXDlg::OnBnClickedButton1)
ON_BN_CLICKED(IDC_BUTTON2, &DXXDlg::OnBnClickedButton2)
ON_BN_CLICKED(IDC_BUTTON3, &DXXDlg::OnBnClickedButton3)
ON_BN_CLICKED(IDC_BUTTON4, &DXXDlg::OnBnClickedButton4)
ON_BN_CLICKED(IDC_BUTTON5, &DXXDlg::OnBnClickedButton5)
ON_BN_CLICKED(IDC_BUTTON6, &DXXDlg::OnBnClickedButton6)
ON_BN_CLICKED(IDC_BUTTON7, &DXXDlg::OnBnClickedButton7)
ON_BN_CLICKED(IDC_BUTTON8, &DXXDlg::OnBnClickedButton8)
ON_BN_CLICKED(IDC_BUTTON9, &DXXDlg::OnBnClickedButton9)
ON_BN_CLICKED(IDC_BUTTON10, &DXXDlg::OnBnClickedButton10)
ON_BN_CLICKED(IDC_BUTTON11, &DXXDlg::OnBnClickedButton11)
END_MESSAGE_MAP()
// DXXDlg 消息处理程序
void DXXDlg::OnBnClickedButton1()
{
MyStrust mystruct;
mystruct.InitMy();
}
void DXXDlg::OnBnClickedButton2()
{
}
void DXXDlg::OnBnClickedButton3()
{
}
void DXXDlg::OnBnClickedButton4()
{
MyStrust mystruct;
mystruct.InitMy(); // 初始化玩家角色数据
mystruct.ChangeBlooad(mystruct.My.Blood/2); // 修改血量
// TODO: 在此添加控件通知处理程序代码
}
void DXXDlg::OnBnClickedButton5()
{
MyStrust mystruct;
mystruct.FindMaster();
}
void DXXDlg::OnBnClickedButton6()
{
MyStrust mystruct;
mystruct.AllKill();
}
// 断点的处理函数,ExceptionInfo里面有当前断点的寄存器的值
void fun(_EXCEPTION_POINTERS* ExceptionInfo) {
call_loaA("wetool:EIP=%X,ECX=%X,参数=%X", ExceptionInfo->ContextRecord->Eip, ExceptionInfo->ContextRecord->Ecx, *(DWORD*)(ExceptionInfo->ContextRecord->Esp + 4));
}
// HOOK按钮的点击事件处理函数
void DXXDlg::OnBnClickedButton7()
{
// 调用AddVeh添加要下断点的地址,地址不要写重了,写重了关闭断点的时候游戏会闪退
CCLVEH::Instance()->AddVeh("n1", 0, 0, 0x01127D60, 1, 0, fun);
CCLVEH::Instance()->AddVeh("n2", 0, 0, 0x01128550, 1, 0, fun);
CCLVEH::Instance()->AddVeh("n3", 0, 0, 0x01128580, 1, 0, fun);
CCLVEH::Instance()->AddVeh("n4", 0, 0, 0x011285B0, 1, 0, fun);
// 下断点
CCLVEH::Instance()->InitVeh();
}
// 卸载HOOK按钮的点击事件处理函数
void DXXDlg::OnBnClickedButton8()
{
// 关闭断点
CCLVEH::Instance()->ExitVeh();
}
// 秒进地图按钮点击事件处理函数
void DXXDlg::OnBnClickedButton9()
{
MyStrust mystruct;
mystruct.StartIn();
}
// 副本内瞬移按钮点击事件处理函数
void DXXDlg::OnBnClickedButton10()
{
MyStrust mystruct;
mystruct.MoveInMap(1, 1);
}
// 返回城镇按钮点击事件处理函数
void DXXDlg::OnBnClickedButton11()
{
MyStrust mystruct;
mystruct.ComeBackTwon();
}
MyStrust.cpp文件的内容:新加 MoveInMap函数、ComeBackTwon函数
#include "pch.h"
#include "MyStrust.h"
void MyStrust::InitMy()
{
// [[0x1AB7CDC]+0x258] 名字
/*
*这个符号表示地址,在c++中被称为指针或指针类型
int表示4字节数字
int*就表示指针类型的int
(int*)0x1AB7CDC;这样表示0x1AB7CDC地址里的内容是4字节的数字
*/
int* address = (int*)0x1AB7CDC;
/*
取出地址中的值,也就是取出0x1AB7CDC里的内容
*address这样在左边只写一个*表示取内存地址里的值
也就是取address它的值,address是0x1AB7CDC,也就是取0x1AB7CDC它的值
*/
int addressValue = *address;
/*
(*(int*)addressValue)意思是
把addressValue转成int*,也就是把
addressValue的值当成内存地址,addressValue的值现在是[0x1AB7CDC]+0x258这个
现在这个地址里面的值是名字的地址,所以在左边加了一个*让把名字的地址去除了出来
取出来之后就得到了名字,名字是UNICODE类型,UNICODE又被称为宽字节,宽字节的数据是用两个字节描述一个文字或字母
在c++里wchar_t类型就是UNICODE
然后在c++中名字这种数据被称为字符串,如果要用字符串必须用指针类型也就是wchar_t*
右边加上*让wchar_t变成指针类型的wchar_t,才能在c++中使用字符串
*/
this->My.Name = (wchar_t*)(*(int*)(addressValue+0x258));
// 一般函数名后面是W就表示有UNICODE,也就是要用宽字节
// OutputDebugStringW(this->My.Name);
// L""这两个"之间表示字符串,L""表示这个字符串是宽字节(使用Unicode编码)
call_logW(L"wetool:人物姓名=%ws,测试=%ws", this->My.Name,L" 52am");
// [[0x1AB7CDC]+0x18C]x坐标
// [[0x1AB7CDC]+0x190]y坐标
/*
取出地址中的值,也就是取出0x1AB7CDC里的内容
*address这样在左边只写一个*表示取内存地址里的值
也就是取address它的值,address是0x1AB7CDC,也就是取0x1AB7CDC它的值
*/
this->My.X= *(float*)(addressValue + 0x18C);
this->My.Y = *(float*)(addressValue + 0x190);
char buf[256] = { 0 };
// 拼接文字,%f表示拼接一个小数(单浮点数)
sprintf(buf, "wetool:x=%f;y=%f", this->My.X, this->My.Y);
OutputDebugStringA(buf);
// [[0x1AB7CDC]+0x36A0]血量
this->My.Blood = *(int*)(addressValue + 0x36A0);
// 拼接文字,%d表示拼接一个整数(32位的整数)
sprintf(buf, "wetool:血量 = %d", this->My.Blood);
OutputDebugStringA(buf);
}
void MyStrust::UseObject(DWORD object)
{
object += 3; // 背包物品序号
/*
try的作用
如果
__asm {
pushad
push object
mov ecx, 0x1A5FB24 // 背包基址
mov ecx, [ecx]
mov eax, 0x7B9130 // 使用物品的函数地址
call eax
popad
}
这个代码运行过程中出现错误了,我不会让游戏崩溃,出现错误之后会执行
catch (...) {
OutputDebugStringA("MyStrust::UseObject error");
}
这个catch里面的代码,现在也就是执行OutputDebugStringA("MyStrust::UseObject error");这一行
*/
try {
__asm {
pushad
push object
mov ecx, 0x1A5FB24 // 背包基址
mov ecx, [ecx]
mov eax, 0x7B9130 // 使用物品的函数地址
call eax
popad
}
}
catch (...) {
OutputDebugStringA("MyStrust::UseObject error");
}
}
void MyStrust::ChangeBlooad(int v)
{
try {
__asm {
pushad
push 0
push 0
push 0
push v
mov ecx, 0x1AB7CDC // 里面有我们玩家角色数据的基址
mov ecx, [ecx]
mov eax, 0x8174E0 // 修改血量的函数地址
call eax
popad
}
}
catch (...) {
OutputDebugStringA("MyStrust::ChangeBlooad error");
}
}
void MyStrust::NewChangeBlooad(DWORD nEcx)
{
try {
__asm {
pushad
push 0
push 0
push 0
push 0
mov ecx, nEcx
mov eax, 0x8174E0 // 修改血量的函数地址
call eax
popad
}
}
catch (...) {
OutputDebugStringA("MyStrust::NewChangeBlooad error");
}
}
// 遍历怪物列表函数,加吸怪
void MyStrust::FindMaster()
{
wchar_t* Tmp1;
/*
[[[[[0x1A5E258]+3*4+0xA8]+0x14]+0x88]+0x10]
下方 ReadDword 函数通过 [[[[[0x1A5E258]+3*4+0xA8]+0x14]+0x88]+0x10] 这个取值算法得到附近列表(怪物列表)
*/
DWORD Tmp = ReadDword(ReadDword(ReadDword(ReadDword(ReadDword(0x1A5E258) + 0x3 * 4 + 0xA8) + 0x14) + 0x88) + 0x10);
if (Tmp != 0) {// 如果是0说明没有找到怪物列表
for (size_t i = 0; i < 100; i++)// 这里的100是随便写的
{
if (ReadDword(Tmp + i * 4)>0 && (ReadDword(ReadDword(Tmp + i * 4) + 0x90) == 0x211)) {
//if (ReadDword(Tmp + i * 4)>0) {
Tmp1 = (wchar_t*)ReadDword(ReadDword(Tmp + i * 4) + 0x258); // 0x258位置是名字
if (Tmp1 == 0) {// 如果是0说明没有名字
//call_logW(L"wetool:NULL\n");
this->masterNum.Name[i] = L"";
}
else {
/*
打印名字到 Dbgview.exe,其中 wetool这个是为了过滤 Dbgview.exe打印的一些跟我们不相关的日志
*/
//call_logW(L"wetool:%ws\n", Tmp1);
this->masterNum.Name[i] = Tmp1;
}
// 血量与玩家角色不是一个位置,可以在修改血量的位置追,这个不重要,后面有机会再找
//masterNum.Blood[i] = ReadDword(ReadDword(Tmp + i * 4) + 0x90);
this->masterNum.type[i] = ReadDword(ReadDword(Tmp + i * 4) + 0x90);
this->masterNum.x[i] = ReadFloat(ReadDword(Tmp + i * 4) + 0x18C);
this->masterNum.y[i] = ReadFloat(ReadDword(Tmp + i * 4) + 0x190);
this->masterNum.x[i] = this->My.X;
this->masterNum.y[i] = this->My.Y;
this->masterNum.Object[i] = ReadDword(Tmp + i * 4);
*(FLOAT*)(ReadDword(ReadDword(Tmp + i * 4) + 0xA8) + 0xC) = this->My.X; // 修改之后会瞬移的坐标,把它的值赋值成我们玩家的坐标
*(FLOAT*)(ReadDword(ReadDword(Tmp + i * 4) + 0xA8) + 0x10) = this->My.Y; // 修改之后会瞬移的坐标,把它的值赋值成我们玩家的坐标
call_logW(L"wetool:第%d个对象:%x 怪物名字:%ls type:0x%x x:%f y:%f", i, this->masterNum.Object[i], this->masterNum.Name[i], this->masterNum.type[i], this->masterNum.x[i], this->masterNum.y[i]);
}
}
}
else {
call_loaA("wetool:Tmp error");
}
}
// 吸怪加秒杀
void MyStrust::AllKill()
{
try {
this->InitMy();
this->FindMaster();
for (size_t i = 0; i < 100; i++)
{
if (this->masterNum.Object[i] > 0 && (this->masterNum.type[i] == 0x211)) {
call_logW(L"wetool:AllKill-第%d个对象:%x 怪物名字:%ls type:0x%x x:%f y:%f", i, this->masterNum.Object[i], this->masterNum.Name[i], this->masterNum.type[i], this->masterNum.x[i], this->masterNum.y[i]);
this->NewChangeBlooad(this->masterNum.Object[i]);// 调用修改血量的函数
}
}
}
catch (...) {
OutputDebugStringA("MyStrust::AllKill error");
}
}
void MyStrust::StartIn()
{
try {
__asm
{
pushad
// 切换地图
push 0x26
mov ecx, 0x1AEB6E4
mov ecx, [ecx]
mov eax, 0x1127D60
call eax
push 0xE
mov ecx, 0x1AEB6E4
mov ecx, [ecx]
mov eax, 0x1128550
call eax
push 0x2
mov ecx, 0x1AEB6E4
mov ecx, [ecx]
mov eax, 0x1128550
call eax
push 0x1E
mov ecx, 0x1AEB6E4
mov ecx, [ecx]
mov eax, 0x1128580
call eax
push 0x10F
mov ecx, 0x1AEB6E4
mov ecx, [ecx]
mov eax, 0x1128580
call eax
push 0x5
mov ecx, 0x1AEB6E4
mov ecx, [ecx]
mov eax, 0x1128550
call eax
push 0xE
mov ecx, 0x1AEB6E4
mov ecx, [ecx]
mov eax, 0x1128580
call eax
push 0x1
mov ecx, 0x1AEB6E4
mov ecx, [ecx]
mov eax, 0x1128580
call eax
mov ecx, 0x1AEB6E4
mov ecx, [ecx]
mov eax, 0x1127EC0
call eax
// 进入副本选择界面
push 0xF
mov ecx, 0x1AEB6E4
mov ecx, [ecx]
mov eax, 0x1127D60
call eax
mov ecx, 0x1AEB6E4
mov ecx, [ecx]
mov eax, 0x1127EC0
call eax
// 进入副本
push 0x10
mov ecx, 0x1AEB6E4
mov ecx, [ecx]
mov eax, 0x1127D60
call eax
push 0x68
mov ecx, 0x1AEB6E4
mov ecx, [ecx]
mov eax, 0x1128580
call eax
push 0
mov ecx, 0x1AEB6E4
mov ecx, [ecx]
mov eax, 0x1128550
call eax
push 0
mov ecx, 0x1AEB6E4
mov ecx, [ecx]
mov eax, 0x1128550
call eax
push 0
mov ecx, 0x1AEB6E4
mov ecx, [ecx]
mov eax, 0x1128550
call eax
mov eax, 0x1127EC0
call eax
popad
// 进入副本选择界面
pushad
push 0xF
mov ecx, 0x1AEB6E4
mov ecx, [ecx]
mov eax, 0x1127D60
call eax
mov ecx, 0x1AEB6E4
mov ecx, [ecx]
mov eax, 0x1127EC0
call eax
popad
}
}
catch (...)
{
OutputDebugStringA(" MyStrust::StartIn error");
}
}
void MyStrust::MoveInMap(DWORD x, DWORD y)
{
try {
DWORD nEcx = *(DWORD*)0x1AEB6E4;
__asm {
pushad
push 0x30
mov ecx, nEcx
mov eax, 0x1127D60
call eax
// 这里的0x2它是地图x的数据
push x
mov ecx, nEcx
mov eax, 0x1128550
call eax
// 这里的0x1它是地图y的数据
push y
mov ecx, nEcx
mov eax, 0x1128550
call eax
// 入参没用
push 0x0
mov ecx, nEcx
mov eax, 0x11285B0
call eax
// 入参没用
push 0x0
mov ecx, nEcx
mov eax, 0x11285B0
call eax
// 入参没用
push 0x0
mov ecx, nEcx
mov eax, 0x1128550
call eax
// 入参没用
push 0x0
mov ecx, nEcx
mov eax, 0x1128580
call eax
// 入参没用
push 0x0
mov ecx, nEcx
mov eax, 0x1128580
call eax
// 入参没用
push 0x0
mov ecx, nEcx
mov eax, 0x1128580
call eax
// 入参没用
push 0x0
mov ecx, nEcx
mov eax, 0x1128580
call eax
// 入参没用
push 0x0
mov ecx, nEcx
mov eax, 0x1128580
call eax
// 入参没用
push 0x0
mov ecx, nEcx
mov eax, 0x11285B0
call eax
// 入参没用
push 0x0
mov ecx, nEcx
mov eax, 0x11285B0
call eax
// 入参没用
push 0x0
mov ecx, nEcx
mov eax, 0x11285B0
call eax
// 入参没用
push 0x0
mov ecx, nEcx
mov eax, 0x11285B0
call eax
// 入参没用
push 0x0
mov ecx, nEcx
mov eax, 0x1128580
call eax
// 入参没用
push 0x0
mov ecx, nEcx
mov eax, 0x11285B0
call eax
mov ecx, nEcx
mov eax, 0x1127EC0
call eax
popad
}
}catch (...) {
OutputDebugStringA(" MyStrust::StartIn error");
}
}
void MyStrust::ComeBackTwon()
{
try {
DWORD nEcx = *(DWORD*)0x1AEB6E4;
__asm {
pushad
mov ecx, 0x1A3CBF4
mov ecx, [ecx]
push 0x138D
mov eax, 0x4E9740
call eax
popad
}
}
catch (...) {
OutputDebugStringA(" MyStrust::ComeBackTwon error");
}
}
MyStrust.h文件的内容:新加 MoveInMap函数声明、ComeBackTwon函数声明
#pragma once
struct Myself {
DWORD Blood;// 血量
FLOAT X; // x坐标
FLOAT Y; // y坐标
wchar_t* Name; // 名字
};
struct Master {
DWORD Object[100];
DWORD Blood[100];
FLOAT x[100];
FLOAT y[100];
wchar_t* Name[100];
DWORD type[100];
};
class MyStrust
{
public:
Myself My;
Master masterNum;
void InitMy();// 玩家角色基本信息(血量、名字、坐标)
void UseObject(DWORD object); // 修改血量
void ChangeBlooad(int v); // 使用物品
void NewChangeBlooad(DWORD nEcx);
void FindMaster();// 遍历怪物
void AllKill(); // 吸怪加秒杀
void StartIn();// 秒进副本
void MoveInMap(DWORD x, DWORD y); // 副本内瞬移
void ComeBackTwon(); // 返回城镇
};
上方的代码不全,只有手写的代码
完整代码:以 49.x86游戏实战-C++实现秒进图 它的代码为基础进行修改
链接:https://pan.baidu.com/s/1W-JpUcGOWbSJmMdmtMzYZg?pwd=q9n5
提取码:q9n5
复制这段内容后打开百度网盘手机App,操作更方便哦