From 7f8f73e39e9ebd97d6849804a92dbbaba99e5f78 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Wed, 22 Feb 2017 09:32:24 +0100 Subject: [PATCH] [Proxy] Cache method params, so we don't have to disassemble everytime --- src/Steam/Proxy.cpp | 158 ++++++++++++++++++++------------------------ src/Steam/Proxy.hpp | 25 +++---- 2 files changed, 81 insertions(+), 102 deletions(-) diff --git a/src/Steam/Proxy.cpp b/src/Steam/Proxy.cpp index a61ac86e..124cf253 100644 --- a/src/Steam/Proxy.cpp +++ b/src/Steam/Proxy.cpp @@ -28,63 +28,46 @@ namespace Steam std::function Proxy::SteamFreeLastCallback; std::function Proxy::SteamGetAPICallResult; - void* Interface::getMethod(std::string method) + std::pair Interface::getMethod(std::string method) { if(this->methodCache.find(method) != this->methodCache.end()) { return this->methodCache[method]; } - void* methodPtr = Interface::lookupMethod(method); - this->methodCache[method] = methodPtr; - return methodPtr; + auto methodData = Interface::lookupMethod(method); + this->methodCache[method] = methodData; + return methodData; } - void* Interface::lookupMethod(std::string method) + std::pair Interface::lookupMethod(std::string method) { - if (::Utils::Memory::IsBadReadPtr(this->interfacePtr)) return nullptr; - unsigned char** vftbl = *static_cast(this->interfacePtr); - - while (!::Utils::Memory::IsBadReadPtr(vftbl) && !::Utils::Memory::IsBadCodePtr((FARPROC(*vftbl)))) + if (!::Utils::Memory::IsBadReadPtr(this->interfacePtr)) { - if(this->getMethodName(*vftbl) == method) return *vftbl; - ++vftbl; - } + unsigned char** vftbl = *static_cast(this->interfacePtr); - return nullptr; - } - - 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(method)); - ud_set_input_buffer(&ud, reinterpret_cast(method), INT32_MAX); - - while (true) - { - ud_disassemble(&ud); - if (ud_insn_mnemonic(&ud) == UD_Iret) + while (!::Utils::Memory::IsBadReadPtr(vftbl) && !::Utils::Memory::IsBadCodePtr((FARPROC(*vftbl)))) { - auto operand = ud_insn_opr(&ud, 0); - if (operand->type == UD_OP_IMM && operand->size == 16) + std::string name; + uint16_t params; + + if (this->getMethodData(*vftbl, &name, ¶ms) && name == method) { - return static_cast(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_init(&ud); @@ -95,20 +78,37 @@ namespace Steam while (true) { 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); if (operand->type == UD_OP_IMM && operand->size == 32) { char* operandPtr = reinterpret_cast(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) @@ -147,56 +147,38 @@ namespace Steam char* modId = "IW4x"; gameID.modID = *reinterpret_cast(modId) | 0x80000000; + Interface clientUtils(Proxy::ClientEngine->GetIClientUtils(Proxy::SteamPipe, "CLIENTUTILS_INTERFACE_VERSION001")); + clientUtils.invoke("SetAppIDForCurrentPipe", Proxy::AppId, false); -// Interface clientApps(Proxy::ClientEngine->GetIClientApps(Proxy::SteamUser, Proxy::SteamPipe, "CLIENTAPPS_INTERFACE_VERSION001")); -// Interface clientShortcuts(Proxy::ClientEngine->GetIClientShortcuts(Proxy::SteamUser, Proxy::SteamPipe, "CLIENTSHORTCUTS_INTERFACE_VERSION001")); -// if (!clientApps || !clientShortcuts) return; -// -// KeyValuesBuilder builder; -// builder.packString("name", mod.data()); -// builder.packUint64("gameid", gameID.bits); -// builder.packString("installed", "1"); -// builder.packString("gamedir", "IW4x"); -// builder.packString("serverbrowsername", "IW4x"); -// builder.packEnd(); -// -// std::string str = builder.getString(); -// uint32_t uniqueId = clientShortcuts.invoke("GetUniqueLocalAppId"); -// if (clientApps.invoke("SetLocalAppConfig", uniqueId, str.data(), static_cast(str.size()))) + char ourPath[MAX_PATH] = { 0 }; + GetModuleFileNameA(GetModuleHandle(nullptr), ourPath, sizeof(ourPath)); + + char ourDirectory[MAX_PATH] = { 0 }; + GetCurrentDirectoryA(sizeof(ourDirectory), ourDirectory); + + std::string cmdline = ::Utils::String::VA("\"%s\" -proc %d", ourPath, GetCurrentProcessId()); + + // As of 02/19/2017, the SpawnProcess method doesn't require the app id anymore, + // but only for those who participate in the beta. + // Therefore we have to check how many bytes the method expects as arguments + // and adapt our call accordingly! + size_t expectedParams = Proxy::ClientUser.paramSize("SpawnProcess"); + if (expectedParams == 40) // Release { - Interface clientUtils(Proxy::ClientEngine->GetIClientUtils(Proxy::SteamPipe, "CLIENTUTILS_INTERFACE_VERSION001")); - clientUtils.invoke("SetAppIDForCurrentPipe", Proxy::AppId, false); - - char ourPath[MAX_PATH] = { 0 }; - GetModuleFileNameA(GetModuleHandle(nullptr), ourPath, sizeof(ourPath)); - - char ourDirectory[MAX_PATH] = { 0 }; - GetCurrentDirectoryA(sizeof(ourDirectory), ourDirectory); - - std::string cmdline = ::Utils::String::VA("\"%s\" -proc %d", ourPath, GetCurrentProcessId()); - - // As of 02/19/2017, the SpawnProcess method doesn't require the app id anymore, - // but only for those who participate in the beta. - // Therefore we have to check how many bytes the method expects as arguments - // and adapt our call accordingly! - size_t expectedParams = Proxy::ClientUser.paramSize("SpawnProcess"); - if(expectedParams == 40) // Release - { - Proxy::ClientUser.invoke("SpawnProcess", ourPath, cmdline.data(), 0, ourDirectory, gameID.bits, Proxy::AppId, mod.data(), 0, 0); - } - else if(expectedParams == 36) // Beta - { - Proxy::ClientUser.invoke("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("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"); - } + Proxy::ClientUser.invoke("SpawnProcess", ourPath, cmdline.data(), 0, ourDirectory, gameID.bits, Proxy::AppId, mod.data(), 0, 0); + } + else if (expectedParams == 36) // Beta + { + Proxy::ClientUser.invoke("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("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"); } } diff --git a/src/Steam/Proxy.hpp b/src/Steam/Proxy.hpp index 1c177541..2159384d 100644 --- a/src/Steam/Proxy.hpp +++ b/src/Steam/Proxy.hpp @@ -146,14 +146,14 @@ namespace Steam return T(); } - void* method = this->getMethod(methodName); - if (!method) + auto method = this->getMethod(methodName); + if (!method.first) { OutputDebugStringA(::Utils::String::VA("Steam interface method %s not found!\n", methodName.data())); return T(); } - size_t argc = this->getMethodParamSize(method); + size_t argc = method.second; constexpr size_t passedArgc = Interface::AddSizes::value; if(passedArgc != argc) { @@ -161,7 +161,7 @@ namespace Steam return T(); } - return reinterpret_cast(method)(this->interfacePtr, args...); + return reinterpret_cast(method.first)(this->interfacePtr, args...); } explicit operator bool() const @@ -171,14 +171,12 @@ namespace Steam size_t paramSize(std::string methodName) { - void* method = this->getMethod(methodName); - if (method) return this->getMethodParamSize(method); - - return 0; + auto method = this->getMethod(methodName); + return method.second; } private: - // TODO: Use fold expressions in C++17 once available + // TODO: Use fold expressions once available (C++17) template struct AddSizes : std::integral_constant {}; @@ -187,11 +185,10 @@ namespace Steam struct AddSizes : std::integral_constant::value + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1))> {}; void* interfacePtr; - std::unordered_map methodCache; - void* getMethod(std::string method); - void* lookupMethod(std::string method); - size_t getMethodParamSize(void* method); - std::string getMethodName(unsigned char* methodPtr); + std::unordered_map> methodCache; + std::pair getMethod(std::string method); + std::pair lookupMethod(std::string method); + bool getMethodData(unsigned char* methodPtr, std::string* name, uint16_t* params); }; class KeyValuesBuilder