重大发现_理论可以获取所有类的虚表,定位一些特殊函数等 - cngege/Mod GitHub Wiki

首先确定你要找哪个虚表地址

比如我要找 ClientInstance的虚表 首先我肯定没办法通过BDS符号找到对应的地方

  • 第一步我们要先获取Win10基岩版 ida的伪c代码, 并按下 Ctrl+F5 保存单文件
  • 然后我们通过ClientInstance 的地址值 获取它的虚表*(uintptr_t**)ClientInstance
  • 比如这里是:7FF71BAE7AE8
  • 好,我们复制这个值,打开CE,进入内存视图,(或者直接减去基址)
  • 右键前往地址,粘贴:7FF71BAE7AE8 回车
  • 这样我们就能看到偏移,比如:Minecraft.Windows.exe + 5007AE8
  • 我们复制 5007AE8
  • 进入伪c代码文件
  • 查找:(如果不能找到就把第一个5去掉,但一般不用)
  • 正常就能找到比如类似 off_145007AE8 这样的值
  • 我们看这是在哪个函数中,比如:_QWORD *__fastcall sub_140379B00(__int64 a1)
  • 我们复制 379B00 然后在前面加上 Minecraft.Windows.exe + 变成: Minecraft.Windows.exe + 379B00
  • 复制,再次来到 CE的内存视图,进入这个地址:往下找,
  • 记得虚表地址 是7FF71BAE7AE8,偏移写法是 Minecraft.Windows.exe + 5007AE8
  • 我们往下看哪个是 lea xxx,[Minecraft.Windows.exe + 5007AE8]
  • 就能用特征码定位 虚表地址了

定位函数

简单概括:

  • 部分基岩版客户端函数很难找到,比如Actor::getMovementProxy 这个函数,这个函数既不在虚表中,
  • 新版本中也没有其他虚表函数有明显的调用
  • 这点从BDS源码可以看到
_QWORD *__fastcall Actor::getMovementProxy(__int64 a1, _QWORD *a2)
{
  __int64 v4; // rcx
  _QWORD *v5; // rax
  __int64 *MovementProxy; // rax
  volatile signed __int32 *v7; // rbx
  __int64 v8; // rcx
  volatile signed __int32 *v9; // rbx
  char v11[8]; // [rsp+28h] [rbp-20h] BYREF
  volatile signed __int32 *v12; // [rsp+30h] [rbp-18h]
  _QWORD *v13; // [rsp+58h] [rbp+10h] BYREF

  v13 = a2;
  v4 = **(_QWORD **)(a1 + 8);
  LODWORD(v13) = *(_DWORD *)(a1 + 16);
  v5 = (_QWORD *)entt::basic_registry<EntityId,std::allocator<EntityId>>::try_get<ActorMovementProxyComponent>(v4, &v13);
  MovementProxy = (__int64 *)ActorMovementProxyComponent::getMovementProxy(v5, (__int64)v11);
  v7 = (volatile signed __int32 *)MovementProxy[1];
  v8 = *MovementProxy;
  *MovementProxy = 0i64;
  MovementProxy[1] = 0i64;
  *a2 = 0i64;
  a2[1] = 0i64;
  if ( v7 )
    _InterlockedIncrement(v7 + 2);
  *a2 = v8;
  a2[1] = v7;
  if ( !v8 )
    gsl::details::terminate(0i64);
  if ( v7 )
  {
    if ( _InterlockedExchangeAdd(v7 + 2, 0xFFFFFFFF) == 1 )
    {
      (**(void (__fastcall ***)(volatile signed __int32 *))v7)(v7);
      if ( _InterlockedExchangeAdd(v7 + 3, 0xFFFFFFFF) == 1 )
        (*(void (__fastcall **)(volatile signed __int32 *))(*(_QWORD *)v7 + 8i64))(v7);
    }
  }
  v9 = v12;
  if ( v12 )
  {
    if ( _InterlockedExchangeAdd(v12 + 2, 0xFFFFFFFF) == 1 )
    {
      (**(void (__fastcall ***)(volatile signed __int32 *))v9)(v9);
      if ( _InterlockedExchangeAdd(v9 + 3, 0xFFFFFFFF) == 1 )
        (*(void (__fastcall **)(volatile signed __int32 *))(*(_QWORD *)v9 + 8i64))(v9);
    }
  }
  return a2;
}

从这个代码中没发现明显的 具有特征的地方 但我们看这个代码的前几行 有条:

entt::basic_registry<EntityId,std::allocator<EntityId>>::try_get<ActorMovementProxyComponent>

如果经常看C源码,就能知道,这个函数内部会调用一个函数,这个函数有个参数具有唯一数值 我们进入这个函数看看

__int64 __fastcall entt::basic_registry<EntityId,std::allocator<EntityId>>::try_get<ActorMovementProxyComponent>(
        __int64 a1,
        _DWORD *a2)
{
  _QWORD *v3; // r10
  unsigned __int64 v4; // rax
  __int64 v5; // r9
  __int64 v6; // rax
  _DWORD *v7; // rdx

  v3 = (_QWORD *)entt::basic_registry<EntityId,std::allocator<EntityId>>::assure<ActorMovementProxyComponent>(
                   a1,
                   -323154920);
  v4 = (unsigned __int64)(*a2 & 0x3FFFF) >> 11;
  v5 = v3[1];
  if ( v4 < (v3[2] - v5) >> 3
    && (v6 = *(_QWORD *)(v5 + 8 * v4)) != 0
    && (v7 = (_DWORD *)(v6 + 4i64 * (*a2 & 0x7FF))) != 0i64
    && (*v7 ^ *a2 & 0xFFFC0000) < 0x3FFFF )
  {
    return *(_QWORD *)(v3[9] + 8 * ((unsigned __int64)(*v7 & 0x3FFFF) >> 7)) + 16i64 * (*v7 & 0x7F);
  }
  else
  {
    return 0i64;
  }
}

看到第 13 行的-323154920了吧, 这个数基本不会出现在其他的函数中 所以他出现的地方,要么是这个函数的内部,要么就是在其他函数中内联了这个函数,但总有一个地方在这个函数的内部 我们复制“-323154920);” 进入客户端的伪C源码 全局查找 直到找到一个和BDS源码中这个函数相似的地方 尤其是最后是 return 0 的 多比较比较,就能确定

__int64 __fastcall sub_1407C1BF0(__int64 a1, _DWORD *a2)
{
  _QWORD *v3; // r10
  unsigned __int64 v4; // rax
  __int64 v5; // r9
  __int64 v6; // rax
  _DWORD *v7; // rdx

  v3 = (_QWORD *)sub_1407C1C80(a1, -323154920);
  v4 = (unsigned __int64)(*a2 & 0x3FFFF) >> 11;
  v5 = v3[1];
  if ( v4 < (v3[2] - v5) >> 3
    && (v6 = *(_QWORD *)(v5 + 8 * v4)) != 0
    && (v7 = (_DWORD *)(v6 + 4i64 * (*a2 & 0x7FF))) != 0i64
    && (*v7 ^ *a2 & 0xFFFC0000) < 0x3FFFF )
  {
    return *(_QWORD *)(v3[9] + 8 * ((unsigned __int64)(*v7 & 0x3FFFF) >> 7)) + 16i64 * (*v7 & 0x7F);
  }
  else
  {
    return 0i64;
  }
}

找到了之后,复制这个函数的名字:sub_1407C1BF0,找调用 直到找到一个 和 Actor::getMovementProxy 非常相似的函数 就这样我们确定了 客户端中的 Actor::getMovementProxy 函数

⚠️ **GitHub.com Fallback** ⚠️