Skip to main content

Crate dynoffsets

Crate dynoffsets 

Source
Expand description

Resolve struct field offsets, module globals, Source 2 interfaces and button addresses at runtime with compile-time literal fallbacks.

Declare modules with the attribute macros below. When the runtime feature is enabled and init has been called with a Process implementation, the generated accessors return live values from the target process. Without the feature (or before init), they return the declared literal.

The crate supports #![no_std] + alloc.

§Using With MinHook

dynoffsets does not provide a hook engine. It resolves runtime addresses; a library such as MinHook installs the detour.

The usual flow is:

  1. Resolve a live interface pointer with interfaces.
  2. Read the vtable slot for the method you want to hook.
  3. Pass that function entry address to MinHook.

schema and globals usually resolve data addresses rather than hook targets, so interfaces is the most common fit for MinHook-based setups.

use core::{ffi::c_void, mem, ptr};

use dynoffsets::interfaces;
use minhook_sys::{MH_CreateHook, MH_EnableHook, MH_Initialize, MH_OK};

#[interfaces("engine2.dll")]
mod engine2 {
    pub const Source2EngineToClient001: usize = 0;
}

type TargetFn = unsafe extern "system" fn(this: *mut c_void, arg: i32) -> i32;

static mut ORIGINAL_TARGET: Option<TargetFn> = None;

unsafe extern "system" fn hk_target(this: *mut c_void, arg: i32) -> i32 {
    let original = ORIGINAL_TARGET.expect("hook not installed");
    original(this, arg)
}

unsafe fn vfunc(instance: usize, index: usize) -> *mut c_void {
    let vtable = *(instance as *const *const usize);
    *vtable.add(index) as *mut c_void
}

unsafe fn install_hook() {
    let iface = engine2::Source2EngineToClient001();
    assert_ne!(iface, 0, "interface was not resolved");

    let target = vfunc(iface, 42);

    assert_eq!(MH_Initialize(), MH_OK);

    let mut original = ptr::null_mut();
    assert_eq!(
        MH_CreateHook(target, hk_target as *mut c_void, &mut original),
        MH_OK,
    );

    ORIGINAL_TARGET = Some(mem::transmute(original));

    assert_eq!(MH_EnableHook(target), MH_OK);
}

The same pattern works for exported functions when your backend can resolve the function entry address.

Structs§

PopulateStats
RuntimeButtons
Button name → address of its state: u32 field, walked from the live g_pButtonList linked list in client.dll.
RuntimeGlobals
Live globals from pattern scan; None on signature miss.
RuntimeInterfaces
Map of module → interface name → instance pointer obtained by walking each module’s CreateInterface registration list.

Traits§

Process
Memory access trait for the runtime dumper.

Functions§

discover_buttons
Walk the live KeyButton linked list in client.dll and collect button name → &state mappings.
discover_globals
Discover engine globals via patterns (for #[globals] macro).
discover_interfaces
Walk CreateInterface registration chains for all known CS2 modules and return the collected interface instances.
discover_interfaces_in
Walk CreateInterface for a caller-provided list of modules.
fnv1a
FNV-1a 32-bit hash (const-eval).
get_runtime_buttons
Returns the discovered button state addresses from client.dll’s KeyButton list.
get_runtime_globals
Returns the lazily-discovered global pointers (pattern scan).
get_runtime_interfaces
Returns the discovered Source 2 interfaces (via CreateInterface chains).
init
Register the process backend used by all runtime accessors.
is_initialized
Returns true after init has been called.
lookup_or_fallback
Internal helper used by the #[schema] macro.
lookup_or_fallback_h
Hash-keyed variant of lookup_or_fallback.
populate
Populates registered r#static slots via live discovery (or no-op). Returns per-category counts. See PopulateStats.
try_lookup_offset
Internal helper used by the cached #[schema] accessor.
try_lookup_offset_h
Hash-keyed variant of try_lookup_offset.

Type Aliases§

Slot
(name, &AtomicUsize) for a r#static slot.

Attribute Macros§

buttons
#[buttons] / #[buttons(r#static)]
globals
#[globals] / #[globals(r#static)]
interfaces
#[interfaces] / #[interfaces("dll")] / #[interfaces(false)] / #[interfaces(..., r#static)]
schema
#[schema] / #[schema("client.dll")] / #[schema(false)] / #[schema(..., r#static)] / #[schema(hashed)]