android_game_hook/
android_game_hook.rs1use substrate::utils::{get_absolute_address, is_library_loaded, string_to_offset};
2use substrate::MSHookFunction;
3use std::ffi::c_void;
4use std::thread;
5use std::time::Duration;
6
7static mut OLD_UPDATE: *mut c_void = std::ptr::null_mut();
8static mut OLD_FIXED_UPDATE: *mut c_void = std::ptr::null_mut();
9
10#[repr(C)]
11struct UnityObject {
12 vtable: *mut c_void,
13 data: *mut c_void,
14}
15
16unsafe extern "C" fn hooked_update(instance: *mut UnityObject) {
17 println!("[Hook] Update() called - instance: {:p}", instance);
18
19 if !OLD_UPDATE.is_null() {
20 let original: extern "C" fn(*mut UnityObject) = std::mem::transmute(OLD_UPDATE);
21 original(instance);
22 }
23}
24
25unsafe extern "C" fn hooked_fixed_update(instance: *mut UnityObject) {
26 println!("[Hook] FixedUpdate() called - instance: {:p}", instance);
27
28 if !OLD_FIXED_UPDATE.is_null() {
29 let original: extern "C" fn(*mut UnityObject) = std::mem::transmute(OLD_FIXED_UPDATE);
30 original(instance);
31 }
32}
33
34fn wait_for_library(lib_name: &str, timeout_secs: u64) -> bool {
35 let start = std::time::Instant::now();
36 while start.elapsed().as_secs() < timeout_secs {
37 if is_library_loaded(lib_name) {
38 return true;
39 }
40 thread::sleep(Duration::from_millis(100));
41 }
42 false
43}
44
45fn main() {
46 println!("=== Android Game Hook Example (IL2CPP/Unity) ===\n");
47
48 let library = "libil2cpp.so";
49
50 let update_offset_str = "0x123456";
51 let fixed_update_offset_str = "0x789ABC";
52
53 println!("Waiting for {} to load...", library);
54
55 if wait_for_library(library, 30) {
56 println!("✓ {} loaded!", library);
57
58 unsafe {
59 match string_to_offset(update_offset_str) {
60 Ok(update_offset) => {
61 println!("\nHooking Update() at offset: 0x{:X}", update_offset);
62
63 match get_absolute_address(library, update_offset) {
64 Ok(addr) => {
65 println!("Absolute address: 0x{:x}", addr);
66
67 MSHookFunction(
68 addr as *mut c_void,
69 hooked_update as *mut c_void,
70 &mut OLD_UPDATE
71 );
72
73 if !OLD_UPDATE.is_null() {
74 println!("✓ Update() hooked successfully!");
75 }
76 }
77 Err(e) => eprintln!("✗ Failed to get address: {}", e),
78 }
79 }
80 Err(e) => eprintln!("✗ Invalid offset: {}", e),
81 }
82
83 match string_to_offset(fixed_update_offset_str) {
84 Ok(fixed_offset) => {
85 println!("\nHooking FixedUpdate() at offset: 0x{:X}", fixed_offset);
86
87 match get_absolute_address(library, fixed_offset) {
88 Ok(addr) => {
89 println!("Absolute address: 0x{:x}", addr);
90
91 MSHookFunction(
92 addr as *mut c_void,
93 hooked_fixed_update as *mut c_void,
94 &mut OLD_FIXED_UPDATE
95 );
96
97 if !OLD_FIXED_UPDATE.is_null() {
98 println!("✓ FixedUpdate() hooked successfully!");
99 }
100 }
101 Err(e) => eprintln!("✗ Failed to get address: {}", e),
102 }
103 }
104 Err(e) => eprintln!("✗ Invalid offset: {}", e),
105 }
106
107 println!("\n=== Hooks Installed ===");
108 println!("The game functions will now call your hooks.");
109 println!("\nNote: Replace the offset values with real ones from your game!");
110 }
111 } else {
112 eprintln!("✗ Timeout waiting for {} to load", library);
113 eprintln!("\nTo use this example:");
114 eprintln!("1. Find function offsets using IDA Pro, Ghidra, or similar");
115 eprintln!("2. Replace the offset strings with actual values");
116 eprintln!("3. Run as part of an injected library in the game process");
117 }
118}