> For the complete documentation index, see [llms.txt](https://0x29a.gitbook.io/0x29a-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://0x29a.gitbook.io/0x29a-docs/runtime/hook.md).

# hook / \_29a

**runtime · reversible · globals: `hook`, `_29a`**

Two complementary tables for modifying running native code:

* **`hook.*`** — reversible byte patches: overwrites bytes, saves the original, and lets you restore it later.
* **`_29a.*`** (also reachable as `hook.create`/`hook.remove`, back-compat aliases) — real detour trampolines: redirects execution from `target` to your own `detour` function, and hands you back a pointer to call the original code.

## Byte patches (`hook.*`)

### `hook.patch(addr, hex_str) -> bool`

Writes the bytes from `hex_str` (e.g. `"90 90 90"` or `"909090"`) at `addr`. The **first** time an address is touched, the original bytes are saved automatically — subsequent calls on the same address don't overwrite that backup.

```lua
hook.patch(jump_addr, "EB")   -- turns it into an unconditional short jmp
```

### `hook.nop(addr, count) -> bool`

Shortcut for `hook.patch`, filling `count` bytes with `0x90` (NOP).

### `hook.restore(addr) -> bool`

Restores the original bytes saved for `addr` and forgets the record.

### `hook.restore_all() -> count`

Restores **every** active patch at once. Returns how many were restored. Call this from your script's `__neuro_on_stop` so it doesn't leave patched bytes behind when the script is removed.

### `hook.original(addr) -> hex_string | nil`

Returns the original bytes saved for `addr`, as a hex string — handy for logging/debugging before restoring.

### `hook.list() -> [{addr, original}]`

Lists every currently active patch.

## Detour trampolines (`_29a.*`)

`_29a.install` has two forms: a **Lua-detour** form (the one you want almost all of the time) and a lower-level **native-stub** form for the rare case a Lua function genuinely can't do the job.

### `_29a.install(target, lua_fn [, sig]) -> trampoline_addr | nil`

Installs `lua_fn` as a real detour on `target`: calls to `target` now run `lua_fn` **instead of** the real function — same arguments, same thread. `lua_fn` decides everything from there: whether to call the original at all, when (before its own logic, after, or not), and what the real caller ultimately sees, since **whatever `lua_fn` returns becomes `target`'s return value.** No hand-assembled stub required — the engine provides a shared native relay so a plain Lua function can sit where a detour address normally has to go.

`sig` is a shape string, one character per argument slot, up to 4: `'i'` for integer/pointer/bool, `'f'` for a 32-bit float — the exact same convention as [`mem.callf`](/0x29a-docs/memory-and-analysis/mem.md)'s `shape` argument. Omit it (or pass `""`) for an all-integer prototype. The returned `trampoline_addr` — call the original through it with `mem.callf(trampoline_addr, sig, ...)`, whenever your callback decides to.

### `_29a.sig(prototype) -> shape_string`

Derives a `sig`/`shape` string from a plain C-style prototype instead of you working out by hand which argument slots are floats:

```lua
_29a.sig("bool __fastcall CreateMove(void* pthis, int nSlot, float flInputSampleTime, bool bActive)")
-- "iifi"
```

Anything containing `float` or `double` becomes `'f'`; everything else becomes `'i'`; a single `void` parameter list returns `""`. Handy whenever you already have (or can look up) the function's real signature — which is most of the time, since disassemblers, public SDK dumps, and reference sources almost always give you one.

{% hint style="info" %}
For anything covered by `cs2_lib`'s own `patterns.json`, this is done for you: `cs2.hook(name, lua_fn [, mod])` resolves the address via `cs2.scan()` **and** runs its recorded prototype through `_29a.sig()` automatically, so you don't need a `sig` argument at all for a known function.
{% endhint %}

```lua
-- bool __fastcall CanTakeDamage(void* pthis, void* info)
local SIG = "ii"
local trampoline

local function on_can_take_damage(pthis, info)
    -- Runs first, instead of the real function.
    do_your_own_thing(pthis, info)

    -- Call the original yourself, whenever you want it to run.
    local real_result = mem.callf(trampoline, SIG, pthis, info)

    return real_result -- forward it so the hook stays transparent
end

trampoline = _29a.install(can_take_damage_addr, on_can_take_damage, SIG)
if not trampoline then print("hook failed") end

-- later, to undo:
_29a.remove(can_take_damage_addr)
```

{% hint style="warning" %}
**This is only safe on functions that (a) run on the same thread this script's own Lua state is ticked on, and (b) genuinely tolerate being re-entered via a trampoline copy of their own prologue instead of being called exactly once by their real caller.** Neither is guaranteed for an arbitrary function, and there is no way to check either from Lua — you have to know your target.

This isn't theoretical: an earlier version of this doc used CS2's `CreateMove` as the example, hooked exactly like above. It installed without error and crashed the instant `CreateMove` actually fired, for two confirmed reasons that have nothing to do with argument marshaling:

1. `CreateMove` runs on a **different OS thread** than the one this script's Lua state is ticked on (confirmed live by logging the thread ID on both sides). Calling into Lua from inside the hook was genuine cross-thread reentrancy into the Lua/LuaJIT state — corrupting it in ways that showed up as unrelated-looking crashes elsewhere a few frames later, not a clean fault at the bad call site.
2. Even with Lua removed entirely — a pure passthrough that just called the real `CreateMove` with its own unmodified arguments and nothing else — it **still** crashed, deterministically, every single time. `CreateMove` is simply not designed to be re-entered via a trampoline copy of its own prologue; it expects to be called exactly once, by its real caller.

Per-tick core simulation functions like this are the case to watch for. Most scripting targets — weapon logic, one-shot checks, entity think functions triggered by an event — don't have this sensitivity, and hook fine. But when a target is this central to the engine's own frame loop, **don't hook it at all** — read/write whatever it produces (a buffer, a struct, a global) from [`_29a.on_frame`](/0x29a-docs/introduction/lifecycle.md) instead, which runs safely on your own thread and never re-enters anything. See `09_createmove_hook.lua` for exactly this pattern applied to CS2's `CreateMove`/`CUserCmd`.
{% endhint %}

### `_29a.install(target, detour_addr) -> trampoline_addr | nil`

### `_29a.hook(target, detour_addr) -> trampoline_addr | nil`

*(identical alias of `install`)*

The lower-level form, for the rare case you need the detour itself to be native code rather than Lua (e.g. it must run somewhere Lua can't safely reach, or it needs to preserve/restore raw registers a Lua call can't see). `detour_addr` must be the address of code that already exists as real x86-64 instructions — either another native function in the target process, or a stub you hand-assemble yourself into memory allocated with [`mem.alloc`](/0x29a-docs/memory-and-analysis/mem.md#allocation-protection).

`trampoline_addr` is the address that **calls the original code** — same as the Lua-detour form's, just invoked from your own assembly instead of via `mem.callf`.

The simplest realistic use: write a 3-byte stub that does nothing and returns, then redirect a function to it — e.g. neutering a `bool CanTakeDamage(...)` check so it always reports false:

```lua
-- xor eax, eax ; ret   — clears the return value (0/false) and returns.
local stub = mem.alloc(16)
mem.write(stub, string.char(0x31, 0xC0, 0xC3))

local trampoline = _29a.install(can_take_damage_addr, stub)
if not trampoline then
    print("hook failed")
end

-- later, to undo:
_29a.remove(can_take_damage_addr)
mem.free(stub)
```

If you need the original function's real result before overriding it — the "detour runs some logic, then calls the original" pattern — write a stub that saves registers, calls `trampoline` itself (its address is a compile-time constant once you know it, so this has to be assembled after `_29a.install` returns it, or looked up from a fixed scratch address you control), does its own native-only check, and returns. This is meaningfully harder than the Lua-detour form above, which can already do this same "call original, then decide" pattern in plain Lua — reach for this form only when the detour genuinely has to be native code.

### `_29a.remove(target) -> bool`

Removes whichever kind of detour is installed on `target` — Lua or native — restoring the original bytes and trampoline.

### `_29a.remove_all()`

Removes every installed detour at once.

* `hook.create` / `hook.remove` are direct aliases of `_29a.install` / `_29a.remove`, kept for compatibility with older scripts.

***

**Best practice:** always pair `hook.patch`/`_29a.install` with its removal counterpart inside a `__neuro_on_stop`, so reloading or stopping the script leaves the target process clean.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://0x29a.gitbook.io/0x29a-docs/runtime/hook.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
