UE4找Actor区分标识

/ 0评 / 1

标识的作用?

我们如何区分 ActorArray 中的 Actor 是敌人、队友、自己、物品还是其他东西?比较通用的做法是对比多个 Actor 的结构,找出不同类型的 Actor 在同一偏移,数值的异同规律。

比如在 Actor0x555 偏移,类型为 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 的蓝图类名后,像绘制基址那样绘制出来,观察并区分即可。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注