重大发现_理论可以获取所有类的虚表,定位一些特殊函数等 - 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 函数