Garry's Mod Wiki

Revision Difference

Binary_Modules_Detouring_Functions#552462

<cat>Dev</cat>⤶ <title>Detouring Functions with Binary Modules</title>⤶ ⤶ # Navigation⤶ - [Some Information](https://wiki.facepunch.com/gmod/Binary_Modules_Deteuring_Functions#someinformation)⤶ - [Getting started](https://wiki.facepunch.com/gmod/Binary_Modules_Deteuring_Functions#gettingstarted)⤶ - [Getting the Signature](https://wiki.facepunch.com/gmod/Binary_Modules_Deteuring_Functions#gettingthesignature)⤶ - [Linux](https://wiki.facepunch.com/gmod/Binary_Modules_Deteuring_Functions#linux)⤶ - [HxD](https://wiki.facepunch.com/gmod/Binary_Modules_Deteuring_Functions#hxdlinux)⤶ - [IDA](https://wiki.facepunch.com/gmod/Binary_Modules_Deteuring_Functions#idalinux)⤶ - [Windows](https://wiki.facepunch.com/gmod/Binary_Modules_Deteuring_Functions#windows)⤶ - [Detouring functions](https://wiki.facepunch.com/gmod/Binary_Modules_Deteuring_Functions#detouringfunctions)⤶ ⤶ # Some Information⤶ This will explain everything for [garrysmod_common](https://github.com/danielga/garrysmod_common). ⤶ This will also explain how to find the signatures with [HxD](https://mh-nexus.de/de/hxd/) and [IDA](https://hex-rays.com/ida-free/). Feel free to decide which one you want to use. ⤶ ⤶ # Getting started⤶ If you don't have already a project, you can create one following [this page](https://wiki.facepunch.com/gmod/Creating_Binary_Modules:_Premake), or you can use a template like [this one](https://github.com/RaphaelIT7/gmod-common-module-base)  ⤶ ⤶ Open your `premake5.lua` file and call these functions after `CreateProject`.  ⤶ ```lua⤶ IncludeHelpersExtended()⤶ IncludeDetouring()⤶ IncludeScanning()⤶ ```⤶ ⤶ This will add the extended helpers, the detouring and scanning submodule, which we'll need for detouring functions.  ⤶ ⤶ ⤶ Your file should look something like this:⤶ ```lua⤶ CreateWorkspace({name = "[Your Project]", abi_compatible = false})⤶ CreateProject({serverside = [true or false], manual_files = false})⤶ [Your includes ...]⤶ IncludeHelpersExtended()⤶ IncludeDetouring()⤶ IncludeScanning()⤶ ``` ⤶ ⤶ # Getting the Signature⤶ This part will explain how to get a signature for the given platform. ⤶ ⤶ ## Linux⤶ First, open one of the .so files you want to search in. ⤶ There may be a _srv.so version of the file you want to search in. If so, use that file. ⤶ ⤶ For this example, we will get the signature for `CServerGameDLL::ShouldHideServer` from the `garrysmod/bin/server_srv.so`. ⤶ ⤶ ### HxD (Linux)⤶ First, open the `server_srv.so` file. ⤶ You can open a file by pressing <key>Ctrl + O</key> or by going onto File->Open. ⤶ ⤶ Now it should look something like this:⤶ <upload src="b04e5/8db94f64eefc60e.png" size="201996" name="image.png" />⤶ ⤶ Now we only need to search for the function we want. ⤶ Open the search box by pressing <key>Ctrl + F</key> or by going onto Search->Search. ⤶ Now enter `ShouldHideServer` and search for it. ⤶ Now, the section containing `ShouldHideServer` is selected. ⤶ <upload src="b04e5/8db94f8454baac0.png" size="20592" name="image.png" />⤶ ⤶ Select everything between the last dot and the next dot, and then we got our signature. ⤶ <upload src="b04e5/8db94f82350bed5.png" size="20534" name="image.png" />⤶ ⤶ And now we got the signature for `CServerGameDLL::ShouldHideServer`. In this case, it is `_ZN14CServerGameDLL16ShouldHideServerEv` ⤶ ⤶ ### IDA (Linux)⤶ First, open the `server_srv.so` file. ⤶ If it's the first time you opened the file, it can take a while for the entire file to be loaded and analyzed. ⤶ After it finished loading, it should look something like this: ⤶ <upload src="b04e5/8db94f971ecd9db.png" size="183968" name="image.png" />⤶ ⤶ Now open the search box by pressing <key>Alt + T</key> or going onto Search->Search. ⤶ Enter `CServerGameDLL::ShouldHideServer` and search. ⤶ <upload src="b04e5/8db94fa412a26e7.png" size="5370" name="image.png" />⤶ ⤶ Now the function should be selected in the left list, and you need to double-click it. ⤶ <upload src="b04e5/8db94fa64b5834d.png" size="49073" name="image.png" />⤶ ⤶ Now it should show us the function and the signature like this. ⤶ <upload src="b04e5/8db94fa77834303.png" size="65429" name="image.png" />⤶ ⤶ And now we got the signature for `CServerGameDLL::ShouldHideServer`. In this case, it is `_ZN14CServerGameDLL16ShouldHideServerEv` ⤶ <upload src="b04e5/8db94fa98d2669b.png" size="15979" name="image.png" />⤶ ⤶ ## Windows⤶ ⤶ ToDo: Learn and Document how to get the signature for windows.⤶ ⤶ # Detouring functions.⤶ ⤶ First, we include everything needed. ⤶ ```cpp⤶ #include <GarrysMod/FactoryLoader.hpp>⤶ #include <scanning/symbolfinder.hpp>⤶ #include <GarrysMod/Symbol.hpp>⤶ #include <detouring/hook.hpp>⤶ ```⤶ ⤶ After that, we declare our `Symbol` like this: ⤶ ```cpp⤶ typedef bool (*ShouldHideServer)();⤶ Detouring::Hook detour_ShouldHideServer;⤶ const std::vector<Symbol> ShouldHideServerSyms = {⤶ Symbol::FromName("_ZN14CServerGameDLL16ShouldHideServerEv"), // Our signature for CServerGameDLL::ShouldHideServer⤶ };⤶ ```⤶ ⤶ Now we only need to declare a function that we want to replace it with.⤶ ```cpp⤶ bool hide_server = true;⤶ bool hook_ShouldHideServer()⤶ {⤶ if (hide_server) {⤶ return true;⤶ }⤶ ⤶ // If we don't want to force the server hidden, we can fallback to the original function.⤶ return detour_ShouldHideServer.GetTrampoline<ShouldHideServer>()();⤶ }⤶ ```⤶ ⤶ We return true to hide the server from the server list. ⤶ This is useful if you would want no players to find your server. ⤶ ⤶ The only thing left is to detour the actual function now.⤶ ```cpp⤶ SourceSDK::ModuleLoader server_loader("server_srv");⤶ SymbolFinder symfinder;⤶ ⤶ void* sv_shserver = nullptr;⤶ ⤶ for (const auto& sym : ShouldHideServerSyms) {⤶ sv_shserver = symfinder.Resolve(server_loader.GetModule(), sym.name.c_str(), sym.length);⤶ ⤶ if (sv_shserver)⤶ break;⤶ }⤶ ⤶ if (sv_shserver == nullptr) {⤶ Msg("Could not locate CServerGameDLL::ShouldHideServer symbol!\n");⤶ } else {⤶ Msg("Located CServerGameDLL::ShouldHideServer\n");⤶ }⤶ ⤶ detour_ShouldHideServer.Create(reinterpret_cast<void*>(sv_shserver), reinterpret_cast<void*>(&hook_ShouldHideServer));⤶ detour_ShouldHideServer.Enable();⤶ if (detour_ShouldHideServer.IsValid()) {⤶ Msg("Valid detour for CServerGameDLL::ShouldHideServer\n");⤶ } else {⤶ Msg("Invalid detour for CServerGameDLL::ShouldHideServer\n");⤶ }⤶ ```⤶ ⤶ We could also add a Lua function to hide the Server. ⤶ At the end, your file could look something like this:⤶ ```cpp⤶ #include <GarrysMod/FactoryLoader.hpp>⤶ #include <scanning/symbolfinder.hpp>⤶ #include <GarrysMod/Symbol.hpp>⤶ #include <detouring/hook.hpp>⤶ ⤶ typedef bool (*ShouldHideServer)();⤶ Detouring::Hook detour_ShouldHideServer;⤶ const std::vector<Symbol> ShouldHideServerSyms = {⤶ Symbol::FromName("_ZN14CServerGameDLL16ShouldHideServerEv"), // Our signature for CServerGameDLL::ShouldHideServer⤶ };⤶ ⤶ bool hide_server = true;⤶ bool hook_ShouldHideServer()⤶ {⤶ if (hide_server) {⤶ return true;⤶ }⤶ ⤶ // If we don't want to force the server hidden, we can fallback to the original function.⤶ return detour_ShouldHideServer.GetTrampoline<ShouldHideServer>()();⤶ }⤶ ⤶ // A Lua function to hide the server. game.HideServer()⤶ LUA_FUNCTION(HideServer)⤶ {⤶ LUA->CheckType(1, Type::Bool);⤶ hide_server = LUA->GetBool(1);⤶ ⤶ return 0;⤶ }⤶ ⤶ GMOD_MODULE_OPEN()⤶ {⤶ LUA->PushSpecial(GarrysMod::Lua::SPECIAL_GLOB);⤶ LUA->GetField(-1, "game");⤶ LUA->PushCFunction(HideServer);⤶ LUA->SetField(-2, "HideServer");⤶ LUA->Pop(2);⤶ ⤶ SourceSDK::ModuleLoader server_loader("server_srv");⤶ SymbolFinder symfinder;⤶ ⤶ void* sv_shserver = nullptr;⤶ ⤶ for (const auto& sym : ShouldHideServerSyms) {⤶ sv_shserver = symfinder.Resolve(server_loader.GetModule(), sym.name.c_str(), sym.length);⤶ ⤶ if (sv_shserver)⤶ break;⤶ }⤶ ⤶ if (sv_shserver == nullptr) {⤶ Msg("Could not locate CServerGameDLL::ShouldHideServer symbol!\n");⤶ }⤶ else {⤶ Msg("Located CServerGameDLL::ShouldHideServer\n");⤶ }⤶ ⤶ detour_ShouldHideServer.Create(reinterpret_cast<void*>(sv_shserver), reinterpret_cast<void*>(&hook_ShouldHideServer));⤶ detour_ShouldHideServer.Enable();⤶ if (detour_ShouldHideServer.IsValid()) {⤶ Msg("Valid detour for CServerGameDLL::ShouldHideServer\n");⤶ }⤶ else {⤶ Msg("Invalid detour for CServerGameDLL::ShouldHideServer\n");⤶ }⤶ ⤶ return 0;⤶ }⤶ ⤶ GMOD_MODULE_CLOSE()⤶ {⤶ detour_ShouldHideServer.Destroy(); // We remove our detour.⤶ ⤶ return 0;⤶ }⤶ ⤶ [...]⤶ ```⤶ ⤶ Now you can test everything and it should work as expected.