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.