# Substrate - Powerful Code Injection Platform
[](https://crates.io/crates/substrate-rs)
[](https://docs.rs/substrate-rs)
[](https://www.gnu.org/licenses/lgpl-3.0)
A complete Rust rewrite of Cydia Substrate and And64InlineHook, providing powerful function hooking capabilities for Android and Linux platforms across multiple architectures.
## Features
- ✅ **Multi-Architecture Support**
- x86/x86-64 (Intel/AMD 32-bit and 64-bit)
- ARMv7 (32-bit ARM with Thumb/Thumb-2)
- ARM64/AArch64 (64-bit ARM)
- ✅ **Complete Function Hooking**
- Inline function hooking with automatic trampoline generation
- PC-relative instruction relocation
- Symbol resolution from ELF binaries
- Library base address lookup
- ✅ **Dual API**
- C-compatible FFI for use in C/C++ projects
- Safe, idiomatic Rust API
- Drop-in replacement for Cydia Substrate
- ✅ **Production Ready**
- Zero unsafe behavior leaks
- Comprehensive error handling
- Memory-safe by default
- Thoroughly tested across architectures
## Installation
Add this to your `Cargo.toml`:
```toml
[dependencies]
substrate = "0.1"
```
Or for C/C++ projects, download the prebuilt library from releases.
## Quick Start
### Rust Usage
```rust
use substrate::{hook_function, utils};
// Define your hook function
unsafe extern "C" fn my_hooked_function(arg: i32) -> i32 {
println!("Hooked! arg = {}", arg);
// Call original if needed
arg + 100
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
unsafe {
// Find the library base address
let lib_base = utils::find_library("libexample.so")?;
// Calculate absolute address
let target_addr = lib_base + 0x1234;
// Hook the function
let original = hook_function(
target_addr as *mut u8,
my_hooked_function as *mut u8
)?;
println!("Hook installed! Original at: {:p}", original);
}
Ok(())
}
```
### C/C++ Usage
```cpp
#include <substrate.h>
// Original function pointer
void (*old_FixedUpdate)(void *instance);
// Hook function
void hooked_FixedUpdate(void *instance) {
// Your code here
printf("FixedUpdate hooked!\n");
// Call original
if (old_FixedUpdate) {
old_FixedUpdate(instance);
}
}
int main() {
// Hook using offset
MSHookFunction(
(void *)getAbsoluteAddress("libil2cpp.so", 0x123456),
(void *)hooked_FixedUpdate,
(void **)&old_FixedUpdate
);
return 0;
}
```
## Complete Examples
### Example 1: Hooking with Symbol Name
```rust
use substrate::{hook_function, utils};
unsafe extern "C" fn my_malloc(size: usize) -> *mut u8 {
println!("Allocating {} bytes", size);
// Call original malloc through dlsym or saved pointer
std::ptr::null_mut()
}
fn main() {
unsafe {
// Find malloc in libc
let malloc_addr = libc::dlsym(
libc::RTLD_DEFAULT,
b"malloc\0".as_ptr() as *const i8
);
if !malloc_addr.is_null() {
let original = hook_function(
malloc_addr as *mut u8,
my_malloc as *mut u8
).expect("Failed to hook malloc");
println!("malloc hooked! Original: {:p}", original);
}
}
}
```
### Example 2: Hooking Game Functions (Android/IL2CPP)
```rust
use substrate::utils::{get_absolute_address, is_library_loaded};
use substrate::MSHookFunction;
use std::ffi::c_void;
static mut OLD_UPDATE: *mut c_void = std::ptr::null_mut();
unsafe extern "C" fn hooked_update(instance: *mut c_void) {
println!("Game Update() called!");
// Modify game behavior here
// Call original
if !OLD_UPDATE.is_null() {
let original: extern "C" fn(*mut c_void) =
std::mem::transmute(OLD_UPDATE);
original(instance);
}
}
fn main() {
unsafe {
// Wait for library to load
while !is_library_loaded("libil2cpp.so") {
std::thread::sleep(std::time::Duration::from_millis(100));
}
// Hook Update function at offset 0x123456
let update_addr = get_absolute_address("libil2cpp.so", 0x123456)
.expect("Failed to find libil2cpp.so");
MSHookFunction(
update_addr as *mut c_void,
hooked_update as *mut c_void,
&mut OLD_UPDATE
);
println!("Update() hooked successfully!");
}
}
```
### Example 3: Using Helper Macros (C-style)
```rust
use substrate::utils::*;
// Convert hex string to offset
fn hook_with_string_offset() {
unsafe {
let offset = string_to_offset("0x123456")
.expect("Invalid offset");
let addr = get_absolute_address("libgame.so", offset)
.expect("Library not found");
println!("Target address: 0x{:x}", addr);
}
}
```
### Example 4: Architecture-Specific Hooking
```rust
use substrate;
fn hook_based_on_arch() {
#[cfg(target_arch = "aarch64")]
unsafe {
// ARM64-specific hooking
substrate::A64HookFunction(
target as *mut std::ffi::c_void,
hook as *mut std::ffi::c_void,
&mut original as *mut *mut std::ffi::c_void
);
}
#[cfg(target_arch = "arm")]
unsafe {
// ARMv7-specific hooking
substrate::MSHookFunction(
target as *mut std::ffi::c_void,
hook as *mut std::ffi::c_void,
&mut original as *mut *mut std::ffi::c_void
);
}
}
```
## API Reference
### Core Functions
#### `MSHookFunction`
```c
void MSHookFunction(void *symbol, void *replace, void **result);
```
Hook a function at the given address. Compatible with original Cydia Substrate.
**Parameters:**
- `symbol`: Target function address to hook
- `replace`: Your hook function address
- `result`: Pointer to store original function (trampoline), can be NULL
#### `A64HookFunction`
```c
void A64HookFunction(void *symbol, void *replace, void **result);
```
ARM64-specific hook function (alias to MSHookFunction on ARM64).
### Utility Functions
#### `findLibrary`
```c
uintptr_t findLibrary(const char *library);
```
Find the base address of a loaded library.
**Example:**
```c
uintptr_t base = findLibrary("libil2cpp.so");
```
#### `getAbsoluteAddress`
```c
uintptr_t getAbsoluteAddress(const char *library, uintptr_t offset);
```
Calculate absolute address from library name and offset.
**Example:**
```c
uintptr_t addr = getAbsoluteAddress("libil2cpp.so", 0x123456);
```
#### `isLibraryLoaded`
```c
bool isLibraryLoaded(const char *library);
```
Check if a library is currently loaded.
**Example:**
```c
if (isLibraryLoaded("libunity.so")) {
// Library is loaded
}
```
#### `string2Offset`
```c
uintptr_t string2Offset(const char *str);
```
Convert hex string (e.g., "0x123456") to numeric offset.
**Example:**
```c
uintptr_t offset = string2Offset("0x123456");
```
#### `hook`
```c
void hook(void *offset, void *ptr, void **orig);
```
Universal hook function that dispatches to the correct implementation.
### Rust API
```rust
// Safe wrapper for hooking
pub unsafe fn hook_function<T>(
symbol: *mut T,
replace: *mut T
) -> Result<*mut T>
// Utility functions
pub fn find_library(library_name: &str) -> Result<usize>
pub fn get_absolute_address(library_name: &str, offset: usize) -> Result<usize>
pub fn is_library_loaded(library_name: &str) -> bool
pub fn string_to_offset(s: &str) -> Result<usize>
// Symbol resolution
pub fn find_symbol_in_process(
pid: libc::pid_t,
library: &str,
symbol: &str
) -> Result<*mut c_void>
```
## Platform Support
| Linux | ✅ | ✅ | ✅ | ✅ | Full |
| Android | ✅ | ✅ | ✅ | ✅ | Full |
| macOS | ❌ | ⚠️ | ❌ | ⚠️ | Untested |
## Building
```bash
# Debug build
cargo build
# Release build (optimized)
cargo build --release
# With debug logging
cargo build --release --features debug
# Cross-compile for Android ARM64
cargo build --release --target aarch64-linux-android
# Cross-compile for Android ARMv7
cargo build --release --target armv7-linux-androideabi
```
## Advanced Usage
### Custom Error Handling
```rust
use substrate::error::SubstrateError;
match hook_function(target, hook) {
Ok(original) => println!("Success! Original: {:p}", original),
Err(SubstrateError::NullPointer) => eprintln!("Null pointer!"),
Err(SubstrateError::HookFailed(msg)) => eprintln!("Hook failed: {}", msg),
Err(e) => eprintln!("Error: {}", e),
}
```
### Enable Debug Logging
```rust
use substrate::{set_debug, is_debug};
// Enable debug output
set_debug(true);
// Check if debug is enabled
if is_debug() {
println!("Debug mode active");
}
```
### Memory Safety
The library uses RAII and safe abstractions internally, but the public API requires `unsafe` due to the nature of function hooking:
```rust
unsafe {
// All hooking must be in unsafe blocks
let orig = hook_function(target, replacement)?;
// Safe to use the trampoline
let original_fn: extern "C" fn() = std::mem::transmute(orig);
original_fn();
}
```
## Technical Details
### Architecture-Specific Implementation
- **x86/x86-64**: Full instruction decoder (HDE64), handles RIP-relative addressing
- **ARMv7**: Separate ARM and Thumb mode handlers, PC-relative instruction relocation
- **ARM64**: Complete instruction fixing for all PC-relative types (B, BL, CBZ, LDR, ADR, ADRP, etc.)
### Trampoline Generation
The library automatically:
1. Disassembles instructions at the target
2. Relocates PC-relative instructions
3. Creates a trampoline with original code
4. Installs jump to hook function
5. Returns pointer to trampoline (original function)
### Memory Protection
Automatically handles:
- Memory page protection (mprotect)
- Instruction cache clearing
- Alignment requirements
- Permission restoration
## License
This project is licensed under the GNU Lesser General Public License v3.0 (LGPL-3.0).
Original Cydia Substrate: Copyright (C) 2008-2011 Jay Freeman (saurik)
And64InlineHook: Copyright (C) 2018 Rprop (MIT License)
Rust Implementation: 2024
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## Acknowledgments
- Jay Freeman (saurik) - Original Cydia Substrate
- Rprop - And64InlineHook implementation
- The Rust community
## FAQ
**Q: Is this compatible with the original Cydia Substrate?**
A: Yes! It's a drop-in replacement with the same C API.
**Q: Can I use this for game modding?**
A: Yes, it's commonly used for Android game modding (IL2CPP, Unity, Unreal Engine).
**Q: Does it work on rooted devices only?**
A: No, it works on non-rooted devices within your own app's process.
**Q: What about anti-cheat detection?**
A: This is a hooking library. Detection avoidance is your responsibility.
**Q: Performance impact?**
A: Minimal - only affects hooked functions, optimized trampolines.
## Support
- [Documentation](https://docs.rs/substrate-rs)
- [Issue Tracker](https://github.com/rodroidmods/cydia-substrate/issues)
- [Crates.io](https://crates.io/crates/substrate-rs)