Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ jobs:
echo "$OUT"
[[ "$OUT" == *"calc(4, 5) = 99"* ]]

cc -O0 -fno-inline examples/instrument_unhook_restore/target.c -o examples/instrument_unhook_restore/app
DYLIB="$PWD/target/aarch64-apple-darwin/debug/examples/libinstrument_unhook_restore.dylib"
OUT=$(DYLD_INSERT_LIBRARIES="$DYLIB" examples/instrument_unhook_restore/app)
echo "$OUT"
[[ "$OUT" == *"hooked_calc(3, 4) = 123"* ]]
[[ "$OUT" == *"calc(3, 4) = 7"* ]]

cc -O0 -fno-inline examples/inline_hook_far/target.c -o examples/inline_hook_far/app
DYLIB="$PWD/target/aarch64-apple-darwin/debug/examples/libinline_hook_far.dylib"
OUT=$(DYLD_INSERT_LIBRARIES="$DYLIB" examples/inline_hook_far/app)
Expand Down Expand Up @@ -102,10 +109,11 @@ jobs:
- name: Cargo clippy
run: cargo clippy --all-targets --target x86_64-apple-darwin -- -D warnings

- name: Build four base examples (macOS x86_64)
- name: Build five base examples (macOS x86_64)
run: |
cargo build --example instrument_with_original --target x86_64-apple-darwin
cargo build --example instrument_no_original --target x86_64-apple-darwin
cargo build --example instrument_unhook_restore --target x86_64-apple-darwin
cargo build --example inline_hook_far --target x86_64-apple-darwin
cargo build --example patchcode_add_to_mul --target x86_64-apple-darwin

Expand All @@ -126,6 +134,13 @@ jobs:
echo "$OUT"
[[ "$OUT" == *"calc(4, 5) = 99"* ]]

cc -O0 -fno-inline examples/instrument_unhook_restore/target.c -o examples/instrument_unhook_restore/app
DYLIB="$PWD/target/x86_64-apple-darwin/debug/examples/libinstrument_unhook_restore.dylib"
OUT=$(DYLD_INSERT_LIBRARIES="$DYLIB" examples/instrument_unhook_restore/app)
echo "$OUT"
[[ "$OUT" == *"hooked_calc(3, 4) = 123"* ]]
[[ "$OUT" == *"calc(3, 4) = 7"* ]]

cc -O0 -fno-inline examples/inline_hook_far/target.c -o examples/inline_hook_far/app
DYLIB="$PWD/target/x86_64-apple-darwin/debug/examples/libinline_hook_far.dylib"
OUT=$(DYLD_INSERT_LIBRARIES="$DYLIB" examples/inline_hook_far/app)
Expand Down Expand Up @@ -188,6 +203,13 @@ jobs:
echo "$OUT"
[[ "$OUT" == *"calc(4, 5) = 99"* ]]

cc -O0 -fno-inline -rdynamic examples/instrument_unhook_restore/target.c -o examples/instrument_unhook_restore/app
SO="$PWD/target/x86_64-unknown-linux-gnu/debug/examples/libinstrument_unhook_restore.so"
OUT=$(LD_PRELOAD="$SO" examples/instrument_unhook_restore/app)
echo "$OUT"
[[ "$OUT" == *"hooked_calc(3, 4) = 123"* ]]
[[ "$OUT" == *"calc(3, 4) = 7"* ]]

cc -O0 -fno-inline -rdynamic examples/inline_hook_far/target.c -o examples/inline_hook_far/app
SO="$PWD/target/x86_64-unknown-linux-gnu/debug/examples/libinline_hook_far.so"
OUT=$(LD_PRELOAD="$SO" examples/inline_hook_far/app)
Expand Down Expand Up @@ -256,6 +278,13 @@ jobs:
echo "$OUT"
[[ "$OUT" == *"calc(4, 5) = 99"* ]]

