Skip to content

Commit

Permalink
feat: Improved function hooking
Browse files Browse the repository at this point in the history
+ Updated the injector with the new valve libraryapps format
+ Made the hooking methods use 5 bytes and look better
  • Loading branch information
Kariaro committed Sep 16, 2021
1 parent 4746405 commit 7686f84
Show file tree
Hide file tree
Showing 12 changed files with 236 additions and 41 deletions.
1 change: 1 addition & 0 deletions PluginDevFolder/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PlaygroundPlugin/
18 changes: 9 additions & 9 deletions PluginDevFolder/SMLuaReaderPlugin/src/hooks.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include <stdio.h>
#include <hook.h>
#include <gamehook.h>
#include "lua.h"

#include <console.h>
Expand All @@ -8,40 +8,40 @@ using Console::Color;

// LUAL_REGISTER
typedef void (*pluaL_register)(lua_State*, const char*, const luaL_Reg*);
Hook *hck_luaL_register;
GameHook* hck_luaL_register;

// LUAL_LOADSTRING
typedef int (*pluaL_loadstring)(lua_State*, const char*);
Hook *hck_luaL_loadstring;
GameHook *hck_luaL_loadstring;

// LUA_NEWSTATE
typedef lua_State *(*plua_newstate)(lua_Alloc, void*);
Hook *hck_lua_newstate;
GameHook *hck_lua_newstate;

// LUAL_LOADBUFFER
typedef int (*pluaL_loadbuffer)(lua_State*, const char*, size_t, const char*);
Hook *hck_luaL_loadbuffer;
GameHook *hck_luaL_loadbuffer;

// =============

namespace Hooks {
void hook_luaL_register(lua_State *L, const char *libname, const luaL_Reg *l) {
Console::log(Color::Aqua, "hook_luaL_register: libname=[%s]", libname);
return ((pluaL_register)hck_luaL_register->Gate())(L, libname, l);
return ((pluaL_register)*hck_luaL_register)(L, libname, l);
}

int hook_luaL_loadstring(lua_State *L, const char *s) {
Console::log(Color::Aqua, "hook_luaL_loadstring: s=[ ... ]");
return ((pluaL_loadstring)hck_luaL_loadstring->Gate())(L, s);
return ((pluaL_loadstring)*hck_luaL_loadstring)(L, s);
}

lua_State *hook_lua_newstate(lua_Alloc f, void* ud) {
Console::log(Color::Aqua, "hck_lua_newstate: ud=[%p]", ud);
return ((plua_newstate)hck_lua_newstate->Gate())(f, ud);
return ((plua_newstate)*hck_lua_newstate)(f, ud);
}

int hook_luaL_loadbuffer(lua_State *L, const char *buff, size_t sz, const char *name) {
Console::log(Color::Aqua, "hck_luaL_loadbuffer: buff=[ ... ], sz=[%zu], name=[%s]", sz, name);
return ((pluaL_loadbuffer)hck_luaL_loadbuffer->Gate())(L, buff, sz, name);
return ((pluaL_loadbuffer)*hck_luaL_loadbuffer)(L, buff, sz, name);
}
}
14 changes: 6 additions & 8 deletions PluginDevFolder/SMLuaReaderPlugin/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,14 @@ LIB_RESULT PluginLoad() {
return PLUGIN_SUCCESSFULL;
}

