From 33f5ed9ef58f26c6b3b52cf605dd4311d658a60d Mon Sep 17 00:00:00 2001 From: salmonik Date: Fri, 6 Nov 2026 16:09:55 +0100 Subject: 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 --- src/hl2h.h | 50 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) (limited to 'src/hl2h.h') diff --git a/src/hl2h.h b/src/hl2h.h index 86573d8..f1a1ea0 100644 --- a/src/hl2h.h +++ b/src/hl2h.h @@ -11,6 +11,8 @@ #include #include +#include "overlay.h" + // Shared float-precision constant. More digits than a float can hold are // rounded at compile time and have no runtime cost; a coarse value would only // hurt angular accuracy, never performance. @@ -23,6 +25,31 @@ constexpr float PI_F = 3.14159265f; void logf(const char *fmt, ...); void logd(const char *fmt, ...); // debug-only (HL2H_DEBUG=1) +// --------------------------------------------------------------------------- +// init readiness +// --------------------------------------------------------------------------- +// +// setup_thread runs detached while the game threads are already live, and some +// hooks are installed lazily from the game thread. Hooks therefore always stay +// installed but pass straight through to the original until setup reaches +// INIT_LIVE, so the game can never observe half-initialised state. INIT_FAILED +// is terminal: the cheat stays inert instead of half-working. +enum { + INIT_FAILED = -1, + INIT_NONE = 0, + INIT_INTERFACES = 1, // modules + interfaces + netvars resolved + INIT_READY = 2, // config loaded, ISurface/fonts resolved + INIT_LIVE = 3 // hooks may run their bodies +}; +void init_set_stage(int stage); +int init_stage(); // acquire load +inline bool init_live() { return init_stage() == INIT_LIVE; } + +// Clone a vtable into a new array, preserving the Itanium ABI header at +// [-2]/[-1] (offset-to-top and typeinfo) so RTTI on the hooked object still +// works. Returns the slot-0 pointer to install (i.e. the clone + 2). +void **vtable_clone(void *iface, size_t slots); + // --------------------------------------------------------------------------- // on-screen toasts // --------------------------------------------------------------------------- @@ -51,7 +78,17 @@ struct Module { return false; } }; -extern Module g_client, g_engine, g_server; +// Immutable module snapshots. A reader gets a reference that stays valid for +// the whole process: find_modules() publishes a new snapshot instead of +// mutating the old one, and retired snapshots are intentionally never freed, so +// a concurrent refresh can never invalidate a reference already in use. +const Module &mod_client(); +const Module &mod_engine(); +const Module &mod_server(); + +// True only when the whole byte range [p, p+n) lies inside one known module +// range. Use before dereferencing pointers read out of engine memory. +bool module_range(uintptr_t p, size_t n); // --------------------------------------------------------------------------- // game interfaces @@ -308,6 +345,9 @@ void skybox_invalidate(); void skybox_reload_image(); bool resolve_server_netvars(); int server_entity_health(int idx, int *maxhealth, int *lifestate, void **base_out); +// Authoritative server velocity; client-side m_vecVelocity is not networked for +// NPCs. Returns false when the server interface or datamap field is missing. +bool server_entity_velocity(int idx, Vector *out); bool server_set_velocity_z(int idx, float vz); int server_active_weapon(int idx); int server_npc_relation(int idx, int local_idx); @@ -332,7 +372,8 @@ void find_modules(); bool module_jump_slot(const char *path, const char *name, uintptr_t *vaddr, uintptr_t *bias); void init_conmsg(); -bool feature_active(int feature); +bool feature_active(int feature); // enabled AND bind held (or unbound) +bool feature_enabled(int feature); // enabled, regardless of bind bool feature_has_bind(int feature); bool feature_bind_held(int feature); bool esp_option(int opt); @@ -359,6 +400,8 @@ int engine_get_local_player(); void draw_text_col(int x, int y, const char *s, int r, int g, int b); void draw_text_small_col(int x, int y, const char *s, int r, int g, int b); void esp_draw(); +void esp_draw_fov_circle(); // aimbot cone, independent of the ESP toggle +void esp_reset_session(); // map change / respawn: clear per-map latches void esp_draw_items(); void esp_draw_projectiles(); void prop_tint_update(); @@ -395,7 +438,4 @@ bool gui_bind_capture_active(); QAngle gui_frozen_view(); void gui_config_init(); void gui_config_write(FILE *f); -// ISurface pointer (defined in menu.cpp, used for drawing tests) -struct ISurface; -extern ISurface *g_surface; #endif -- cgit v1.2.3