1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
# Context: HL2 Linux Internal Cheat (`hl2hake`)
## What this is
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.
## Build / run
- `./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.
## Runtime architecture
- 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`.
## Interfaces / indices
| Interface | Version | Notes |
|---|---|---|
| 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
- `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.
## Aim features
- 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`.
## Gotchas / lessons
- 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.
## Roadmap / TODO
- **Melee killbot**: 360° silent aim for hostile NPCs inside crowbar/melee
range, sorted by distance, only while the crowbar is held.
- **Jumpbug**: automate the HL2 jumpbug movement glitch (trigger conditions
to be defined/tested).
- **GUI**: replace/augment the VGUI ISurface menu with an immediate-mode GUI
(Dear ImGui or a custom implementation); needs a design grill first.
- **Distance weapon spread thesis**: TBD — user will explain. Likely models
weapon spread as a function of distance for aim/trigger decisions or visuals.
|