cc -O0 -fno-inline -rdynamic examples/instrument_unhook_restore/target.c -o examples/instrument_unhook_restore/app
SO="$PWD/target/aarch64-unknown-linux-gnu/debug/examples/libinstrument_unhook_restore.so"
OUT=$(LD_PRELOAD="$SO" examples/instrument_unhook_restore/app)
echo "$OUT"
[[ "$OUT" == *"hooked_calc(3, 4) = 123"* ]]
[[ "$OUT" == *"calc(3, 4) = 7"* ]]

cc -O0 -fno-inline -rdynamic examples/instrument_adrp_no_original/target.c -o examples/instrument_adrp_no_original/app
SO="$PWD/target/aarch64-unknown-linux-gnu/debug/examples/libinstrument_adrp_no_original.so"
OUT=$(LD_PRELOAD="$SO" examples/instrument_adrp_no_original/app)
Expand Down
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ name = "instrument_adrp_no_original"
path = "examples/instrument_adrp_no_original/main.rs"
crate-type = ["cdylib"]

[[example]]
name = "instrument_unhook_restore"
path = "examples/instrument_unhook_restore/main.rs"
crate-type = ["cdylib"]

[[example]]
name = "inline_hook_far"
path = "examples/inline_hook_far/main.rs"
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Available examples:
- `instrument_with_original`: BRK instrumentation + execute original opcode
- `instrument_no_original`: BRK instrumentation + skip original opcode
- `instrument_adrp_no_original`: aarch64 `adrp` patch-point via `instrument_no_original` + manual callback emulation
- `instrument_unhook_restore`: instrument + unhook restore demo
- `inline_hook_far`: function-entry detour with inline hook

## Coverage matrix
Expand Down
45 changes: 45 additions & 0 deletions examples/instrument_unhook_restore/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# instrument_unhook_restore

Demonstrates `sighook::instrument` + `sighook::unhook`.

Flow:

1. install an instruction hook
2. call `calc(3, 4)` once while hook is active and verify hooked value `123`
3. call `unhook` on the same patchpoint
4. execute target function again and verify original behavior is restored (`7`)

The callback would force result to `123` if triggered:

- `aarch64`: set `x8 = 120`, `x9 = 3`, then original `add w0, w8, w9` runs
- `linux x86_64`: set `rax = 123`

Expected runtime output proves both stages:

- callback is reached before unhook (`hooked_calc(3, 4) = 123`)
- original behavior is restored after unhook (`calc(3, 4) = 7`)

## Run (from repository root)

macOS:

```bash
cc -O0 -fno-inline examples/instrument_unhook_restore/target.c -o examples/instrument_unhook_restore/app
cargo build --example instrument_unhook_restore
DYLD_INSERT_LIBRARIES="$PWD/target/debug/examples/libinstrument_unhook_restore.dylib" examples/instrument_unhook_restore/app
```

Linux:

```bash
cc -O0 -fno-inline -rdynamic examples/instrument_unhook_restore/target.c -o examples/instrument_unhook_restore/app
cargo build --example instrument_unhook_restore
LD_PRELOAD="$PWD/target/debug/examples/libinstrument_unhook_restore.so" examples/instrument_unhook_restore/app
```

Expected output:

```text
hooked_calc(3, 4) = 123
calc(3, 4) = 7
```
85 changes: 85 additions & 0 deletions examples/instrument_unhook_restore/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use sighook::{HookContext, instrument, unhook};

#[cfg(all(any(target_os = "macos", target_os = "ios"), target_arch = "aarch64"))]
const ADD_INSN_OFFSET: u64 = 0x14;

#[cfg(all(target_arch = "x86_64", any(target_os = "linux", target_os = "macos")))]
const X86_PATCHPOINT_OFFSET: u64 = 0x4;

extern "C" fn hook_callback(_address: u64, ctx: *mut HookContext) {
unsafe {
#[cfg(target_arch = "aarch64")]
{
(*ctx).regs.named.x8 = 120;
(*ctx).regs.named.x9 = 3;
}

#[cfg(all(target_arch = "x86_64", any(target_os = "linux", target_os = "macos")))]
{
(*ctx).rax = 123;
}
}
}

