vtable_hook 0.1.1

Simple crate for hooking C++ VTables
Documentation
#[derive(Debug)]
#[repr(C)]
pub struct CppClass {
    pub vtable: *const CppClassVTable,
    /* other fields... */
}

impl Default for CppClass {
    fn default() -> Self {
        static VTABLE: CppClassVTable = CppClassVTable { foo: foo_original };

        Self { vtable: &VTABLE }
    }
}

#[derive(Debug)]
#[repr(C)]
pub struct CppClassVTable {
    pub foo: unsafe extern "system" fn(thisptr: *const CppClass) -> std::os::raw::c_int,
    /* other methods... */
}

unsafe extern "system" fn foo_original(_thisptr: *const CppClass) -> std::os::raw::c_int {
    0
}

unsafe extern "system" fn foo_hooked(_thisptr: *const CppClass) -> std::os::raw::c_int {
    1
}

fn main() {
    unsafe {
        /* The same classes, but one is our victim, and the other is unaffected. */
        let mut victim_cpp_class = CppClass::default();
        let unaffected_cpp_class = CppClass::default();

        /* Setting up raw hook */
        let original_vtable = vtable_hook::VTable::new_with_size(victim_cpp_class.vtable as _, 1);
        let mut raw_hook = vtable_hook::hook::copy::raw::RawHook::new(
            &mut victim_cpp_class.vtable as *mut _ as _,
            Some(original_vtable),
        );
        eprintln!("Raw hook: {raw_hook:#?}");

        /* Hooks are unset now, let's test that its true */
        eprintln!("-- Hook is disabled -- ");
        eprintln!(
            "victim_cpp_class's raw_hook is_enabled {}",
            raw_hook.is_enabled()
        );
        eprintln!(
            "victim_cpp_class foo() result = {}",
            (victim_cpp_class.vtable.read().foo)(&victim_cpp_class)
        );
        eprintln!(
            "unaffected_cpp_class foo() result = {}",
            (unaffected_cpp_class.vtable.read().foo)(&unaffected_cpp_class)
        );
        /* victim_cpp_class's raw_hook is_enabled false
         * victim_cpp_class foo() result = 0
         * unaffected_cpp_class foo() result = 0 */

        /* Replacing foo inside raw_hook */
        raw_hook.replace_method(0, foo_hooked as _);
        /* Replacing victim_cpp_class's VTable */
        raw_hook.enable();
        /* Testing */
        eprintln!("-- Hook is enabled -- ");
        eprintln!(
            "victim_cpp_class's raw_hook is_enabled {}",
            raw_hook.is_enabled()
        );
        eprintln!(
            "victim_cpp_class foo() result = {}",
            (victim_cpp_class.vtable.read().foo)(&victim_cpp_class)
        );
        eprintln!(
            "unaffected_cpp_class foo() result = {}",
            (unaffected_cpp_class.vtable.read().foo)(&unaffected_cpp_class)
        );
        /* victim_cpp_class's raw_hook is_enabled true
         * victim_cpp_class foo() result = 1
         * unaffected_cpp_class foo() result = 0 */

        /* Restoring victim_cpp_class's VTable */
        raw_hook.disable();
        /* NOTE: raw_hook's foo method still points to our function,
         * but we have restored original VTable inside victim_cpp_class
         * To restore methods inside raw_hook use raw_hook.restore_method()
         * or raw_hook.restore_all() */
        eprintln!("-- Hook is disabled -- ");
        eprintln!(
            "victim_cpp_class's raw_hook is_enabled {}",
            raw_hook.is_enabled()
        );
        eprintln!(
            "victim_cpp_class foo() result = {}",
            (victim_cpp_class.vtable.read().foo)(&victim_cpp_class)
        );
        eprintln!(
            "unaffected_cpp_class foo() result = {}",
            (unaffected_cpp_class.vtable.read().foo)(&unaffected_cpp_class)
        );
        /* victim_cpp_class's raw_hook is_enabled false
         * victim_cpp_class foo() result = 0
         * unaffected_cpp_class foo() result = 0 */
    }
}