mumble-voip_mumble/plugins/se/client.h

182 lines
5.5 KiB
C++

// Copyright 2020-2023 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
#ifndef MUMBLE_MUMBLE_PLUGIN_SE_CLIENT_
#define MUMBLE_MUMBLE_PLUGIN_SE_CLIENT_
struct TypeDescription {
uint32_t fieldType;
uint32_t fieldName;
int32_t fieldOffset;
uint16_t fieldSize;
int16_t flags;
uint32_t externalName;
uint32_t saveRestoreOps;
uint32_t inputFunc;
uint32_t td;
int32_t fieldSizeInBytes;
uint32_t overrideField;
int32_t overrideCount;
float fieldTolerance;
int32_t flatOffset[2];
uint16_t flatGroup;
};
struct DataMap {
uint32_t dataDesc;
int32_t dataNumFields;
uint32_t dataClassName;
uint32_t baseMap;
int32_t nPackedSize;
uint32_t optimizedDataMap;
};
struct EntityCacheInfo {
uint32_t networkable;
uint16_t baseEntitiesIndex;
uint16_t isDormant;
};
static int32_t getDataVar(const std::string &name, procptr_t predMap) {
while (predMap) {
DataMap dataMap;
if (!proc->peek(predMap, dataMap)) {
return 0;
}
// The structure is 4 bytes bigger on Linux.
const size_t realStructSize = isWin32 ? sizeof(TypeDescription) : sizeof(TypeDescription) + 4;
const auto descs = proc->peekVector< TypeDescription >(dataMap.dataDesc, dataMap.dataNumFields, realStructSize);
for (const auto &desc : descs) {
if (!desc.fieldName) {
continue;
}
const auto fieldName = proc->peekString(desc.fieldName);
if (fieldName == name) {
return desc.fieldOffset;
}
if (desc.td) {
const auto offset = getDataVar(name, desc.td);
if (offset) {
return offset;
}
}
}
predMap = dataMap.baseMap;
}
return 0;
}
static int32_t getDataVarFromEntity(const std::string &name, const procptr_t entity) {
procptr_t GetPredDescMap;
// Brute-force virtual function index.
for (uint8_t i = 20; i > 16; --i) {
GetPredDescMap = proc->virtualFunction(entity, i);
if (proc->peek< uint8_t >(GetPredDescMap + (isWin32 ? 0 : 1)) == 0xB8) {
break;
}
}
// Windows:
// B8 ?? ?? ?? ?? mov eax, offset dword_????????
// C3 retn
//
// Linux:
// 55 push ebp
// B8 ?? ?? ?? ?? mov eax, offset dword_????????
// 89 E5 mov ebp, esp
// 5D pop ebp
// C3 retn
return getDataVar(name, proc->peek< uint32_t >(GetPredDescMap + (isWin32 ? 1 : 2)));
}
static procptr_t getLocalPlayer(const procptr_t localClient, procptr_t clientEntityList, const procptr_t engineClient) {
const auto GetLocalPlayer = proc->virtualFunction(engineClient, 12);
// Windows:
// 6A FF push 0FFFFFFFFh
// E8 ?? ?? ?? ?? call GetLocalClient
// 8B 80 ?? ?? ?? ?? mov eax, [eax+?]
// 83 C4 04 add esp, 4
// 40 inc eax
// C3 retn
//
// Linux:
// 55 push ebp
// 89 E5 mov ebp, esp
// 83 EC 18 sub esp, 18h
// C7 04 24 FF FF FF FF mov dword ptr [esp], 0FFFFFFFFh
// E8 ?? ?? ?? ?? call GetLocalClient
// 8B 80 ?? ?? ?? ?? mov eax, [eax+?]
// C9 leave
// 83 C0 01 add eax, 1
// C3 retn
const auto localPlayerIndexOffset = proc->peek< int32_t >(GetLocalPlayer + (isWin32 ? 9 : 20));
const auto localPlayerIndex = proc->peek< uint32_t >(localClient + localPlayerIndexOffset) + 1;
auto GetClientNetworkable = proc->virtualFunction(clientEntityList, 0);
// Left 4 Dead:
// 8B 44 24 04 mov eax, [esp+arg_0]
// 8B 44 C1 ?? mov eax, [ecx+eax*8+?]
// C2 04 00 retn 4
// Windows:
// 55 push ebp
// 8B EC mov ebp, esp
// 8B 45 08 mov eax, [ebp+arg_0]
// 3D ?? ?? ?? ?? cmp eax, ?
// 77 08 ja short 08
// 8B 44 C1 ?? mov eax, [ecx+eax*8+?]
// 5D pop ebp
// C2 04 00 retn 4
//
// Linux:
// 81 6C 24 04 ?? ?? ?? ?? sub [esp+arg_0], ????????
// EB ?? jmp short GetClientNetworkable
if (!isWin32) {
clientEntityList -= proc->peek< int32_t >(GetClientNetworkable + 4);
GetClientNetworkable = GetClientNetworkable + 10 + proc->peek< int8_t >(GetClientNetworkable + 9);
// 55 push ebp
// 89 E5 mov ebp, esp
// 8B 45 0C mov eax, [ebp+arg_4]
// 3D ?? ?? ?? ?? cmp eax, ?
// 77 13 ja short 13
// 8B 55 08 mov edx, [ebp+arg_0]
// 5D pop ebp
// 8B 84 C2 ?? ?? ?? ?? mov eax, [edx+eax*8+?]
// C3 retn
}
procptr_t entityCacheInfo;
if (isWin32) {
if (proc->peek< uint8_t >(GetClientNetworkable + 6) == 0xC1) {
// Left 4 Dead
entityCacheInfo = clientEntityList + proc->peek< int8_t >(GetClientNetworkable + 7);
} else {
entityCacheInfo = clientEntityList + proc->peek< int8_t >(GetClientNetworkable + 16);
}
} else {
entityCacheInfo = clientEntityList + proc->peek< int32_t >(GetClientNetworkable + 20);
}
const auto entity = proc->peek< EntityCacheInfo >(entityCacheInfo + sizeof(EntityCacheInfo) * localPlayerIndex);
// We subtract 8 bytes in order to cast from IClientNetworkable to IClientEntity.
return entity.networkable ? (entity.networkable - 8) : 0;
}
#endif