From d164e3373108d0c0f83452fbea22f8569b84eec6 Mon Sep 17 00:00:00 2001 From: salmonik Date: Mon, 26 Oct 2026 17:53:40 +0100 Subject: Docs/cleanup: refresh CONTEXT, update ISurface status, remove dead code - rewrite CONTEXT.md for the current architecture, interfaces, netvars, menu tree, bones/ESP, aimbot/vischeck, triggerbot, silent aim and ABH - update ISURFACE_STATUS.md: DrawLine slot, current menu/config path, 9 build TUs, current file layout - remove the stale root-level `tree` file - drop unused class_health_off/per-class health scan (NPC HP is not networked), surf_call_slot, and unused scancode helpers --- CONTEXT.md | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 153 insertions(+), 32 deletions(-) (limited to 'CONTEXT.md') diff --git a/CONTEXT.md b/CONTEXT.md index 21128f7..1969781 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -1,47 +1,168 @@ # Context: HL2 Linux Internal Cheat (`hl2hake`) -## What is this project? +## What this is -A minimal, featureless cheat for Half-Life 2 (native Linux build, 32-bit `hl2_linux`). Originally ported from a Windows reference (`hake`) that implemented auto-bhop, auto-ABH, and auto-climb via a `CHLClient::CreateMove` vtable hook. +A 32-bit Linux internal for Half-Life 2 (native `hl2_linux`), loaded with +`LD_PRELOAD` through Steam's bundled 32-bit runtime. It started as a port of +the Windows `hake` reference (auto-bhop / auto-ABH / auto-climb) and now also +covers ESP, aimbot, triggerbot and silent aim. -## Resolved terms +## Build / run -- **Build**: LD_PRELOAD a 32-bit `.so` into `hl2_linux` via Steam's bundled 32-bit runtime (`steam-runtime/run.sh`). -- **Hook**: Copy-and-swap vtable on the `CHLClient` instance (slot 21 = `CreateMove`). 256-slot copy to cover all dispatched virtuals. -- **Netvars**: RecvTable walk from `IBaseClientDLL::GetAllClasses` (vtable slot 8). Prediction datamap scan for `CBaseEntity::m_MoveType` (networked `movetype` is a virtual proxy with offset 0). -- **CInput**: Discovered via byte-scan of `CHLClient::CreateMove` prologue (gcc PIC `call thunk; add $imm,%esi` idiom), resolving the module-level `IInput*` variable. -- **UserCMD CRC**: Standard CRC-32 (IEEE reflected, poly `0xEDB88320`, init `0xFFFFFFFF`). Field set and offsets verified against the binary's `CInput::ValidateUserCmd` disassembly. -- **Module discovery**: `/proc/self/maps` scan for `client.so` / `engine.so` basenames (exact match at `/` boundary). PROT_NONE (`---p`) entries excluded. Merged file-backed ranges absorb one directly-adjacent anonymous `.bss` tail; anon-to-anon chaining is prohibited. -- **Calling convention**: All game virtuals use **cdecl** with `this` as first stack arg (`0x8(%ebp)` in prologues, bare `ret`). No MSVC `__thiscall`. +- `./build.sh` -> `libhl2hake.so` + (`g++ -m32 -O2 -std=c++17 -Wall -Wextra -Werror -fPIC -shared`, + sources: `hl2h modules cinput netvars menu esp server bones vischeck`, `-ldl`). +- `./play.sh` -> Steam runtime + `hl2.sh -steam -novid -console +map d1_town_01`. +- Logs: `/tmp/hl2hake.log` (`logf`) and stderr. `./play.sh > /tmp/hl2run.log 2>&1` + captures everything. +- Debug env: + - `HL2H_DRAW_TEST=1` — red probe rect + text, no menu needed. + - `HL2H_BONE_DEBUG=1` — white marker at each NPC head hitbox. + - `HL2H_NO_SERVER=1` — disable server-side health reads. +- Config: `$XDG_CONFIG_HOME/hl2hake/config`, falling back to + `~/.config/hl2hake/config`. Plain INI, saved on every change. -## Netvar offsets (this build, verified via both RecvTable walk and pred-TD scan) +## Runtime architecture -| Field | Offset | Source | -|---|---|---| -| `m_fFlags` | `0x33C` | RecvTable + pred TD | -| `m_vecVelocity[0]` | `0xE0` | RecvTable (array, FIELD_VECTOR) | -| `m_MoveType` | `0x164` | pred-TD only (networked `movetype` has offset 0) | +- Modules from `/proc/self/maps`: `client.so`, `engine.so`, `server.so`. + PROT_NONE ranges excluded; file-backed bss tails merged. +- Calling convention: cdecl (i386); `this` is the first stack argument. +- Hooks (copy-and-swap vtable, 256 slots): + - `CHLClient` slot 21 `CreateMove`; slot 35 `FrameStageNotify`. + - `ISurface` slot 114 `PaintTraverseEx` (primary draw); + slot 22 `DrawPrintText` fallback. Draws wrapped in + `PushMakeCurrent`/`PopMakeCurrent`. +- `CInput*` discovered by byte-scanning the `CHLClient::CreateMove` prologue. + Usercmd CRC-32 is repaired after any cmd edit (`note_modified`). +- Console output via `ConMsg` from `libtier0.so` + (`_Z6ConMsgPKcz`), not `IVEngineClient::ClientCmd`. -## Key engine index table (IVEngineClient014, this build) +## Interfaces / indices -| Index | Method | Verification | +| Interface | Version | Notes | |---|---|---| -| 8 | *(not ClientCmd)* | Disasm: per-player index bounds check on `(arg-1)` — likely `GetPlayerInfo` | -| 9 | *(not ClientCmd_Unrestricted)* | Disasm: loop over split-screen players — no-op'd in single-player | -| 12 | `GetLocalPlayer` | Disasm: `return *(CClientState + 0x1b0) + 1` — confirmed in-game | -| 65 | `HudUpdate`-family | Crash-site (256-slot bump): `call *(this, float)` | +| Client | `VClient017` | | +| Entity list | `VClientEntityList003` | | +| Engine | `VEngineClient014` | | +| Model info | `VModelInfoClient006` | | +| Engine server | `VEngineServer021` | edicts / listen-server entities | +| Debug overlay | `VDebugOverlay003` | accurate world->screen | +| Engine trace | `EngineTraceClient003` | vischeck / trigger ray | +| Surface | `VGUI_Surface030` | | + +Relevant vtable slots (this build): + +- Engine: `GetLocalPlayer` 12, `Time` 14, `GetViewAngles` 19, `SetViewAngles` 20. +- Entity list: `GetClientNetworkable` 0, `GetClientEntity` 3, + `GetHighestEntityIndex` 6. +- `IClientNetworkable`: `GetClientClass` 2. `ClientClass::m_pNetworkName` at +8. +- `IClientUnknown`: `GetClientRenderable` 6. +- `IClientRenderable`: `GetModel` 9, `SetupBones` 16. +- `IVModelInfo`: `GetModel` 2, `GetModelName` 4, `GetStudiomodel` 29. +- `IVEngineServer`: `PEntityOfEntIndex` 19. + Edict `m_pUnk` at +0xC; `IServerUnknown::GetBaseEntity` 6. +- `IVDebugOverlay`: `ScreenPosition` 9. +- `IEngineTrace`: `TraceRay` 4. `Ray_t` uses 16-byte `VectorAligned`; + `trace_t`: `fraction` 44, `startsolid` 55, `m_pEnt` 76. + +## Netvars / entity data (this build) + +| Field | Client offset | +|---|---| +| `m_fFlags` | `0x33C` | +| `m_vecVelocity[0]` | `0xE0` | +| `m_MoveType` (prediction datamap) | `0x164` | +| `m_iHealth` | `0x80` | +| `m_iMaxHealth` | not networked | +| `m_iTeamNum` | `0x88` | +| `m_vecOrigin` | `0x324` | +| `m_vecViewOffset[0]` | `0xD4` | +| `m_vecMins` / `m_vecMaxs` | `0x1A8` / `0x1B4` | +| `m_lifeState` | `0x7F` | +| `m_nModelIndex` | `0x7C` | + +- Eye position = `m_vecOrigin + m_vecViewOffset` on **all three axes**. + (The old code added the X component to Z, which left the eye ~64 units low.) +- NPC health is not networked. Alive state comes from the networked client + `m_lifeState`; real HP is read from the listen-server `CBaseEntity` found via + `PEntityOfEntIndex -> edict->m_pUnk -> GetBaseEntity`, using offsets scanned + from `server.so`'s datamap: + - `m_iHealth` `0xF4`, `m_iMaxHealth` `0xF0`, + `m_lifeState` `0xF8`, `m_nModelIndex` `0x7A` (short). + +## Menu + +Starts fully collapsed on first open. `Insert` toggles; `Up`/`Down` navigate; +`Enter` expands/toggles; `Left`/`Right` edit values; `Escape` closes. +Bind rows enter bind-select mode: press a key/button to assign, `Escape` sets +None (always-on), `Enter` cancels. + +``` +HL2hake + Bunnyhop + Active + Uni-directional auto-strafe + ABH + Active + ESP + Active + Show neutral / healthbar / healthnum / name / skeleton / box + Aimbot + Active + Bind + Vischeck + Bones -> Head / Torso / Left arm / Right arm / Left leg / Right leg + Smoothness (1..45 deg/tick, Left/Right) + Triggerbot + Active + Bind + Silent aim + Active + Bind +``` + +Binds use SDL scancodes; mouse buttons are pseudo-scancodes 600..604. + +## Bones / ESP -Console output uses `ConMsg` from `libtier0.so` (resolved via `dlsym(RTLD_DEFAULT, "_Z6ConMsgPKcz")`), not `IVEngineClient::ClientCmd`. +- `SetupBones(BONE_USED_BY_ANYTHING)` with engine `Time()`; studio header via + `GetStudiomodel`; hitboxes and bone names/parents from that header. +- HL2 strips many ValveBiped bone names (the zombie's spine/legs/left arm are + unnamed), so the humanoid skeleton is built from the **union of hitbox bone + parent paths** (`HITGROUP_*`). Head group 1 includes the zombie headcrab body + and its `HC_Head` child; fingers, toes and other `HC_*` limbs are excluded. +- Non-humanoid NPCs (headcrabs, crows, antlions, ...) draw the full bone tree. +- `aim_points` = head hitbox/bone + hitbox centers for the selected groups; + the aim features pick the nearest visible point. -## Menu system +## Aim features -- **Toggle architecture**: each feature is runtime-toggleable, ON by default. A `MenuState` struct holds per-feature `active` bool and `bind` (key code or None). -- **Rendering**: hook `IEngineVGui::Paint` via `VEngineVGui001` — draws every frame including the main menu. Text-only overlay in top-left corner. -- **Persistence**: INI-style plain text config; one line per setting (`climb_active=1`, `climb_bind=MOUSE3`). Config written on every state change; loaded before hooks install. -- **Input model**: Insert toggles menu open/closed; arrow keys navigate the tree; Enter expands/collapses nodes and toggles boolean fields; Escape closes the menu. Modal while open (all game input blocked). Long-term: selective pass-through for unused inputs. -- **Bind assignment UX**: selecting a Bind node enters bind-select mode; any key press becomes the new bind; Escape sets bind to None (disabling the feature); Enter cancels bind change and reverts. -- **Tree layout**: HL2hake (root) → Climb (Active + Bind), Bunnyhop (Active), ABH (Active). Children indented. All nodes expanded by default when the menu opens. +- Shared target selection (`select_aim_target`): live hostile NPCs, skip + friendly/neutral, rank by angle to the crosshair, then pick the nearest + selected hitgroup point. Target candidates are tried nearest-first. +- Vischeck: `EngineTraceClient003::TraceRay`, `MASK_VISIBLE`, + `TRACE_WORLD_ONLY` (world only; static props are not tested — deliberate). +- Aimbot: bounded smoothing — 0.05 deg deadzone, max `Smoothness` deg/tick, + shortest-path yaw, pitch clamped to ±89; pushes the new view with + `SetViewAngles`. +- Triggerbot: crosshair ray with `MASK_SHOT` + `TRACE_EVERYTHING` (filter + ignores the local player). First hit must be a live hostile NPC; sets + `IN_ATTACK`. +- Silent aim: exact target angle written to `cmd->viewangles`, `IN_ATTACK`, + then the rendered view is restored immediately **and** at + `FRAME_RENDER_START`; `forwardmove`/`sidemove` are rewritten to preserve the + world-space movement wish vector. +- ABH: on the landing/jump tick, face opposite the horizontal velocity with + zero forward/side movement; HL2's jump speed-bound subtraction then + accelerates along the velocity. Camera restored at `FRAME_RENDER_START`. -## Open questions +## Gotchas / lessons -None currently. +- Never pipe-mask build failures: + `./build.sh > /tmp/build.log 2>&1; echo rc=$?`. +- `CHLClient` slot numbers: `CreateMove` 21, `FrameStageNotify` 35. +- `ISurface` binary slot = SDK header slot + 4; see `src/ISURFACE_STATUS.md`. +- Silent aim and ABH temporarily replace the usercmd view; silent aim preserves + movement, ABH intentionally zeroes it for the jump tick. +- Aimbot vischeck (`MASK_VISIBLE`/world-only) differs from triggerbot + (`MASK_SHOT`/all entities): glass and static props can behave differently. -- cgit v1.2.3