[Proxy] Cache method params, so we don't have to disassemble everytime

This commit is contained in:
momo5502 2017-02-22 09:32:24 +01:00
parent f5d5e76ac1
commit 7f8f73e39e
2 changed files with 81 additions and 102 deletions

View File

@ -28,63 +28,46 @@ namespace Steam
std::function<Proxy::SteamFreeLastCallbackFn> Proxy::SteamFreeLastCallback;
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())
{
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<void*, uint16_t> Interface::lookupMethod(std::string method)
{
if (::Utils::Memory::IsBadReadPtr(this->interfacePtr)) return nullptr;
unsigned char** vftbl = *static_cast<unsigned char***>(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<unsigned char***>(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<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)
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, &params) && 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_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<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)
@ -147,56 +147,38 @@ namespace Steam
char* modId = "IW4x";
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"));
// 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<uint32_t>("GetUniqueLocalAppId");
// if (clientApps.invoke<bool>("SetLocalAppConfig", uniqueId, str.data(), static_cast<uint32_t>(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<void>("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<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");
}
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");
}
}

View File

@ -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<sizeof(Args)...>::value;
if(passedArgc != argc)
{
@ -161,7 +161,7 @@ namespace Steam
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
@ -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<std::size_t ...>
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))> {};
void* interfacePtr;
std::unordered_map<std::string, void*> 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<std::string, std::pair<void*, uint16_t>> methodCache;
std::pair<void*, uint16_t> getMethod(std::string method);
std::pair<void*, uint16_t> lookupMethod(std::string method);
bool getMethodData(unsigned char* methodPtr, std::string* name, uint16_t* params);
};
class KeyValuesBuilder