[Proxy] Cache method params, so we don't have to disassemble everytime
This commit is contained in:
parent
f5d5e76ac1
commit
7f8f73e39e
@ -28,63 +28,46 @@ namespace Steam
|
|||||||
std::function<Proxy::SteamFreeLastCallbackFn> Proxy::SteamFreeLastCallback;
|
std::function<Proxy::SteamFreeLastCallbackFn> Proxy::SteamFreeLastCallback;
|
||||||
std::function<Proxy::SteamGetAPICallResultFn> Proxy::SteamGetAPICallResult;
|
std::function<Proxy::SteamGetAPICallResultFn> Proxy::SteamGetAPICallResult;
|
||||||
|
|
||||||
void* Interface::getMethod(std::string method)
|
std::pair<void*, uint16_t> Interface::getMethod(std::string method)
|
||||||
{
|
{
|
||||||
if(this->methodCache.find(method) != this->methodCache.end())
|
if(this->methodCache.find(method) != this->methodCache.end())
|
||||||
{
|
{
|
||||||
return this->methodCache[method];
|
return this->methodCache[method];
|
||||||
}
|
}
|
||||||
|
|
||||||
void* methodPtr = Interface::lookupMethod(method);
|
auto methodData = Interface::lookupMethod(method);
|
||||||
this->methodCache[method] = methodPtr;
|
this->methodCache[method] = methodData;
|
||||||
return methodPtr;
|
return methodData;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* Interface::lookupMethod(std::string method)
|
std::pair<void*, uint16_t> Interface::lookupMethod(std::string method)
|
||||||
{
|
{
|
||||||
if (::Utils::Memory::IsBadReadPtr(this->interfacePtr)) return nullptr;
|
if (!::Utils::Memory::IsBadReadPtr(this->interfacePtr))
|
||||||
unsigned char** vftbl = *static_cast<unsigned char***>(this->interfacePtr);
|
|
||||||
|
|
||||||
while (!::Utils::Memory::IsBadReadPtr(vftbl) && !::Utils::Memory::IsBadCodePtr((FARPROC(*vftbl))))
|
|
||||||
{
|
{
|
||||||
if(this->getMethodName(*vftbl) == method) return *vftbl;
|
unsigned char** vftbl = *static_cast<unsigned char***>(this->interfacePtr);
|
||||||
++vftbl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
while (!::Utils::Memory::IsBadReadPtr(vftbl) && !::Utils::Memory::IsBadCodePtr((FARPROC(*vftbl))))
|
||||||
}
|
|
||||||
|
|
||||||
size_t Interface::getMethodParamSize(void* method)
|
|
||||||
{
|
|
||||||
if (::Utils::Memory::IsBadCodePtr(method)) return 0;
|
|
||||||
|
|
||||||
ud_t ud;
|
|
||||||
ud_init(&ud);
|
|
||||||
ud_set_mode(&ud, 32);
|
|
||||||
ud_set_pc(&ud, reinterpret_cast<uint64_t>(method));
|
|
||||||
ud_set_input_buffer(&ud, reinterpret_cast<uint8_t*>(method), INT32_MAX);
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
ud_disassemble(&ud);
|
|
||||||
if (ud_insn_mnemonic(&ud) == UD_Iret)
|
|
||||||
{
|
{
|
||||||
auto operand = ud_insn_opr(&ud, 0);
|
std::string name;
|
||||||
if (operand->type == UD_OP_IMM && operand->size == 16)
|
uint16_t params;
|
||||||
|
|
||||||
|
if (this->getMethodData(*vftbl, &name, ¶ms) && name == method)
|
||||||
{
|
{
|
||||||
return static_cast<size_t>(operand->lval.uword);
|
return{ *vftbl, params };
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
++vftbl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return { nullptr, 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Interface::getMethodName(unsigned char* methodPtr)
|
bool Interface::getMethodData(unsigned char* methodPtr, std::string* name, uint16_t* params)
|
||||||
{
|
{
|
||||||
if (::Utils::Memory::IsBadCodePtr(methodPtr)) return "";
|
name->clear();
|
||||||
|
*params = 0;
|
||||||
|
if (::Utils::Memory::IsBadCodePtr(methodPtr)) return false;
|
||||||
|
|
||||||
ud_t ud;
|
ud_t ud;
|
||||||
ud_init(&ud);
|
ud_init(&ud);
|
||||||
@ -95,20 +78,37 @@ namespace Steam
|
|||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
ud_disassemble(&ud);
|
ud_disassemble(&ud);
|
||||||
if (ud_insn_mnemonic(&ud) == UD_Iret) break;
|
|
||||||
|
|
||||||
if (ud_insn_mnemonic(&ud) == UD_Ipush)
|
if (ud_insn_mnemonic(&ud) == UD_Iret)
|
||||||
|
{
|
||||||
|
const ud_operand* operand = ud_insn_opr(&ud, 0);
|
||||||
|
if (!operand) break;
|
||||||
|
|
||||||
|
if (operand->type == UD_OP_IMM && operand->size == 16)
|
||||||
|
{
|
||||||
|
*params = operand->lval.uword;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ud_insn_mnemonic(&ud) == UD_Ipush && name->empty())
|
||||||
{
|
{
|
||||||
auto operand = ud_insn_opr(&ud, 0);
|
auto operand = ud_insn_opr(&ud, 0);
|
||||||
if (operand->type == UD_OP_IMM && operand->size == 32)
|
if (operand->type == UD_OP_IMM && operand->size == 32)
|
||||||
{
|
{
|
||||||
char* operandPtr = reinterpret_cast<char*>(operand->lval.udword);
|
char* operandPtr = reinterpret_cast<char*>(operand->lval.udword);
|
||||||
if (!::Utils::Memory::IsBadReadPtr(operandPtr)) return operandPtr;
|
if (!::Utils::Memory::IsBadReadPtr(operandPtr))
|
||||||
|
{
|
||||||
|
name->clear();
|
||||||
|
name->append(operandPtr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Proxy::SetGame(uint32_t appId)
|
void Proxy::SetGame(uint32_t appId)
|
||||||
@ -147,56 +147,38 @@ namespace Steam
|
|||||||
char* modId = "IW4x";
|
char* modId = "IW4x";
|
||||||
gameID.modID = *reinterpret_cast<unsigned int*>(modId) | 0x80000000;
|
gameID.modID = *reinterpret_cast<unsigned int*>(modId) | 0x80000000;
|
||||||
|
|
||||||
|
Interface clientUtils(Proxy::ClientEngine->GetIClientUtils(Proxy::SteamPipe, "CLIENTUTILS_INTERFACE_VERSION001"));
|
||||||
|
clientUtils.invoke<void>("SetAppIDForCurrentPipe", Proxy::AppId, false);
|
||||||
|
|
||||||
// Interface clientApps(Proxy::ClientEngine->GetIClientApps(Proxy::SteamUser, Proxy::SteamPipe, "CLIENTAPPS_INTERFACE_VERSION001"));
|
char ourPath[MAX_PATH] = { 0 };
|
||||||
// Interface clientShortcuts(Proxy::ClientEngine->GetIClientShortcuts(Proxy::SteamUser, Proxy::SteamPipe, "CLIENTSHORTCUTS_INTERFACE_VERSION001"));
|
GetModuleFileNameA(GetModuleHandle(nullptr), ourPath, sizeof(ourPath));
|
||||||
// if (!clientApps || !clientShortcuts) return;
|
|
||||||
//
|
char ourDirectory[MAX_PATH] = { 0 };
|
||||||
// KeyValuesBuilder builder;
|
GetCurrentDirectoryA(sizeof(ourDirectory), ourDirectory);
|
||||||
// builder.packString("name", mod.data());
|
|
||||||
// builder.packUint64("gameid", gameID.bits);
|
std::string cmdline = ::Utils::String::VA("\"%s\" -proc %d", ourPath, GetCurrentProcessId());
|
||||||
// builder.packString("installed", "1");
|
|
||||||
// builder.packString("gamedir", "IW4x");
|
// As of 02/19/2017, the SpawnProcess method doesn't require the app id anymore,
|
||||||
// builder.packString("serverbrowsername", "IW4x");
|
// but only for those who participate in the beta.
|
||||||
// builder.packEnd();
|
// Therefore we have to check how many bytes the method expects as arguments
|
||||||
//
|
// and adapt our call accordingly!
|
||||||
// std::string str = builder.getString();
|
size_t expectedParams = Proxy::ClientUser.paramSize("SpawnProcess");
|
||||||
// uint32_t uniqueId = clientShortcuts.invoke<uint32_t>("GetUniqueLocalAppId");
|
if (expectedParams == 40) // Release
|
||||||
// if (clientApps.invoke<bool>("SetLocalAppConfig", uniqueId, str.data(), static_cast<uint32_t>(str.size())))
|
|
||||||
{
|
{
|
||||||
Interface clientUtils(Proxy::ClientEngine->GetIClientUtils(Proxy::SteamPipe, "CLIENTUTILS_INTERFACE_VERSION001"));
|
Proxy::ClientUser.invoke<bool>("SpawnProcess", ourPath, cmdline.data(), 0, ourDirectory, gameID.bits, Proxy::AppId, mod.data(), 0, 0);
|
||||||
clientUtils.invoke<void>("SetAppIDForCurrentPipe", Proxy::AppId, false);
|
}
|
||||||
|
else if (expectedParams == 36) // Beta
|
||||||
char ourPath[MAX_PATH] = { 0 };
|
{
|
||||||
GetModuleFileNameA(GetModuleHandle(nullptr), ourPath, sizeof(ourPath));
|
Proxy::ClientUser.invoke<bool>("SpawnProcess", ourPath, cmdline.data(), 0, ourDirectory, gameID.bits, mod.data(), 0, 0);
|
||||||
|
}
|
||||||
char ourDirectory[MAX_PATH] = { 0 };
|
else if (expectedParams == 48) // Legacy, expects VAC blob
|
||||||
GetCurrentDirectoryA(sizeof(ourDirectory), ourDirectory);
|
{
|
||||||
|
char blob[8] = { 0 };
|
||||||
std::string cmdline = ::Utils::String::VA("\"%s\" -proc %d", ourPath, GetCurrentProcessId());
|
Proxy::ClientUser.invoke<bool>("SpawnProcess", blob, 0, ourPath, cmdline.data(), 0, ourDirectory, gameID.bits, Proxy::AppId, mod.data(), 0, 0);
|
||||||
|
}
|
||||||
// As of 02/19/2017, the SpawnProcess method doesn't require the app id anymore,
|
else
|
||||||
// but only for those who participate in the beta.
|
{
|
||||||
// Therefore we have to check how many bytes the method expects as arguments
|
OutputDebugStringA("Steam proxy was unable to match the arguments for SpawnProcess!\n");
|
||||||
// and adapt our call accordingly!
|
|
||||||
size_t expectedParams = Proxy::ClientUser.paramSize("SpawnProcess");
|
|
||||||
if(expectedParams == 40) // Release
|
|
||||||
{
|
|
||||||
Proxy::ClientUser.invoke<bool>("SpawnProcess", ourPath, cmdline.data(), 0, ourDirectory, gameID.bits, Proxy::AppId, mod.data(), 0, 0);
|
|
||||||
}
|
|
||||||
else if(expectedParams == 36) // Beta
|
|
||||||
{
|
|
||||||
Proxy::ClientUser.invoke<bool>("SpawnProcess", ourPath, cmdline.data(), 0, ourDirectory, gameID.bits, mod.data(), 0, 0);
|
|
||||||
}
|
|
||||||
else if (expectedParams == 48) // Legacy, expects VAC blob
|
|
||||||
{
|
|
||||||
char blob[8] = { 0 };
|
|
||||||
Proxy::ClientUser.invoke<bool>("SpawnProcess", blob, 0, ourPath, cmdline.data(), 0, ourDirectory, gameID.bits, Proxy::AppId, mod.data(), 0, 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OutputDebugStringA("Steam proxy was unable to match the arguments for SpawnProcess!\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,14 +146,14 @@ namespace Steam
|
|||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
|
|
||||||
void* method = this->getMethod(methodName);
|
auto method = this->getMethod(methodName);
|
||||||
if (!method)
|
if (!method.first)
|
||||||
{
|
{
|
||||||
OutputDebugStringA(::Utils::String::VA("Steam interface method %s not found!\n", methodName.data()));
|
OutputDebugStringA(::Utils::String::VA("Steam interface method %s not found!\n", methodName.data()));
|
||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t argc = this->getMethodParamSize(method);
|
size_t argc = method.second;
|
||||||
constexpr size_t passedArgc = Interface::AddSizes<sizeof(Args)...>::value;
|
constexpr size_t passedArgc = Interface::AddSizes<sizeof(Args)...>::value;
|
||||||
if(passedArgc != argc)
|
if(passedArgc != argc)
|
||||||
{
|
{
|
||||||
@ -161,7 +161,7 @@ namespace Steam
|
|||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
|
|
||||||
return reinterpret_cast<T(__thiscall*)(void*, Args ...)>(method)(this->interfacePtr, args...);
|
return reinterpret_cast<T(__thiscall*)(void*, Args ...)>(method.first)(this->interfacePtr, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit operator bool() const
|
explicit operator bool() const
|
||||||
@ -171,14 +171,12 @@ namespace Steam
|
|||||||
|
|
||||||
size_t paramSize(std::string methodName)
|
size_t paramSize(std::string methodName)
|
||||||
{
|
{
|
||||||
void* method = this->getMethod(methodName);
|
auto method = this->getMethod(methodName);
|
||||||
if (method) return this->getMethodParamSize(method);
|
return method.second;
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// TODO: Use fold expressions in C++17 once available
|
// TODO: Use fold expressions once available (C++17)
|
||||||
template<std::size_t ...>
|
template<std::size_t ...>
|
||||||
struct AddSizes : std::integral_constant<std::size_t, 0> {};
|
struct AddSizes : std::integral_constant<std::size_t, 0> {};
|
||||||
|
|
||||||
@ -187,11 +185,10 @@ namespace Steam
|
|||||||
struct AddSizes<X, Xs...> : std::integral_constant<std::size_t, X + ((AddSizes<Xs...>::value + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1))> {};
|
struct AddSizes<X, Xs...> : std::integral_constant<std::size_t, X + ((AddSizes<Xs...>::value + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1))> {};
|
||||||
|
|
||||||
void* interfacePtr;
|
void* interfacePtr;
|
||||||
std::unordered_map<std::string, void*> methodCache;
|
std::unordered_map<std::string, std::pair<void*, uint16_t>> methodCache;
|
||||||
void* getMethod(std::string method);
|
std::pair<void*, uint16_t> getMethod(std::string method);
|
||||||
void* lookupMethod(std::string method);
|
std::pair<void*, uint16_t> lookupMethod(std::string method);
|
||||||
size_t getMethodParamSize(void* method);
|
bool getMethodData(unsigned char* methodPtr, std::string* name, uint16_t* params);
|
||||||
std::string getMethodName(unsigned char* methodPtr);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class KeyValuesBuilder
|
class KeyValuesBuilder
|
||||||
|
Loading…
Reference in New Issue
Block a user