#[used]
#[cfg_attr(
any(target_os = "macos", target_os = "ios"),
unsafe(link_section = "__DATA,__mod_init_func")
)]
#[cfg_attr(
any(target_os = "linux", target_os = "android"),
unsafe(link_section = ".init_array")
)]
static INIT_ARRAY: extern "C" fn() = init;

extern "C" fn init() {
unsafe {
let calc_symbol = libc::dlsym(libc::RTLD_DEFAULT, c"calc".as_ptr());
if calc_symbol.is_null() {
return;
}
let calc_fn: extern "C" fn(i32, i32) -> i32 = std::mem::transmute(calc_symbol);

let target_address = {
#[cfg(all(
any(target_os = "linux", target_os = "android"),
target_arch = "aarch64"
))]
{
let symbol = libc::dlsym(libc::RTLD_DEFAULT, c"calc_add_insn".as_ptr());
if symbol.is_null() {
return;
}
symbol as u64
}

#[cfg(all(target_arch = "x86_64", any(target_os = "linux", target_os = "macos")))]
{
let symbol = libc::dlsym(libc::RTLD_DEFAULT, c"calc".as_ptr());
if symbol.is_null() {
return;
}
symbol as u64 + X86_PATCHPOINT_OFFSET
}

#[cfg(all(any(target_os = "macos", target_os = "ios"), target_arch = "aarch64"))]
{
let symbol = libc::dlsym(libc::RTLD_DEFAULT, c"calc".as_ptr());
if symbol.is_null() {
return;
}
symbol as u64 + ADD_INSN_OFFSET
}
};

let _ = instrument(target_address, hook_callback);

let hooked = calc_fn(3, 4);
println!("hooked_calc(3, 4) = {hooked}");
if hooked != 123 {
return;
}

let _ = unhook(target_address);
}
}
67 changes: 67 additions & 0 deletions examples/instrument_unhook_restore/target.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#include <stdio.h>

#if defined(__linux__) && defined(__aarch64__)
int calc(int a, int b);

__asm__(
".text\n"
".global calc\n"
".global calc_add_insn\n"
".type calc, %function\n"
"calc:\n"
" mov x8, x0\n"
" mov x9, x1\n"
" nop\n"
" nop\n"
" nop\n"
"calc_add_insn:\n"
" add w0, w8, w9\n"
" ret\n"
".size calc, .-calc\n");
#elif defined(__x86_64__) && defined(__APPLE__)
int calc(int a, int b);

__asm__(
".text\n"
".globl _calc\n"
"_calc:\n"
" mov %edi, %eax\n"
" add %esi, %eax\n"
" nop\n"
" ret\n");
#elif defined(__x86_64__) && defined(__linux__)
int calc(int a, int b);

__asm__(
".text\n"
".global calc\n"
".type calc, @function\n"
"calc:\n"
" mov %edi, %eax\n"
" add %esi, %eax\n"
" nop\n"
" ret\n"
".size calc, .-calc\n");
#elif defined(__aarch64__)
__attribute__((naked, noinline))
int calc(int a, int b) {
__asm__ volatile(
"mov x8, x0\n"
"mov x9, x1\n"
"nop\n"
"nop\n"
"nop\n"
"add w0, w8, w9\n"
"ret\n");
}
#else
__attribute__((noinline))
int calc(int a, int b) {
return a + b;
}
#endif

int main(void) {
printf("calc(3, 4) = %d\n", calc(3, 4));
return 0;
}
2 changes: 2 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SigHookError {
InvalidAddress,
HookNotFound,
UnsupportedPlatform,
UnsupportedArchitecture,
UnsupportedOperation,
Expand Down Expand Up @@ -78,6 +79,7 @@ impl fmt::Display for SigHookError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
SigHookError::InvalidAddress => write!(f, "invalid address"),
SigHookError::HookNotFound => write!(f, "hook not found at address"),
SigHookError::UnsupportedPlatform => write!(f, "unsupported platform"),
SigHookError::UnsupportedArchitecture => write!(f, "unsupported architecture"),
SigHookError::UnsupportedOperation => write!(f, "unsupported operation"),
Expand Down
Loading