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..67dd1ee 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,36 @@ typedef unsigned char uchar; -VTable* sharedHooker; -VTable* clientHooker; +VMTHook* luaSharedVMTHook; +VMTHook* luaClientVMTHook; using namespace GarrysMod; Lua::ILuaBase *MENU; -void *clientState; +lua_State *clientState; -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) { 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); +lua_State * __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 +53,29 @@ 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); + return luaSharedVMTHook->GetOriginalFunction(CLOSELUAINTERFACE)(thisptr, state); } -class CLuaInterface -{ -private: - template - inline t get(unsigned short which) - { - return t((*(char ***)(this))[which]); - } - -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 - } - -}; - -int RunOnClient(lua_State* state) +int RunOnClient(lua_State *state) { + LUA->CheckType(1, GarrysMod::Lua::Type::STRING); + if (!clientState) LUA->ThrowError("Not in game"); - reinterpret_cast(clientState)->RunStringEx(LUA->CheckString(-3), LUA->CheckString(-2), LUA->CheckString()); + luaClientVMTHook->GetOriginalFunction(RUNSTRINGEX)(clientState, "", "", LUA->CheckString(1), true, true, true, true); return 0; } @@ -100,15 +85,14 @@ GMOD_MODULE_OPEN() MENU = LUA; 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);