Just a quick update for those of you not following the discussion over at UC.
Seems like more natives are shorter than 14 bytes than I expected. To hook those it's necessary to find free mem in a 2GB region around the native function, so that it's possible to use a shorter jump intruction. "MinHook" is a nice library which does exactly that.
To be able to hook the aforementioned natives, add MinHook to your solution, adjust project settings (set runtime library to multi-threaded dll, etc.) and add the include/library path to your project.
Example code:
Code:
#include "stdafx.h"
#include <Windows.h>
#include "MinHook.h"
#pragma comment(lib, "libMinHook.x64.lib")
template <typename T>
bool hookNative(UINT64 hash, LPVOID hookFunction, T** trampoline)
{
if (*reinterpret_cast<LPVOID*>(trampoline) != NULL)
return true;
auto originalFunction = GetNativeHandler(hash);
if (originalFunction != 0) {
MH_STATUS createHookStatus = MH_CreateHook(originalFunction, hookFunction, reinterpret_cast<LPVOID*>(trampoline));
if (
((createHookStatus == MH_OK) || (createHookStatus == MH_ERROR_ALREADY_CREATED))
&& (MH_EnableHook(originalFunction) == MH_OK)
)
{
Log::Debug("Hooked 0x%#p", hash);
return true;
}
}
return false;
}
NativeHandler ORIG_IS_DLC_PRESENT = NULL;
void* __cdecl MY_IS_DLC_PRESENT(NativeContext *cxt)
{
Hash DlcHash = cxt->GetArgument<Hash>(0);
if (DlcHash == 2532323046) { // DEV
// game requested dev dlc -> return true;
cxt->SetResult(0, true);
}
else
{
ORIG_IS_DLC_PRESENT(cxt);
}
return cxt;
}
NativeHandler ORIG_CLEAR_PED_TASKS_IMMEDIATELY = NULL;
void *__cdecl MY_CLEAR_PED_TASKS_IMMEDIATELY(NativeContext *cxt)
{
if (cxt->GetArgument<Ped>(0) != PLAYER::PLAYER_PED_ID())
{
ORIG_CLEAR_PED_TASKS_IMMEDIATELY(cxt);
}
else
{
Log::Debug("Prevented CLEAR_PED_TASKS_IMMEDIATELY");
}
return cxt;
}
NativeHandler ORIG_CLEAR_PED_TASKS = NULL;
void *__cdecl MY_CLEAR_PED_TASKS(NativeContext *cxt)
{
if (cxt->GetArgument<Ped>(0) != PLAYER::PLAYER_PED_ID())
{
ORIG_CLEAR_PED_TASKS(cxt);
}
else
{
Log::Debug("Prevented CLEAR_PED_TASKS");
}
return cxt;
}
NativeHandler ORIG_CLEAR_PED_SECONDARY_TASK = NULL;
void *__cdecl MY_CLEAR_PED_SECONDARY_TASK(NativeContext *cxt)
{
if (cxt->GetArgument<Ped>(0) != PLAYER::PLAYER_PED_ID())
{
ORIG_CLEAR_PED_SECONDARY_TASK(cxt);
}
else
{
Log::Debug("Prevented CLEAR_PED_SECONDARY_TASK");
}
return cxt;
}
NativeHandler ORIG_CLONE_PED = NULL;
void *__cdecl MY_CLONE_PED(NativeContext *cxt)
{
if (cxt->GetArgument<Ped>(0) != PLAYER::PLAYER_PED_ID()) // seems to be actually asc order
{
ORIG_CLONE_PED(cxt);
}
else
{
Log::Debug("Prevented CLONE_PED");
}
return cxt;
}
NativeHandler ORIG_NETWORK_SESSION_KICK_PLAYER = NULL;
void *__cdecl MY_NETWORK_SESSION_KICK_PLAYER(NativeContext *cxt)
{
if (cxt->GetArgument<Player>(0) != PLAYER::PLAYER_ID()) {
ORIG_NETWORK_SESSION_KICK_PLAYER(cxt);
}
else
{
Log::Debug("Prevented NETWORK_SESSION_KICK_PLAYER");
}
return cxt;
}
NativeHandler ORIG_ADD_OWNED_EXPLOSION = NULL;
void *__cdecl MY_ADD_OWNED_EXPLOSION(NativeContext *cxt)
{
if (cxt->GetArgument<Ped>(0) != PLAYER::PLAYER_PED_ID())
{
ORIG_ADD_OWNED_EXPLOSION(cxt);
}
else
{
Log::Debug("Prevented ADD_OWNED_EXPLOSION");
}
return cxt;
}
bool AttemptHookNatives() {
static DWORD64 dwThreadCollectionPtr = 0;
if (!dwThreadCollectionPtr) {
// scan for GTA Thread Pool
dwThreadCollectionPtr = Pattern::Scan(g_MainModuleInfo, "48 8B 05 ? ? ? ? 8B CA 4C 8B 0C C8 45 39 51 08");
}
if ( !dwThreadCollectionPtr
|| !Pattern::Scan(g_MainModuleInfo, "76 61 49 8B 7A 40 48 8D 0D") // scan for Native Registration Table
) {
// too early. GetNativeHandler would log a fatal error and exit the process
return false;
}
return true
// && hookNative(0x2D6859674806FDCE, &MY_IS_DLC_PRESENT, &ORIG_IS_DLC_PRESENT)
&& hookNative(0xC1A624FF5CF18419, &MY_NETWORK_SESSION_KICK_PLAYER, &ORIG_NETWORK_SESSION_KICK_PLAYER)
&& hookNative(0x1E2B48EE3EC55DCF, &MY_CLEAR_PED_TASKS_IMMEDIATELY, &ORIG_CLEAR_PED_TASKS_IMMEDIATELY)
&& hookNative(0x27CC98B7C879C320, &MY_CLEAR_PED_TASKS, &ORIG_CLEAR_PED_TASKS)
&& hookNative(0x4191220706130B86, &MY_CLEAR_PED_SECONDARY_TASK, &ORIG_CLEAR_PED_SECONDARY_TASK)
&& hookNative(0x237D19F4F17C2B85, &MY_CLONE_PED, &ORIG_CLONE_PED)
&& hookNative(0x0AEB0F5A0526E37F, &MY_ADD_OWNED_EXPLOSION, &ORIG_ADD_OWNED_EXPLOSION)
// add more hooks here
;
}
DWORD WINAPI lpHookNatives(LPVOID lpParam) {
Log::Debug("Initializing Hooks");
// Initialize MinHook.
if (MH_Initialize() != MH_OK)
{
Log::Fatal("Failed to initialize MinHook");
}
while (!AttemptHookNatives()) {
Sleep(100);
}
Log::Debug("Finished hooking");
return 0;
}
void SpawnHookNatives() {
CreateThread(0, 0, lpHookNatives, 0, 0, 0);
}
Keep in mind MinHook is under BSD license when redistributing.
To make it clear: This post is just for developers who want to hook more natives. If you came for the Dev T-Shirts, simply download + inject the .dll from page 1