mirror of
https://github.com/mumble-voip/mumble.git
synced 2025-10-26 11:19:16 +00:00
FEAT(client): add getExportedSymbol() function for positional audio plugins
This function is of critical importance for the Source Engine plugin, let me elaborate on why. Most games consist in an executable and maybe one or more libraries, but they don't have particular exported symbols we can use to easily access (existing) resources inside the process, which means we have to rely on hardcoded offsets and hex pattern scanning. Source Engine games are special: the executable is nothing more than a manager that takes care of loading the core libraries, which are engine(.dll|.so) and client(.dll|.so) (or server(.dll|.so) in the case of a dedicated server). Those libraries have a common exported symbol, which is CreateInterface(). The function takes the interface's name as argument (char *), creates the interface object if it doesn't exist yet and returns a pointer (void *) to it. The interfaces objects are stored in a list called s_pInterfaceList, which is usually an exported symbol on Linux. getExportedSymbol() allows us to get the address to CreateInterface() (different for each loaded library) inside the process and read the function's assembly code in order to reach s_pInterfaceList. If s_pInterfaceList is exported we can get its address with getExportedSymbol(), without the need of CreateInterface().
This commit is contained in:
parent
d8c4b82efe
commit
5df2bb2c0b
@ -11,6 +11,7 @@
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <elf.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <math.h>
|
||||
@ -212,6 +213,107 @@ static inline bool peekProc(const procptr_t &addr, void *dest, const size_t &len
|
||||
return (nread != -1 && static_cast< size_t >(nread) == in.iov_len);
|
||||
}
|
||||
|
||||
static inline procptr_t getExportedSymbol(const std::string &symbol, const procptr_t module) {
|
||||
if (isWin32) {
|
||||
return getWin32ExportedSymbol(symbol, module);
|
||||
}
|
||||
|
||||
procptr_t hashTable = 0;
|
||||
procptr_t strTable = 0;
|
||||
procptr_t symTable = 0;
|
||||
|
||||
if (is64Bit) {
|
||||
const auto ehdr = peekProc< Elf64_Ehdr >(module);
|
||||
const auto phdr = peekProcVector< Elf64_Phdr >(module + ehdr.e_phoff, ehdr.e_phnum);
|
||||
|
||||
for (size_t i = 0; i < phdr.size(); ++i) {
|
||||
if (phdr[i].p_type == PT_DYNAMIC) {
|
||||
const auto dyn =
|
||||
peekProcVector< Elf64_Dyn >(module + phdr[i].p_vaddr, phdr[i].p_memsz / sizeof(Elf64_Dyn));
|
||||
|
||||
for (size_t j = 0; j < dyn.size(); ++j) {
|
||||
switch (dyn[j].d_tag) {
|
||||
case DT_HASH:
|
||||
hashTable = dyn[j].d_un.d_ptr;
|
||||
break;
|
||||
case DT_STRTAB:
|
||||
strTable = dyn[j].d_un.d_ptr;
|
||||
break;
|
||||
case DT_SYMTAB:
|
||||
symTable = dyn[j].d_un.d_ptr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hashTable && strTable && symTable) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const auto ehdr = peekProc< Elf32_Ehdr >(module);
|
||||
const auto phdr = peekProcVector< Elf32_Phdr >(module + ehdr.e_phoff, ehdr.e_phnum);
|
||||
|
||||
for (size_t i = 0; i < phdr.size(); ++i) {
|
||||
if (phdr[i].p_type == PT_DYNAMIC) {
|
||||
const auto dyn =
|
||||
peekProcVector< Elf32_Dyn >(module + phdr[i].p_vaddr, phdr[i].p_memsz / sizeof(Elf32_Dyn));
|
||||
|
||||
for (size_t j = 0; j < dyn.size(); ++j) {
|
||||
switch (dyn[j].d_tag) {
|
||||
case DT_HASH:
|
||||
hashTable = dyn[j].d_un.d_ptr;
|
||||
break;
|
||||
case DT_STRTAB:
|
||||
strTable = dyn[j].d_un.d_ptr;
|
||||
break;
|
||||
case DT_SYMTAB:
|
||||
symTable = dyn[j].d_un.d_ptr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hashTable && strTable && symTable) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hash table pseudo-struct:
|
||||
// uint32_t nBucket;
|
||||
// uint32_t nChain;
|
||||
// uint32_t bucket[nBucket];
|
||||
// uint32_t chain[nChain];
|
||||
const auto nChain = peekProc< uint32_t >(hashTable + sizeof(uint32_t));
|
||||
|
||||
if (is64Bit) {
|
||||
for (uint32_t i = 0; i < nChain; ++i) {
|
||||
const auto sym = peekProc< Elf64_Sym >(symTable + sizeof(Elf64_Sym) * i);
|
||||
const auto name = peekProcString(strTable + sym.st_name, symbol.size());
|
||||
|
||||
if (name == symbol) {
|
||||
return module + sym.st_value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0; i < nChain; ++i) {
|
||||
const auto sym = peekProc< Elf32_Sym >(symTable + sizeof(Elf32_Sym) * i);
|
||||
const auto name = peekProcString(strTable + sym.st_name, symbol.size());
|
||||
|
||||
if (name == symbol) {
|
||||
return module + sym.st_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void generic_unlock() {
|
||||
pModule = 0;
|
||||
pPid = 0;
|
||||
|
||||
@ -151,6 +151,52 @@ static inline procptr_t getVirtualFunction(const procptr_t classObject, const si
|
||||
return peekProcPtr(vTable + (index * GET_POINTER_SIZE));
|
||||
}
|
||||
|
||||
#ifdef OS_WINDOWS
|
||||
static inline procptr_t getExportedSymbol(const std::string &symbol, const procptr_t module) {
|
||||
#else
|
||||
/// We use a different name because the function is called by the Linux version
|
||||
/// of getExportedSymbol() in case the process is running through Wine.
|
||||
static inline procptr_t getWin32ExportedSymbol(const std::string &symbol, const procptr_t module) {
|
||||
#endif
|
||||
const auto dos = peekProc< ImageDosHeader >(module);
|
||||
if (!(dos.magic[0] == 'M' && dos.magic[1] == 'Z')) {
|
||||
// Invalid DOS signature
|
||||
return -1;
|
||||
}
|
||||
|
||||
procptr_t dataAddress;
|
||||
|
||||
if (is64Bit) {
|
||||
const auto nt = peekProc< ImageNtHeaders64 >(module + dos.addressOfNtHeader);
|
||||
dataAddress = nt.optionalHeader.dataDirectory[0].virtualAddress;
|
||||
} else {
|
||||
const auto nt = peekProc< ImageNtHeaders32 >(module + dos.addressOfNtHeader);
|
||||
dataAddress = nt.optionalHeader.dataDirectory[0].virtualAddress;
|
||||
}
|
||||
|
||||
if (!dataAddress) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto exportDir = peekProc< ImageExportDirectory >(module + dataAddress);
|
||||
|
||||
const auto funcs = peekProcVector< uint32_t >(module + exportDir.addressOfFunctions, exportDir.numberOfFunctions);
|
||||
const auto names = peekProcVector< uint32_t >(module + exportDir.addressOfNames, exportDir.numberOfNames);
|
||||
const auto ords = peekProcVector< uint16_t >(module + exportDir.addressOfNameOrdinals, exportDir.numberOfNames);
|
||||
|
||||
for (uint32_t i = 0; i < exportDir.numberOfNames; ++i) {
|
||||
if (names[i]) {
|
||||
const auto name = peekProcString(module + names[i], symbol.size());
|
||||
|
||||
if (name == symbol) {
|
||||
return module + funcs[ords[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This function returns:
|
||||
// -1 in case of failure.
|
||||
// 0 if the process is 32-bit.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user