diff options
| author | salmonik <salmonik@tuta.io> | 2026-11-06 16:09:55 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-11-06 16:09:55 +0100 |
| commit | 33f5ed9ef58f26c6b3b52cf605dd4311d658a60d (patch) | |
| tree | a97254872e465c9107b7ef19695ab56bb076d7e2 /src/hl2h.h | |
| parent | Items: log the usable ammo table once (diff) | |
| download | hl2h-33f5ed9ef58f26c6b3b52cf605dd4311d658a60d.tar.gz hl2h-33f5ed9ef58f26c6b3b52cf605dd4311d658a60d.zip | |
Fix audit findings: crash safety, correctness, rendering, performance
Crash/UB:
- gate every hook behind an init-stage flag until config/surface/input are ready
- publish module maps as immutable atomic snapshots instead of reallocating
shared vectors under unlocked readers
- validate the %ebp walk in the RandomSeed/RandomFloat hooks against the stack
- bounds-check every RecvTable/RecvProp/string dereference with module_range()
- preserve the Itanium vtable header when cloning (client, CInput, model render,
studio render)
- reject negative usercmd sequence numbers; guard config_load against a null
section target; cap the bone parent walk; check mprotect; drop PROT_EXEC on
the GOT patch; absorb server.so .bss tail
Correctness:
- filter dormant entities across ESP, aimbot, triggerbot, melee, items and
projectiles (slot self-tested against the local player)
- reset the server-health latch per map and only latch on valid model indices
- bone cache sized for the 128-target scan; set valid=false on every failure;
head_position keeps searching past an invalid bone
- vischeck steps past glass by probing until clear, and ignores entities owned
by the target (held weapons, bone followers)
- aimbot: maxs no longer disables it, AABB-aware FOV prefilter, lock hysteresis,
lead point re-checked for FOV and visibility
- spread: seed hook arms on the no-spread toggle alone; engine toggle only
controls the RandomFloat zeroing
- resolve m_hOwnerEntity/m_hOwner; NPC lead velocity from the server entity
- vector_angles pitch range, point_ground normal test, underwater jump+duck no
longer skips combat, semi-auto press/release edge for synthetic fire
- item/projectile markers and blast rings refresh origin per frame while
discovery stays at 10 Hz; trajectory uses the real m_DmgRadius and honours
the blast-radius toggle
Rendering/GUI:
- one overlay draw per engine frame (FRAME_RENDER_START boundary), fixing
alpha stacking and the speed-graph flicker
- clipped lines/outlines and ellipsised text instead of drawing past the
content pane or shifting over neighbours
- dropdown dismissal consumes the click; hover ignores hidden controls
- chams: reset AlphaModulate, reference shared materials once, restore prop
tint colour; fonts created once per handle
- projection uses Source's 4:3 aspect scaling and roll; screen size refreshed
each frame; FOV circle gated on the aim toggles, not the bind
Performance:
- TTL caches for NPC relation, friendly/neutral and damage class
- model names memoised per model index
- entity pointer -> index cache for the triggerbot (was a 4096-slot walk/tick)
Build:
- real Makefile with -MMD dependency tracking, debug/release objects and an
always-relink rule; build.sh kept as a thin wrapper
Diffstat (limited to 'src/hl2h.h')
| -rw-r--r-- | src/hl2h.h | 50 |
1 files changed, 45 insertions, 5 deletions
| @@ -11,6 +11,8 @@ | |||
| 11 | #include <pthread.h> | 11 | #include <pthread.h> |
| 12 | #include <unistd.h> | 12 | #include <unistd.h> |
| 13 | 13 | ||
| 14 | #include "overlay.h" | ||
| 15 | |||
| 14 | // Shared float-precision constant. More digits than a float can hold are | 16 | // Shared float-precision constant. More digits than a float can hold are |
| 15 | // rounded at compile time and have no runtime cost; a coarse value would only | 17 | // rounded at compile time and have no runtime cost; a coarse value would only |
| 16 | // hurt angular accuracy, never performance. | 18 | // hurt angular accuracy, never performance. |
| @@ -24,6 +26,31 @@ void logf(const char *fmt, ...); | |||
| 24 | void logd(const char *fmt, ...); // debug-only (HL2H_DEBUG=1) | 26 | void logd(const char *fmt, ...); // debug-only (HL2H_DEBUG=1) |
| 25 | 27 | ||
| 26 | // --------------------------------------------------------------------------- | 28 | // --------------------------------------------------------------------------- |
| 29 | // init readiness | ||
| 30 | // --------------------------------------------------------------------------- | ||
| 31 | // | ||
| 32 | // setup_thread runs detached while the game threads are already live, and some | ||
| 33 | // hooks are installed lazily from the game thread. Hooks therefore always stay | ||
| 34 | // installed but pass straight through to the original until setup reaches | ||
| 35 | // INIT_LIVE, so the game can never observe half-initialised state. INIT_FAILED | ||
| 36 | // is terminal: the cheat stays inert instead of half-working. | ||
| 37 | enum { | ||
| 38 | INIT_FAILED = -1, | ||
| 39 | INIT_NONE = 0, | ||
| 40 | INIT_INTERFACES = 1, // modules + interfaces + netvars resolved | ||
| 41 | INIT_READY = 2, // config loaded, ISurface/fonts resolved | ||
| 42 | INIT_LIVE = 3 // hooks may run their bodies | ||
| 43 | }; | ||
| 44 | void init_set_stage(int stage); | ||
| 45 | int init_stage(); // acquire load | ||
| 46 | inline bool init_live() { return init_stage() == INIT_LIVE; } | ||
| 47 | |||
| 48 | // Clone a vtable into a new array, preserving the Itanium ABI header at | ||
| 49 | // [-2]/[-1] (offset-to-top and typeinfo) so RTTI on the hooked object still | ||
| 50 | // works. Returns the slot-0 pointer to install (i.e. the clone + 2). | ||
| 51 | void **vtable_clone(void *iface, size_t slots); | ||
| 52 | |||
| 53 | // --------------------------------------------------------------------------- | ||
| 27 | // on-screen toasts | 54 | // on-screen toasts |
| 28 | // --------------------------------------------------------------------------- | 55 | // --------------------------------------------------------------------------- |
| 29 | 56 | ||
| @@ -51,7 +78,17 @@ struct Module { | |||
| 51 | return false; | 78 | return false; |
| 52 | } | 79 | } |
| 53 | }; | 80 | }; |
| 54 | extern Module g_client, g_engine, g_server; | 81 | // Immutable module snapshots. A reader gets a reference that stays valid for |
| 82 | // the whole process: find_modules() publishes a new snapshot instead of | ||
| 83 | // mutating the old one, and retired snapshots are intentionally never freed, so | ||
| 84 | // a concurrent refresh can never invalidate a reference already in use. | ||
| 85 | const Module &mod_client(); | ||
| 86 | const Module &mod_engine(); | ||
| 87 | const Module &mod_server(); | ||
| 88 | |||
| 89 | // True only when the whole byte range [p, p+n) lies inside one known module | ||
| 90 | // range. Use before dereferencing pointers read out of engine memory. | ||
| 91 | bool module_range(uintptr_t p, size_t n); | ||
| 55 | 92 | ||
| 56 | // --------------------------------------------------------------------------- | 93 | // --------------------------------------------------------------------------- |
| 57 | // game interfaces | 94 | // game interfaces |
| @@ -308,6 +345,9 @@ void skybox_invalidate(); | |||
| 308 | void skybox_reload_image(); | 345 | void skybox_reload_image(); |
| 309 | bool resolve_server_netvars(); | 346 | bool resolve_server_netvars(); |
| 310 | int server_entity_health(int idx, int *maxhealth, int *lifestate, void **base_out); | 347 | int server_entity_health(int idx, int *maxhealth, int *lifestate, void **base_out); |
| 348 | // Authoritative server velocity; client-side m_vecVelocity is not networked for | ||
| 349 | // NPCs. Returns false when the server interface or datamap field is missing. | ||
| 350 | bool server_entity_velocity(int idx, Vector *out); | ||
| 311 | bool server_set_velocity_z(int idx, float vz); | 351 | bool server_set_velocity_z(int idx, float vz); |
| 312 | int server_active_weapon(int idx); | 352 | int server_active_weapon(int idx); |
| 313 | int server_npc_relation(int idx, int local_idx); | 353 | int server_npc_relation(int idx, int local_idx); |
| @@ -332,7 +372,8 @@ void find_modules(); | |||
| 332 | bool module_jump_slot(const char *path, const char *name, uintptr_t *vaddr, | 372 | bool module_jump_slot(const char *path, const char *name, uintptr_t *vaddr, |
| 333 | uintptr_t *bias); | 373 | uintptr_t *bias); |
| 334 | void init_conmsg(); | 374 | void init_conmsg(); |
| 335 | bool feature_active(int feature); | 375 | bool feature_active(int feature); // enabled AND bind held (or unbound) |
| 376 | bool feature_enabled(int feature); // enabled, regardless of bind | ||
| 336 | bool feature_has_bind(int feature); | 377 | bool feature_has_bind(int feature); |
| 337 | bool feature_bind_held(int feature); | 378 | bool feature_bind_held(int feature); |
| 338 | bool esp_option(int opt); | 379 | bool esp_option(int opt); |
| @@ -359,6 +400,8 @@ int engine_get_local_player(); | |||
| 359 | void draw_text_col(int x, int y, const char *s, int r, int g, int b); | 400 | void draw_text_col(int x, int y, const char *s, int r, int g, int b); |
| 360 | void draw_text_small_col(int x, int y, const char *s, int r, int g, int b); | 401 | void draw_text_small_col(int x, int y, const char *s, int r, int g, int b); |
| 361 | void esp_draw(); | 402 | void esp_draw(); |
| 403 | void esp_draw_fov_circle(); // aimbot cone, independent of the ESP toggle | ||
| 404 | void esp_reset_session(); // map change / respawn: clear per-map latches | ||
| 362 | void esp_draw_items(); | 405 | void esp_draw_items(); |
| 363 | void esp_draw_projectiles(); | 406 | void esp_draw_projectiles(); |
| 364 | void prop_tint_update(); | 407 | void prop_tint_update(); |
| @@ -395,7 +438,4 @@ bool gui_bind_capture_active(); | |||
| 395 | QAngle gui_frozen_view(); | 438 | QAngle gui_frozen_view(); |
| 396 | void gui_config_init(); | 439 | void gui_config_init(); |
| 397 | void gui_config_write(FILE *f); | 440 | void gui_config_write(FILE *f); |
| 398 | // ISurface pointer (defined in menu.cpp, used for drawing tests) | ||
| 399 | struct ISurface; | ||
| 400 | extern ISurface *g_surface; | ||
| 401 | #endif | 441 | #endif |
