40.x86游戏实战-找出XXX遍历周围的类型

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!

本次游戏没法给

内容参考于:微尘网络安全

工具下载:

链接:https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd=6tw3

提取码:6tw3

复制这段内容后打开百度网盘手机App,操作更方便哦

上一个内容:39.x86游戏实战-c++代码遍历周围

上一个内容把附近的东西给遍历了出来,里面有怪物有附近建筑物有我们玩家角色,然后怪物肯定都是一个类型、角色肯定是一个类型、建筑物是一个类型,正常来说怪物、建筑物、角色会在某个偏移上的数据不同,然后怪物与怪物在某个偏移上相同,现在找的东西的特点就是怪物与怪物相同,怪物与建筑物、玩家角色不同的数据,然后找类型的方式,把怪物对象(对象指的是基址)、人物对象、附近建筑物对象从偏移0到偏移100、500的复制到文本文档中,详情看下方操作

首先写下图红框里的内容,把各种对象打印出来

写完上图红框的内容重新生成,然后以管理员打开OD并附加到游戏,然后以管理员打开Dbgview.exe,然后以管理员打开wctool.exe并注入刚刚重新生成玩的dll

然后 Dbgview.exe记得设置过滤关键字

然后遍历出了对象(地址)

然后记住下图红框的地址也就是0xCC91C00,然后来到OD

然后在OD跳转过去,如下图

然后从0一直选中到0x500多,然后复制,选中的方式首先鼠标点击偏移0位置的数据,然后往下滑到0x500偏移左右,然后按着键盘上的shift键,然后鼠标单击偏移0x500位置就可以选中了

然后粘贴到文本文档中

然后根据上方的步骤再找两个怪物的,在找我们玩家角色的,然后如下图红框,发现偏移0x90位置建筑物是0x21,怪物是0x211,玩家角色是0x111,这样就找出了类型

上方类型在对象的第一层,而有些游戏这种数据并不在第一层,然后还有些游戏偏移0x500并找不到类型,这怎么办?如果0x500找不到类型那就扩展到0x1000、0x2000这么大的一个范围还找不到,在去下一层找,下一层找的意思是,看下图红框,这些都是内存地址,找下一层也就是挨个取它们的值,把它们的值当做偏移0然后再从偏移0到偏移0x500、0x1000、0x2000这样挨个对比

找下一层也就是再写一个循环,如下图红框,如找下一层的下一层还是

找下一层的下一层,就写如下图红框的代码,下一层的下一层的下一层也是同理,继续往里添加for(循环)

MyStrust.cpp文件的修改:修改了 FindMaster函数

#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"人物姓名=%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, "x=%f;y=%f", this->My.X, this->My.Y);
	OutputDebugStringA(buf);

	// [[0x1AB7CDC]+0x36A0]血量
	this->My.Blood = *(int*)(addressValue + 0x36A0);
	// 拼接文字,%d表示拼接一个整数(32位的整数)
	sprintf(buf, "血量 = %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::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是随便写的
		{
			Tmp1 = (wchar_t*)ReadDword(ReadDword(Tmp + i * 4) + 0x258); // 0x258位置是名字
			if (Tmp1 == 0) {// 如果是0说明没有名字
				call_logW(L"wetool:NULL\n");
			}
			else {
				/*
					打印名字到 Dbgview.exe,其中 wetool这个是为了过滤 Dbgview.exe打印的一些跟我们不相关的日志

				*/ 
				call_logW(L"wetool:%ws\n", Tmp1);
			}
			
			call_logW(L"wetool:第%d个对象:%x\n",i, ReadDword(Tmp + i * 4));
		}
	}
	else {
		call_loaA("wetool:Tmp error");
	}
}

img

  • 12
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值