From 87fa2917df3d7f99077c08fb7bbf2ce8d4eb87c4 Mon Sep 17 00:00:00 2001 From: Alexander Sagen Date: Sun, 25 Feb 2018 16:45:17 +0100 Subject: [PATCH 1/4] Update for latest GMod version --- build.bat | 44 ++++++++++++++++++++++++-- include/vmthook.h | 73 ++++++++++++++++++++++++++++++++++++++++++++ include/vtable.h | 61 ------------------------------------ premake5.lua | 2 +- src/main.cpp | 78 +++++++++++++++++++++-------------------------- 5 files changed, 149 insertions(+), 109 deletions(-) create mode 100644 include/vmthook.h delete mode 100644 include/vtable.h diff --git a/build.bat b/build.bat index ca82559..0e5d37e 100644 --- a/build.bat +++ b/build.bat @@ -1,4 +1,42 @@ -@echo off -echo Generating Windows project... -premake5 vs2017 +:: @echo off + +echo Building Module. +echo Dependencies: GIT, PREMAKE5, VISUAL STUDIO 2015/2013. echo. + +:: Hacky way to ensure we've got a git repository & submodules at our disposal +git init +git submodule update --init --recursive +echo. + +:: Attempt to load VS2017 +CALL "%SYSTEMDRIVE%\PROGRA~2\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat" 2>nul >nul + +:: Fire up a Visual Studio command-line environment and generate our solution +IF DEFINED VS150COMNTOOLS ( + echo Preparing for build with Visual Studio 2017. + premake5 vs2017 +) ELSE IF DEFINED VS140COMNTOOLS ( + echo Preparing for build with Visual Studio 2015. + premake5 vs2015 + CALL "%VS140COMNTOOLS%VSDevCmd.bat" +) ELSE IF DEFINED VS120COMNTOOLS ( + echo Preparing for build with Visual Studio 2013. + premake5 vs2013 + CALL "%VS120COMNTOOLS%VSDevCmd.bat" +) ELSE ( + echo Failed to detect Visual Studio installation. + timeout 5 + exit /B 1 +) + +echo. + +for /R %%X in (*.sln) do ( + msbuild %%X +) + +:: We're done! +timeout 5 + +exit /B %ERRORLEVEL% diff --git a/include/vmthook.h b/include/vmthook.h new file mode 100644 index 0000000..6893e14 --- /dev/null +++ b/include/vmthook.h @@ -0,0 +1,73 @@ +#pragma once + +// thanks https://github.com/aixxe/vmthook + +#include +#include +#include +#include + +class VMTHook +{ + private: + std::uintptr_t **baseclass = nullptr; + std::unique_ptr current_vft = nullptr; + std::uintptr_t *original_vft = nullptr; + std::size_t total_functions = 0; + + public: + VMTHook(void) = default; + + VMTHook(void *baseclass) + { + this->baseclass = static_cast(baseclass); + + while (static_cast(*this->baseclass)[this->total_functions]) + ++this->total_functions; + + const std::size_t table_size = this->total_functions * sizeof(std::uintptr_t); + + this->original_vft = *this->baseclass; + this->current_vft = std::make_unique(this->total_functions); + + std::memcpy(this->current_vft.get(), this->original_vft, table_size); + + *this->baseclass = this->current_vft.get(); + }; + + ~VMTHook() + { + *this->baseclass = this->original_vft; + }; + + template + inline const Function GetOriginalFunction(std::size_t function_index) + { + return reinterpret_cast(this->original_vft[function_index]); + } + + inline bool HookFunction(void *new_function, const std::size_t function_index) + { + if (function_index > this->total_functions) + return false; + + this->current_vft[function_index] = reinterpret_cast(new_function); + + return true; + } + + inline bool UnhookFunction(const std::size_t function_index) + { + if (function_index > this->total_functions) + return false; + + this->current_vft[function_index] = this->original_vft[function_index]; + + return true; + } + + inline std::size_t GetTotalFunctions() + { + return this->total_functions; + } +}; \ No newline at end of file diff --git a/include/vtable.h b/include/vtable.h deleted file mode 100644 index 303ee81..0000000 --- a/include/vtable.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef VTABLE_H -#define VTABLE_H - -#define ushort_max (unsigned short(-1)) - -typedef char *vtindex; // sizeof(pointer) with ability to add numbers and shit -#ifndef offset -#define offset(x,y) ((char *)(x) - (char *)(y)) -#endif - -class VTable -{ -public: - VTable(void *object) - { - original_vt = *(vtindex **)object; - vtindex *last_index = original_vt; - while (*last_index++); - - unsigned int size = offset(last_index, original_vt) / sizeof(*last_index); - - new_vt = new vtindex[size]; - while (--last_index >= original_vt) - new_vt[offset(last_index, original_vt) / sizeof(*last_index)] = *last_index; - - *(vtindex **)object = new_vt; - - hooked = (void **)object; - } - ~VTable() - { - *hooked = original_vt; - delete[] new_vt; - } - - void hook(unsigned short index, void *func) - { - get(index) = (vtindex)func; - } - void unhook(unsigned short index) - { - get(index) = getold(index); - } - - - vtindex &getold(unsigned short index) { return original_vt[index]; } - -private: - vtindex &get (unsigned short index) { return new_vt[index]; } - - -public: - vtindex *original_vt; - vtindex *new_vt; - void **hooked; - -}; - -#undef offset - -#endif // VTABLE_H \ No newline at end of file diff --git a/premake5.lua b/premake5.lua index 0dc20c2..3c0cb67 100644 --- a/premake5.lua +++ b/premake5.lua @@ -14,7 +14,7 @@ solution "gm_roc2" flags "StaticRuntime" kind "SharedLib" - targetdir "C:/Program Files (x86)/Steam/steamapps/common/GarrysMod/garrysmod/lua/bin" + targetdir "bin" buildoptions {"/Os", "/MP", "/arch:SSE2"} diff --git a/src/main.cpp b/src/main.cpp index 72cf4b1..6c4d87e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,5 @@ #include "GarrysMod/Lua/Interface.h" -#include "vtable.h" +#include "vmthook.h" #include "util.h" #include "windows.h" @@ -9,36 +9,40 @@ typedef unsigned char uchar; -VTable* sharedHooker; -VTable* clientHooker; +VMTHook* luaSharedVMTHook; +VMTHook* luaClientVMTHook; using namespace GarrysMod; -Lua::ILuaBase *MENU; -void *clientState; +Lua::ILuaBase* MENU; +lua_State* clientState; +void* rexthisptr; -typedef void *(__thiscall *hRunStringExFn)(void*, char const*, char const*, char const*, bool, bool, bool, bool); -void * __fastcall hRunStringEx(void *_this, void*, char const* filename, char const* path, char const* torun, bool run, bool showerrors, bool idk, bool idk2) +typedef void* (__thiscall *RunStringExHookFn)(void* thisptr, const char* fileName, const char* path, const char* stringToRun, bool run, bool showErrors, bool, bool); +void* __fastcall RunStringExHook(void* thisptr, int edx, const char* fileName, const char* path, const char* stringToRun, bool run, bool showErrors, bool a, bool b) { + if (rexthisptr == nullptr) + rexthisptr = thisptr; + MENU->PushSpecial(Lua::SPECIAL_GLOB); MENU->GetField(-1, "hook"); MENU->GetField(-1, "Call"); MENU->PushString("RunOnClient"); MENU->PushNil(); - MENU->PushString(filename); - MENU->PushString(torun); + MENU->PushString(fileName); + MENU->PushString(stringToRun); MENU->Call(4, 1); if (!MENU->IsType(-1, Lua::Type::NIL)) - torun = MENU->CheckString(); + stringToRun = MENU->CheckString(); MENU->Pop(3); - return hRunStringExFn(clientHooker->getold(RUNSTRINGEX))(_this, filename, path, torun, run, showerrors, idk, idk2); + return luaClientVMTHook->GetOriginalFunction(RUNSTRINGEX)(thisptr, fileName, path, stringToRun, run, showErrors, a, b); } -typedef void *(__thiscall *hCreateLuaInterfaceFn)(void *, uchar, bool); -void * __fastcall hCreateLuaInterface(void *_this, void *, uchar stateType, bool renew) +typedef lua_State* (__thiscall *CreateLuaInterfaceHookFn)(void* thisptr, unsigned char stateType, bool renew); +void* __fastcall CreateLuaInterfaceHook(void* thisptr, int edx, unsigned char stateType, bool renew) { - void *state = hCreateLuaInterfaceFn(sharedHooker->getold(CREATELUAINTERFACE))(_this, stateType, renew); + lua_State* state = luaSharedVMTHook->GetOriginalFunction(CREATELUAINTERFACE)(thisptr, stateType, renew); MENU->PushSpecial(Lua::SPECIAL_GLOB); MENU->GetField(-1, "hook"); @@ -53,44 +57,31 @@ void * __fastcall hCreateLuaInterface(void *_this, void *, uchar stateType, bool clientState = state; - clientHooker = new VTable(clientState); - clientHooker->hook(RUNSTRINGEX, hRunStringEx); + luaClientVMTHook = new VMTHook(state); + luaClientVMTHook->HookFunction(&RunStringExHook, RUNSTRINGEX); return clientState; } -typedef void *(__thiscall *hCloseLuaInterfaceFn)(void*, void*); -void * __fastcall hCloseLuaInterface(void *_this, void *ukwn, void *luaInterface) +typedef void* (__thiscall *CloseLuaInterfaceHookFn)(void* thisptr, void* state); +void* __fastcall CloseLuaInterfaceHook(void* thisptr, int edx, lua_State* state) { - if (luaInterface == clientState) + if (state == clientState) { clientState = NULL; - - return hCloseLuaInterfaceFn(sharedHooker->getold(CLOSELUAINTERFACE))(_this, luaInterface); -} - -class CLuaInterface -{ -private: - template - inline t get(unsigned short which) - { - return t((*(char ***)(this))[which]); + rexthisptr = nullptr; } -public: - void RunStringEx(char const* filename, char const* path, char const* torun, bool run = true, bool showerrors = true, bool idk = true, bool idk2 = true) - { - return get(RUNSTRINGEX)(this, filename, path, torun, run, showerrors, idk, idk2); //free cookies for people that know how to detect stuff - } - -}; + return luaSharedVMTHook->GetOriginalFunction(CLOSELUAINTERFACE)(thisptr, state); +} int RunOnClient(lua_State* state) { - if (!clientState) + LUA->CheckType(1, GarrysMod::Lua::Type::STRING); + + if (!clientState || rexthisptr == nullptr) LUA->ThrowError("Not in game"); - reinterpret_cast(clientState)->RunStringEx(LUA->CheckString(-3), LUA->CheckString(-2), LUA->CheckString()); + luaClientVMTHook->GetOriginalFunction(RUNSTRINGEX)(rexthisptr, "", "", LUA->CheckString(1), true, true, false, false); return 0; } @@ -99,16 +90,15 @@ GMOD_MODULE_OPEN() { MENU = LUA; - auto luaShared = util::GetInterfaceSingle("lua_shared.dll", "LUASHARED003"); + auto luaShared = util::GetInterfaceSingle("lua_shared.dll", "LUASHARED003"); if (!luaShared) MessageBoxA(NULL, "gay", "gay", NULL); - sharedHooker = new VTable(luaShared); + luaSharedVMTHook = new VMTHook(luaShared); + luaSharedVMTHook->HookFunction(&CreateLuaInterfaceHook, CREATELUAINTERFACE); + luaSharedVMTHook->HookFunction(&CloseLuaInterfaceHook, CLOSELUAINTERFACE); - sharedHooker->hook(CREATELUAINTERFACE, hCreateLuaInterface); - sharedHooker->hook(CLOSELUAINTERFACE, hCloseLuaInterface); - LUA->PushSpecial(Lua::SPECIAL_GLOB); LUA->PushString("RunOnClient"); LUA->PushCFunction(RunOnClient); From cafe8ede1d59bf11f29a7cae1f6a0b849f843c74 Mon Sep 17 00:00:00 2001 From: Alexander Sagen Date: Sun, 25 Feb 2018 16:54:17 +0100 Subject: [PATCH 2/4] Make RunOnClient catch errors in passed code --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 6c4d87e..fd1b235 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -81,7 +81,7 @@ int RunOnClient(lua_State* state) if (!clientState || rexthisptr == nullptr) LUA->ThrowError("Not in game"); - luaClientVMTHook->GetOriginalFunction(RUNSTRINGEX)(rexthisptr, "", "", LUA->CheckString(1), true, true, false, false); + luaClientVMTHook->GetOriginalFunction(RUNSTRINGEX)(rexthisptr, "", "", LUA->CheckString(1), true, true, true, true); return 0; } From 484179562aefa45cef380d5cadb23316253df587 Mon Sep 17 00:00:00 2001 From: Alexander Sagen Date: Sun, 25 Feb 2018 17:01:53 +0100 Subject: [PATCH 3/4] Remove redundant client state pointer --- src/main.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index fd1b235..3207f28 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,14 +16,10 @@ using namespace GarrysMod; Lua::ILuaBase* MENU; lua_State* clientState; -void* rexthisptr; typedef void* (__thiscall *RunStringExHookFn)(void* thisptr, const char* fileName, const char* path, const char* stringToRun, bool run, bool showErrors, bool, bool); void* __fastcall RunStringExHook(void* thisptr, int edx, const char* fileName, const char* path, const char* stringToRun, bool run, bool showErrors, bool a, bool b) { - if (rexthisptr == nullptr) - rexthisptr = thisptr; - MENU->PushSpecial(Lua::SPECIAL_GLOB); MENU->GetField(-1, "hook"); MENU->GetField(-1, "Call"); @@ -66,10 +62,8 @@ void* __fastcall CreateLuaInterfaceHook(void* thisptr, int edx, unsigned char st typedef void* (__thiscall *CloseLuaInterfaceHookFn)(void* thisptr, void* state); void* __fastcall CloseLuaInterfaceHook(void* thisptr, int edx, lua_State* state) { - if (state == clientState) { + if (state == clientState) clientState = NULL; - rexthisptr = nullptr; - } return luaSharedVMTHook->GetOriginalFunction(CLOSELUAINTERFACE)(thisptr, state); } @@ -78,10 +72,10 @@ int RunOnClient(lua_State* state) { LUA->CheckType(1, GarrysMod::Lua::Type::STRING); - if (!clientState || rexthisptr == nullptr) + if (!clientState) LUA->ThrowError("Not in game"); - luaClientVMTHook->GetOriginalFunction(RUNSTRINGEX)(rexthisptr, "", "", LUA->CheckString(1), true, true, true, true); + luaClientVMTHook->GetOriginalFunction(RUNSTRINGEX)(clientState, "", "", LUA->CheckString(1), true, true, true, true); return 0; } From 5a845199024a430541d9190d8c81266e1f411be8 Mon Sep 17 00:00:00 2001 From: Alexander Sagen Date: Sun, 25 Feb 2018 17:35:04 +0100 Subject: [PATCH 4/4] Make Lenny happy again --- src/main.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 3207f28..67dd1ee 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,11 +14,11 @@ VMTHook* luaClientVMTHook; using namespace GarrysMod; -Lua::ILuaBase* MENU; -lua_State* clientState; +Lua::ILuaBase *MENU; +lua_State *clientState; -typedef void* (__thiscall *RunStringExHookFn)(void* thisptr, const char* fileName, const char* path, const char* stringToRun, bool run, bool showErrors, bool, bool); -void* __fastcall RunStringExHook(void* thisptr, int edx, const char* fileName, const char* path, const char* stringToRun, bool run, bool showErrors, bool a, bool b) +typedef void *(__thiscall *RunStringExHookFn)(void *thisptr, const char *fileName, const char *path, const char *stringToRun, bool run, bool showErrors, bool, bool); +void * __fastcall RunStringExHook(void *thisptr, int edx, const char *fileName, const char *path, const char *stringToRun, bool run, bool showErrors, bool a, bool b) { MENU->PushSpecial(Lua::SPECIAL_GLOB); MENU->GetField(-1, "hook"); @@ -35,10 +35,10 @@ void* __fastcall RunStringExHook(void* thisptr, int edx, const char* fileName, c return luaClientVMTHook->GetOriginalFunction(RUNSTRINGEX)(thisptr, fileName, path, stringToRun, run, showErrors, a, b); } -typedef lua_State* (__thiscall *CreateLuaInterfaceHookFn)(void* thisptr, unsigned char stateType, bool renew); -void* __fastcall CreateLuaInterfaceHook(void* thisptr, int edx, unsigned char stateType, bool renew) +typedef lua_State *(__thiscall *CreateLuaInterfaceHookFn)(void *thisptr, unsigned char stateType, bool renew); +lua_State * __fastcall CreateLuaInterfaceHook(void *thisptr, int edx, unsigned char stateType, bool renew) { - lua_State* state = luaSharedVMTHook->GetOriginalFunction(CREATELUAINTERFACE)(thisptr, stateType, renew); + lua_State *state = luaSharedVMTHook->GetOriginalFunction(CREATELUAINTERFACE)(thisptr, stateType, renew); MENU->PushSpecial(Lua::SPECIAL_GLOB); MENU->GetField(-1, "hook"); @@ -59,8 +59,8 @@ void* __fastcall CreateLuaInterfaceHook(void* thisptr, int edx, unsigned char st return clientState; } -typedef void* (__thiscall *CloseLuaInterfaceHookFn)(void* thisptr, void* state); -void* __fastcall CloseLuaInterfaceHook(void* thisptr, int edx, lua_State* state) +typedef void *(__thiscall *CloseLuaInterfaceHookFn)(void *thisptr, void *state); +void * __fastcall CloseLuaInterfaceHook(void *thisptr, int edx, lua_State *state) { if (state == clientState) clientState = NULL; @@ -68,7 +68,7 @@ void* __fastcall CloseLuaInterfaceHook(void* thisptr, int edx, lua_State* state) return luaSharedVMTHook->GetOriginalFunction(CLOSELUAINTERFACE)(thisptr, state); } -int RunOnClient(lua_State* state) +int RunOnClient(lua_State *state) { LUA->CheckType(1, GarrysMod::Lua::Type::STRING); @@ -84,8 +84,8 @@ GMOD_MODULE_OPEN() { MENU = LUA; - auto luaShared = util::GetInterfaceSingle("lua_shared.dll", "LUASHARED003"); - + auto luaShared = util::GetInterfaceSingle("lua_shared.dll", "LUASHARED003"); + if (!luaShared) MessageBoxA(NULL, "gay", "gay", NULL);