标识的作用?
我们如何区分 ActorArray
中的 Actor
是敌人、队友、自己、物品还是其他东西?比较通用的做法是对比多个 Actor
的结构,找出不同类型的 Actor
在同一偏移,数值的异同规律。
比如在 Actor
下 0x555
偏移,类型为 4 Bytes
,是敌人的时候这个数值为0,是队友的时候数值为1,是自己的时候数值为2,不是人的时候数值为3。这样就可以区分出人和其他物品,并且可以区分出人里面的敌人、队友和自己。这就是找标识的作用。
由于我测试的游戏是一个单机游戏,相对来说是比较容易对比 Actor
结构中的数据。
找出区分的标识
先遍历 ActorArray
并绘制所有 Actor
的地址,然后观察绘制出来的地址。发现自己身上有多个地址,这是因为身上的装备也是 Actor
。
我发现在人物播放闲置动画或下蹲的时候,不会移动的是人物的地址,会移动的是物品的地址。(只是刚好这个游戏是这样的)
还有一种区分方式是把自己身上的所有地址都添加到 CE 地址列表,然后挨个浏览内存区域,类型改为 float
,将附近的 1.0
数值改为其他数值检查人物是否有加速效果。如果有加速效果,则说明这地址是人物的地址。这是 UE4 引擎结构的原因,加速控制的数值就是 Actor
下面很靠前的偏移,类型是 float
。
注意:有些第一人称游戏,看不到自己身上的一些地址,可以通过调整坐标,让地址显示出来。比如调整自己的 x
坐标增加 50-200
,再转屏幕坐标。这样就可以把本该绘制在自己身上的地址绘制在自己旁边。
使用自己的人物地址、多个敌人的人物地址、物品的地址去结构分析,找到能够区分他们的条件。
观察自己的人物数值、敌人的人物数值、物品数值。
自己的人物数值和所有敌人的人物数值相同,但于物品的数值不同,则可用来区分人物和物品。
自己的人物数值、所有敌人的人物数值、物品的数值都不同,可以用来区分是自己还是敌人。
记录偏移和数值,尽量记录更多的数据。然后重启游戏并 CE 重新附加进程,根据记录的偏移和数值去对比当前数据,保留未变动的。这个步骤可以重复多次操作,来确保记录的偏移和数值不会发生变动,才可以用来做区分类型的判断条件。
如果自己的人物和敌人的人物数值没变,物品的变了。可能是不同的物品,也可能是我们的物品地址多次操作填的不是同一个位置的。
人物和模型区分
50 1-人物 0-模型-----------没变
54 4-人物 0-模型-----------没变
84 258-人物 2-模型-----------没变
B8 512-人物 0-模型-----------没变
138 3-人物 1-模型 float-----------没变
154 4-人物 0-模型-----------没变
298 10-人物 2-模型-----------没变
29C 24-人物 4-模型-----------没变
2A0 1023-人物 3-模型-----------没变
2B8 10-人物 2-模型-----------没变
2C8 2-人物 1-模型-----------没变
2D8 16-人物 1-模型-----------没变
308 6-人物 2-模型-----------没变
30C 24-人物 4-模型-----------没变
自己和敌人区分
150 3-自己 1-敌人 0-模型
最终选择一个或多个条件来区分判断。
网络游戏
在网络游戏中,敌人和队友可不会站桩等你去对比数据,所以可以将想要对比的 Actor
地址挨个结构分析并保存数值到本地 txt 文本文件,然后再用其他文本对比工具来对比数据。比如免费开源的 WinMerge。
我们可以在代码中实现拷贝 Actor
地址到剪贴板,从而实现快速收集需要分析的结构。
比如我可以像自瞄那样计算目标到准星的距离,将距离准星距离最新的 Actor
在我们按下某个快捷键的时候绘制出特殊的文字(文字更大、颜色和其他不一样之类的),并且使用拷贝这个地址到剪贴板。
UE4更好的区分标识办法
在 UE4 中,我们除了通过上面说的找不同 Actor
的异同标识来区分类型外,还有一种更精准并且更容易的方式。
首先需要熟悉一个 UE4 的固定结构,也就是 UWorld -> GameInstance -> LocalPlayer -> PlayerController -> APawn
,而 APawn
则和 ActorArray
中某个 Actor
是同一个对象,也就是玩家自己的 Actor
。
有了这个结构,我们就可以在遍历 ActorArray
的时候,判断和 APawn
地址相同的 Actor
就是玩家自己。
然后怎么区分敌人和队友,还有其他 Actor
?
我们还需要知道一个概念,也就是 UE4 中的 GName
,我们可以通过 Actor
偏移 0x18
得到一个 ID,然后根据 GName
基址和固定算法计算出 Actor
的蓝图类名。再结合玩家自己的 Actor
的蓝图类名,来区分敌我和其他 Actor
。
GName查找和算法
字符串搜索 MulticastDelegateProperty
,搜到结果后,查看内存浏览器,切换到字节类型显示。然后像上翻,查找顶部有没有 ....*.None
开头,如果是则鼠标放 *
上,得到一个地址,加入地址列表里:
对这个地址搜索16进制8字节,得到一个静态地址:
这个静态地址减去 0x10
,就是 Gname
入口地址:
UE4.23后读取Name
std::string getBpCName(uintptr_t base)
{
int objId = Memory::get().read<int>(base + 0x18);
UCHAR tableLocaltion = (UINT)(int)(objId >> 16);
ULONG rowLocaltion = (USHORT)objId;
int pRowLocation = rowLocaltion;
uintptr_t gNameTable = "GName基址";
uintptr_t tableLocationAddress = Memory::get().read<uintptr_t>(gNameTable + 0x10 + tableLocaltion * 8);
tableLocationAddress += 2 * pRowLocation;
USHORT sLength = Memory::get().read<USHORT>(tableLocationAddress);
sLength = sLength >> 6;
if (sLength < 128)
{
char buffer[128] = { '\0' };
for (int i = 0; i < sLength; i++)
{
buffer[i] = Memory::get().read<char>(tableLocationAddress + 2 + i);
}
std::string name = buffer;
name.resize(sLength);
return name;
}
return "None";
}
读出每个 Actor
的蓝图类名后,像绘制基址那样绘制出来,观察并区分即可。
谢谢大佬
用易语言咋写name啊