mumble/plugins/se/engine.h
Davide Beatrici 501072ef50 FEAT(client): add Source Engine plugin, retract Left 4 Dead 1 & 2 plugins
5df2bb2c0b explains how we gain access to the game's interfaces.

Once we have access to the interfaces, we retrieve the address of the variables' we're interested in by reading the assembly of one or more functions accessing them.

The process for each function is documented in the code.
2020-09-27 20:12:17 +02:00

184 lines
6.4 KiB
C

// Copyright 2019-2020 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_ENGINE_
#define MUMBLE_MUMBLE_PLUGIN_SE_ENGINE_
struct NetInfo {
uint32_t type;
uint8_t ip[4];
uint16_t port;
};
static procptr_t getLocalClient(const procptr_t engineClient) {
// We use GetBaseLocalClient() instead of GetLocalClient() because we just need the main client.
// GetLocalClient() gets the client from an array at the index passed to the function.
// There are multiple clients because of the split screen feature.
const auto GetNetChannelInfo = getVirtualFunction(engineClient, 74);
// Windows:
// E8 ?? ?? ?? ?? call GetBaseLocalClient
// 8B 40 ?? mov eax, [eax+?]
// C3 retn
//
// Linux:
// 55 push ebp
// 89 E5 mov ebp, esp
// 83 EC 08 sub esp, 8
// E8 ?? ?? ?? ?? call GetBaseLocalClient
// 8B 40 ?? mov eax, [eax+?]
// C9 leave
// C3 retn
const auto callTarget = peekProc< int32_t >(GetNetChannelInfo + (isWin32 ? 1 : 7));
const auto callInstructionEnd = GetNetChannelInfo + (isWin32 ? 5 : 11);
const auto GetBaseLocalClient = callInstructionEnd + callTarget;
// Windows:
// A1 ?? ?? ?? ?? mov eax, dword_????????
// 83 C0 ?? add eax, ?
// C3 retn
if (isWin32) {
return peekProcPtr(peekProc< uint32_t >(GetBaseLocalClient + 1)) + peekProc< int8_t >(GetBaseLocalClient + 7);
}
// Linux:
// 55 push ebp
// 89 E5 mov ebp, esp
// 83 EC 18 sub esp, 18h
// C7 44 24 04 00 00 00 00 mov dword ptr [esp+4], 0
// C7 04 24 ?? ?? ?? ?? mov dword ptr [esp], offset dword_????????
// E8 ?? ?? ?? ?? call sub_????????
// C9 leave
// C3 retn
//
// The function is quite different on Linux. It returns the result of an unknown function:
//
// 55 push ebp
// 89 E5 mov ebp, esp
// 53 push ebx
// 83 EC 14 sub esp, 14h
// 8B 45 0C mov eax, [ebp+arg_4]
// 8B 5D 08 mov ebx, [ebp+arg_0]
// 83 F8 FF cmp eax, 0FFFFFFFFh
// 74 0E jz short loc_1
// 8B 44 83 04 mov eax, [ebx+eax*4+4]
// 83 C0 04 add eax, 4
//
// loc_0:
// 83 C4 14 add esp, 14h
// 5B pop ebx
// 5D pop ebp
// C3 retn
//
// 90 align 10h
//
// loc_1:
// 8B 03 mov eax, [ebx]
// 89 1C 24 mov [esp], ebx
// FF 50 14 call dword ptr [eax+14h]
// 8B 44 83 04 mov eax, [ebx+eax*4+4]
// 83 C0 04 add eax, 4
// EB E8 jmp short loc_0
//
// Its purpose seem to be to iterate over the clients array, which is done directly by GetBaseLocalClient() and
// GetLocalClient() on Windows.
return peekProcPtr(peekProc< uint32_t >(GetBaseLocalClient + 17) + 4) + 4;
}
static int8_t getSignOnStateOffset(const procptr_t engineClient) {
const auto IsInGame = getVirtualFunction(engineClient, 26);
// Windows:
// E8 ?? ?? ?? ?? call GetBaseLocalClient
// 33 C9 xor ecx, ecx
// 83 78 ?? 06 cmp dword ptr [eax+?], 6
// 0F 94 C0 setz al
// C3 retn
//
// Linux:
// 55 push ebp
// 89 E5 mov ebp, esp
// 83 EC 08 sub esp, 8
// E8 ?? ?? ?? ?? call GetBaseLocalClient
// 83 78 ?? 06 cmp dword ptr [eax+?], 6
// C9 leave
// 0F 94 C0 setz al
// C3 retn
return peekProc< int8_t >(IsInGame + (isWin32 ? 9 : 13));
}
static int32_t getLevelNameOffset(const procptr_t engineClient) {
const auto GetLevelNameShort = getVirtualFunction(engineClient, 53);
// Windows:
// ...
//
// E8 ?? ?? ?? ?? call GetBaseLocalClient
// 05 ?? ?? ?? ?? add eax, ?
// C3 retn
//
// Linux:
// ...
//
// E8 ?? ?? ?? ?? call GetBaseLocalClient
// C9 leave
// 05 ?? ?? ?? ?? add eax, ?
// C3 retn
if (isWin32) {
if (peekProc< uint8_t >(GetLevelNameShort + 37) == 0x05) {
// Left 4 Dead
return peekProc< int32_t >(GetLevelNameShort + 38);
} else {
return peekProc< int32_t >(GetLevelNameShort + 40);
}
}
return peekProc< int32_t >(GetLevelNameShort + 57);
}
static int32_t getNetInfoOffset(const procptr_t localClient, const procptr_t engineClient) {
const auto GetNetChannelInfo = getVirtualFunction(engineClient, 74);
// Windows:
// E8 ?? ?? ?? ?? call GetBaseLocalClient
// 8B 40 ?? mov eax, [eax+?]
// C3 retn
//
// Linux:
// 55 push ebp
// 89 E5 mov ebp, esp
// 83 EC 08 sub esp, 8
// E8 ?? ?? ?? ?? call GetBaseLocalClient
// 8B 40 ?? mov eax, [eax+?]
// C9 leave
// C3 retn
const auto NetChannelInfo = peekProcPtr(localClient + peekProc< int8_t >(GetNetChannelInfo + (isWin32 ? 7 : 13)));
const auto GetAddress = getVirtualFunction(NetChannelInfo, 1);
// Windows:
// 6A 00 push 0
// 81 C1 ?? ?? ?? ?? add ecx, ?
// E8 C3 9D 1D 00 call ToString
// C3 retn
//
// Linux:
// 55 push ebp
// 89 E5 mov ebp, esp
// 83 EC ?? sub esp, ?
// 8B 45 08 mov eax, [ebp+arg_0]
// C7 44 24 04 00 00 00 00 mov dword ptr [esp+4], 0
// 05 ?? ?? ?? ?? add eax, ?
// 89 04 24 mov [esp], eax
// E8 ?? ?? ?? ?? call ToString
// C9 leave
// C3 retn
const auto netInfo = NetChannelInfo + peekProc< int32_t >(GetAddress + (isWin32 ? 4 : 18));
return static_cast< int32_t >(netInfo - localClient);
}
#endif