HookUtility* util;
bool InjectLua() {
Console::log(Color::Aqua, "Installing lua hooks");

util = new HookUtility();
hck_luaL_register = util->InjectFromName("lua51.dll", "luaL_register", &Hooks::hook_luaL_register, 15);
hck_luaL_loadstring = util->InjectFromName("lua51.dll", "luaL_loadstring", &Hooks::hook_luaL_loadstring, 16);
hck_lua_newstate = util->InjectFromName("lua51.dll", "lua_newstate", &Hooks::hook_lua_newstate, -14, 6);
hck_luaL_loadbuffer = util->InjectFromName("lua51.dll", "luaL_loadbuffer", &Hooks::hook_luaL_loadbuffer, 17, 13);

hck_luaL_register = GameHooks::InjectFromName("lua51.dll", "luaL_register", &Hooks::hook_luaL_register, 15);
hck_luaL_loadstring = GameHooks::InjectFromName("lua51.dll", "luaL_loadstring", &Hooks::hook_luaL_loadstring, 16);
hck_lua_newstate = GameHooks::InjectFromName("lua51.dll", "lua_newstate", &Hooks::hook_lua_newstate, 6);
hck_luaL_loadbuffer = GameHooks::InjectFromName("lua51.dll", "luaL_loadbuffer", &Hooks::hook_luaL_loadbuffer, 13);

if(!hck_luaL_register) {
Console::log(Color::Red, "Failed to inject 'luaL_register'");
return false;
Expand All @@ -54,6 +52,6 @@ bool InjectLua() {

LIB_RESULT PluginUnload() {
Console::log(Color::Aqua, "Unloading this plugin!");
util->Unload();
//util->Unload();
return PLUGIN_SUCCESSFULL;
}
22 changes: 10 additions & 12 deletions PluginDevFolder/TileEditorUnlockPlugin/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
#include <console.h>
using Console::Color;

#include <hook.h>
#include "windows.h"
#include <gamehook.h>

// debugSize == h.blueprintListCompressedSize

Expand All @@ -24,10 +25,10 @@ namespace Injection {
typedef longlong* _vector_ptr;

typedef void(*pReadNode)(void*, _vector_ptr, void*, void*, void*, int);
Hook *hck_ReadNode;
GameHook *hck_ReadNode;

typedef void(*pReadBlueprintList)(int, _vector_ptr, void*, void*, void*, int, void*, int);
Hook *hck_ReadBlueprintList;
GameHook *hck_ReadBlueprintList;

longlong get_sm_handle() {
return (longlong)GetModuleHandleA("ScrapMechanic.exe");
Expand All @@ -48,15 +49,15 @@ namespace Injection {

void hook_ReadNode(void* a, _vector_ptr vec, void* b, void* c, void* d, int e) {
Console::log(Color::Aqua, "hook_ReadNode: a=[%p], vec=[%p], b=[%p], c=[%p], d=[%p], e=[%d]", a, vec, b, c, d, e);
((pReadNode)hck_ReadNode->Gate())(a, vec, b, c, d, e);
((pReadNode)*hck_ReadNode)(a, vec, b, c, d, e);

AddEntityToMemory(vec);
return;
}

void hook_ReadBlueprintList(int a, _vector_ptr vec, void* b, void* c, void* d, int e, void* f, int g) {
Console::log(Color::Aqua, "hook_ReadBlueprintList: a=[%d], vec=[%p], b=[%p], c=[%p], d=[%p], e=[%d], f=[%p], g=[%d]", a, vec, b, c, d, e, f, g);
((pReadBlueprintList)hck_ReadBlueprintList->Gate())(a, vec, b, c, d, e, f, g);
((pReadBlueprintList)*hck_ReadBlueprintList)(a, vec, b, c, d, e, f, g);

AddEntityToMemory(vec);
return;
Expand All @@ -69,20 +70,17 @@ LIB_RESULT PluginLoad() {

longlong sm_handle = Injection::get_sm_handle();

Injection::hck_ReadNode = new Hook();
Injection::hck_ReadNode->Inject((void*)(sm_handle + offset_ReadNode), &Injection::hook_ReadNode, 14);

Injection::hck_ReadBlueprintList = new Hook();
Injection::hck_ReadBlueprintList->Inject((void*)(sm_handle + offset_ReadBlueprintList), &Injection::hook_ReadBlueprintList, 16);
Injection::hck_ReadNode = GameHooks::Inject((void*)(sm_handle + offset_ReadNode), &Injection::hook_ReadNode, 14);
Injection::hck_ReadBlueprintList = GameHooks::Inject((void*)(sm_handle + offset_ReadBlueprintList), &Injection::hook_ReadBlueprintList, 16);

return PLUGIN_SUCCESSFULL;
}

LIB_RESULT PluginUnload() {
Console::log(Color::Red, "Unloading this plugin!");

Injection::hck_ReadNode->Uninject();
Injection::hck_ReadBlueprintList->Uninject();
//Injection::hck_ReadNode->Uninject();
//Injection::hck_ReadBlueprintList->Uninject();

return PLUGIN_SUCCESSFULL;
}
11 changes: 8 additions & 3 deletions SMInjector/src/find_steam.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,28 @@ namespace SteamFinder {
int idx = 0;
int len = 0;

bool next_is_path = false;
bool quote = false;
for(int i = 16; i < buffer.size(); i++) {
char c = buffer[i];
if(c == '\\') c = buffer[++i];

if(c == '"') {
temp[idx] = 0;
if(i > 16 && quote) {
if(quote) {
len++;
if(len > 4 && ((len & 1) == 0)) {

if(next_is_path) {
vec.push_back(std::wstring(temp).append(L"\\steamapps\\common"));
next_is_path = false;
} else {
next_is_path = (wcscmp(temp, L"path") == 0);
}

temp[0] = 0;
idx = 0;
}

quote = !quote;
} else {
if(quote) {
Expand Down
1 change: 1 addition & 0 deletions SMLibrary/SMLibrary.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\console.h" />
<ClInclude Include="include\gamehook.h" />
<ClInclude Include="include\hook.h" />
<ClInclude Include="include\sm_lib.h" />
<ClInclude Include="include\stdafx.h" />
Expand Down
3 changes: 3 additions & 0 deletions SMLibrary/SMLibrary.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,8 @@
<ClInclude Include="src\hooks.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\gamehook.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
181 changes: 181 additions & 0 deletions SMLibrary/include/gamehook.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
#include "stdafx.h"

#ifndef __GAMEHOOK_H__
#define __GAMEHOOK_H__

typedef void* GameHook;
typedef long long longlong;

#ifndef _SM_LIBRARY_BUILD_PLUGIN
#include <windows.h>
#include <map>
#endif


namespace GameHooks {
#ifndef _SM_LIBRARY_BUILD_PLUGIN

// Struct describing the hook
struct GateContext {
// The address of the hooked function
void* function;

// The length in bytes of the hook
size_t length;

// The allocated page within 2GB
void* page_address;

// The gate that should be called
void* end_gate;

// Last hook injected
GameHook* last_hook;
};

// We need to specify a map that contains information about where we have hooked stuff previously
std::map<void*, GateContext*> hooked_map;

void* AllocatePageWithin2GB(void* target_address, int length) {
static ULONG dwAllocationGranularity;

// Fetch some allocation size
if(!dwAllocationGranularity) {
SYSTEM_INFO si;
GetSystemInfo(&si);
dwAllocationGranularity = si.dwAllocationGranularity;
}

UINT_PTR address = (UINT_PTR)target_address;
SIZE_T dwSize = length;

UINT_PTR min, max, addr, add = dwAllocationGranularity - 1, mask = ~add;
min = address >= 0x80000000 ? (address - 0x80000000 + add) & mask : 0;
max = address < (UINTPTR_MAX - 0x80000000) ? (address + 0x80000000) & mask : UINTPTR_MAX;

::MEMORY_BASIC_INFORMATION mbi;
do {
// Query information about the currently requested memory
if(!VirtualQuery((void*)min, &mbi, sizeof(mbi))) return nullptr;

// Spooky stuff
min = (UINT_PTR)mbi.BaseAddress + mbi.RegionSize;

if(mbi.State == MEM_FREE) {
addr = ((UINT_PTR)mbi.BaseAddress + add) & mask;

if(addr < min && dwSize <= (min - addr)) {
if(addr = (UINT_PTR)VirtualAlloc((PVOID)addr, dwSize, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)) {
return (PVOID)addr;
}
}
}
} while (min < max);

return nullptr;
}

GateContext* CreateInjectionHook(void* target_function, int length) {
GateContext* context = new GateContext();
context->function = target_function;
context->length = length;
context->page_address = AllocatePageWithin2GB(target_function, length + 28);
context->end_gate = (void*)((long long)context->page_address + 14);
// context.last_hook

return context;
}

// Modify context to inject gate

bool InjectGate(GateContext* context, void *dst, void *src) {
typedef long long longlong;

size_t len = context->length;
void* gate = context->page_address;

// Make the first jump into the target method
*(longlong*)((longlong)gate) = 0x25FF; // Add far jump
*(longlong*)((longlong)gate + 6) = (longlong)src;

// Store the first 'len' bytes of the function into the newly created gate
memcpy((void*)((longlong)gate + 14), dst, len);
// Construct the far jump to the begining of the hooked function
*(longlong*)((longlong)gate + len + 14) = 0x25FF; // Add far jump
*(longlong*)((longlong)gate + len + 20) = (longlong)dst + len;

DWORD oldProtection;
// Update the protection for the gate memory
VirtualProtect(gate, (size_t)len + 6, PAGE_EXECUTE_READ, &oldProtection);

// Allow modifications of the target function
VirtualProtect(dst, (size_t)len, PAGE_EXECUTE_READWRITE, &oldProtection);

memset(dst, 0x90, len); // Fill the replaced region with NOP

// Modify the target function
// page_address - target_addres - 5

// Make the first 5 bytes point towards the page with the absolute jump
*(char*)((longlong)dst) = 0xE9;
// Make sure this is within the 2GB range !!!!!!!!
*(int*)((longlong)dst + 1) = (int)((longlong)context->page_address - (longlong)dst - 5);

DWORD temp;
VirtualProtect(dst, (size_t)len, oldProtection, &temp);

GameHook* gate_hook = new GameHook();
*((void**)gate_hook) = context->end_gate;
context->last_hook = gate_hook;

return true;
}

_LIB_EXPORT GameHook* Inject(void* target_function, void* hook_function, int length) {
// First we check if we have already hooked the function
auto iter = hooked_map.find(target_function);
if(iter != hooked_map.end()) {
// target function was already injected
GateContext* context = iter->second;

// Modify the last hooks gate to point to the new function
*((void**)context->last_hook) = hook_function;

// Modify the next hook to point towards the end gate
GameHook* next_hook = new GameHook();
*((void**)next_hook) = context->end_gate;

// Update the last injected hook
context->last_hook = next_hook;

return next_hook;
} else {
GateContext* new_context = CreateInjectionHook(target_function, length);

if(!InjectGate(new_context, target_function, hook_function)) {
return nullptr;
}

hooked_map[target_function] = new_context;
return new_context->last_hook;
}
}

_LIB_EXPORT GameHook* InjectFromName(const char* module_name, const char* proc_name, void* hook_function, int length) {
HMODULE hModule = GetModuleHandleA(module_name);
if(!hModule) {
//printf("The module '%s' was not found\n", module_name);
return nullptr;
}

void* target = GetProcAddress(hModule, proc_name);
return GameHooks::Inject(target, hook_function, length);
}
#else
_LIB_IMPORT GameHook* InjectFromName(const char* module_name, const char* proc_name, void* hook_function, int length);
_LIB_IMPORT GameHook* Inject(void* target_function, void* hook_function, int length);
#endif

}

#endif
Loading

0 comments on commit 7686f84

Please sign in to comment.