# How the NativeHooks System Invokes GTA Native Functions in YimMenuV2

> Discover how YimMenuV2's NativeHooks system invokes GTA native functions. Learn about VMT hooking and native handler redirection for game modding.

- Repository: [YimMenu/YimMenuV2](https://github.com/YimMenu/YimMenuV2)
- Tags: internals
- Published: 2026-07-16

---

**YimMenuV2 hooks the script-program VMT and overwrites native-handler entry points to intercept and redirect GTA native function calls.**

The YimMenuV2 mod menu for GTA 5 intercepts native function calls by manipulating the internal script-program structures used by the RAGE engine. This deep integration allows the NativeHooks system to invoke GTA native functions through custom replacement handlers while preserving the ability to call original implementations. Understanding this mechanism requires examining how the menu patches the virtual-method table (VMT) of script programs and redirects individual native entry points.

## Architecture of the NativeHooks System

The NativeHooks module operates by targeting `rage::scrProgram` instances, the core data structures the game uses to manage script execution. When GTA loads a new script, the system registers the program, creates a VMT hook to monitor its lifecycle, and prepares to swap native function pointers.

### Program Registration and VMT Hooking

Registration begins in `NativeHooks::RunScriptImpl`, which scans the global `Pointers.ScriptPrograms` array to detect newly loaded scripts. For each program, it calls `RegisterProgram` to initialize a hook context.

The `NativeHooks::Program` constructor then creates a `VMTHook` targeting the program's VMT at slot 9. Specifically, it replaces the destructor at index 6 with `ScrProgram_Dtor`. This ensures the menu can execute cleanup logic when the script unloads.

### Preserving Original Native Handlers

Before applying any modifications, the constructor captures the program's original state. It copies the `program->m_NativeEntrypoints` array into `m_OrigHandlers`, creating a snapshot of all native function pointers. This backup is essential for the `Program::Cleanup` method to restore the original table later.

### Installing Custom Replacement Handlers

New hooks are registered via `AddHookImpl`, which stores a `Hook` structure containing a `NativeIndex` and replacement `rage::scrNativeHandler`. The system immediately iterates through all registered programs and invokes `Program::Apply` to patch the live entry-point table:

```cpp
auto old_native = NativeInvoker::GetNativeHandler(hook.m_Index);
auto old_native_addr = m_Program->GetAddressOfNativeEntrypoint(old_native);
if (old_native_addr)
    *old_native_addr = hook.m_Replacement;

```

Here, `NativeInvoker::GetNativeHandler` resolves the absolute address of the original native from its index, then `GetAddressOfNativeEntrypoint` locates the pointer within the program's native table. The menu overwrites this pointer with the custom handler, redirecting future calls.

### Runtime Invocation Flow

When a game script invokes a native like `GET_ENTITY_COORDS`, the script VM looks up the function address in `m_Program->m_NativeEntrypoints`. Because the pointer now references the replacement function rather than the original code, control transfers to the menu's implementation first. The replacement handler can forward arguments to the original function (accessed via `m_OrigHandlers`) or implement entirely new behavior before returning to the script.

## Implementation Example

Hooking a specific native requires specifying its hash index and providing a compatible handler function. The following example intercepts `GET_ENTITY_COORDS` (index `0x2C`) to log entity coordinates:

```cpp
NativeHooks::AddHook(ALL_SCRIPTS, 0x2C, [](rage::scrNativeCallContext* ctx) -> void {
    // Call the original native first
    auto* orig = NativeInvoker::GetNativeHandler(0x2C);
    orig(ctx);

    // Now add our custom behaviour
    Vector3* coords = reinterpret_cast<Vector3*>(ctx->GetResult());
    LOG(INFO, "Entity {} was queried at ({}, {}, {})", ctx->GetArgument<int>(0), coords->x, coords->y, coords->z);
});

```

The `ALL_SCRIPTS` scope ensures the hook applies to every script program currently registered with the NativeHooks system.

## Cleanup and Restoration

When a script terminates, the hooked destructor `ScrProgram_Dtor` triggers `NativeHooks::UnregisterProgram`. The `Program::Cleanup` method executes two critical operations: it restores the original native handler table using `memcpy` from `m_OrigHandlers`, and it disables the VMT hook to prevent dangling pointers. This ensures clean detachment when the menu unloads:

```cpp
// Unregister all hooks when the menu unloads
NativeHooks::Destroy();

```

The restoration process guarantees that the game's script engine resumes using the original native implementations, preventing crashes or undefined behavior after the menu exits.

## Summary

- **VMT Hooking**: The system hooks the script-program destructor (index 6) via slot 9 to monitor script lifecycle and ensure cleanup.
- **Table Patching**: `AddHookImpl` overwrites specific entries in `m_NativeEntrypoints` after resolving addresses via `NativeInvoker::GetNativeHandler`.
- **Original Preservation**: The constructor snapshots the native table to `m_OrigHandlers`, enabling restoration during `Cleanup`.
- **Interception Flow**: Script VM calls redirect to custom handlers first, allowing pre/post-processing or complete replacement of native behavior.
- **Safe Teardown**: `Program::Cleanup` restores original handlers and removes VMT hooks using `memcpy` and the hook destructor.

## Frequently Asked Questions

### How does YimMenuV2 locate the native function addresses to hook?

According to the source code in [`src/game/gta/invoker/Invoker.hpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/game/gta/invoker/Invoker.hpp), the `NativeInvoker::GetNativeHandler` function resolves the absolute memory address of a native function from its hash index. This address is then passed to `GetAddressOfNativeEntrypoint` within the script program to locate the specific pointer slot that needs replacement.

### What prevents the game from crashing when the menu unloads?

The `NativeHooks::Program` class stores the original native handler table in `m_OrigHandlers` during construction, as seen in [`src/game/backend/NativeHooks.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/game/backend/NativeHooks.cpp). When the script unloads or `NativeHooks::Destroy()` is called, the `Cleanup` method uses `memcpy` to restore the original `m_NativeEntrypoints` array and disables the VMT hook, returning the program to its unmodified state.

### Can hooks target specific scripts rather than all scripts?

Yes. While the example uses `ALL_SCRIPTS`, the `AddHook` function accepts a script identifier parameter. The implementation in [`src/game/backend/NativeHooks.cpp`](https://github.com/YimMenu/YimMenuV2/blob/main/src/game/backend/NativeHooks.cpp) stores hooks in `m_RegisteredHooks` and applies them only to matching program instances during the registration walk, allowing selective targeting of specific script threads.

### Why is the destructor hook (index 6) necessary?

The VMT hook on slot 9 replaces the destructor at index 6 with `ScrProgram_Dtor` to intercept script termination. This hook ensures `NativeHooks::UnregisterProgram` executes before the program memory is freed, providing a guaranteed execution path to restore the original native handler table and prevent use-after-free errors.