Skip to content

Commit 9cf8b93

Browse files
committed
docs: add classic injection case folder with runnable hook sample
1 parent 44aa26f commit 9cf8b93

8 files changed

Lines changed: 180 additions & 0 deletions

File tree

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,11 @@
11
/target
2+
.DS_Store
3+
4+
# Example workspace outputs
5+
/examples/**/target/
6+
/examples/**/Cargo.lock
7+
8+
# classic-tight-headerpad-case runtime artifacts
9+
/examples/classic-tight-headerpad-case/calc_*
10+
/examples/classic-tight-headerpad-case/*.dylib
11+
/examples/classic-tight-headerpad-case/calc_bin

README.cn.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ insert-dylib [options] <dylib_path> <binary_path> [new_binary_path]
4848

4949
## 示例
5050

51+
更多案例见 [`examples/`](./examples/README.md)
52+
5153
注入 dylib,输出到默认文件:
5254

5355
```bash

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ If `new_binary_path` is omitted (and `--inplace` is not used), output defaults t
4848

4949
## Examples
5050

51+
More case studies are in [`examples/`](./examples/README.md).
52+
5153
Inject a dylib and write to default output:
5254

5355
```bash

examples/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Examples
2+
3+
- [Classic case folder: tight headerpad on `calc`](./classic-tight-headerpad-case/)
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Classic `calc` Injection Case (Tight Headerpad)
2+
3+
This folder is a reproducible failure/success comparison for Mach-O dylib injection:
4+
5+
- The target binary (`calc`) has very limited header padding (about 32 bytes).
6+
- Injecting a long dylib path can corrupt the binary after re-signing.
7+
- Injecting a short dylib path with the right flags can persist the hook safely.
8+
9+
## Directory Layout
10+
11+
- `calc/calc.c`: target program source
12+
- `hook/`: Rust hook payload project (`sighook` based)
13+
14+
## Prerequisites
15+
16+
- macOS aarch64 (Apple Silicon)
17+
- `codesign` available in your environment
18+
19+
## Build and Run (Step-by-Step)
20+
21+
Run from repository root:
22+
23+
```bash
24+
# 1) Build insert-dylib
25+
cargo build --release
26+
27+
# 2) Enter this example folder
28+
cd examples/classic-tight-headerpad-case
29+
30+
# 3) Build the target binary
31+
cc -O2 -fno-inline calc/calc.c -o calc_bin
32+
33+
# 4) Build the hook dylib
34+
cargo build --release --manifest-path hook/Cargo.toml
35+
```
36+
37+
### Baseline Check (`DYLD_INSERT_LIBRARIES`)
38+
39+
```bash
40+
DYLD_INSERT_LIBRARIES="$PWD/hook/target/release/libclassic_hook.dylib" ./calc_bin
41+
```
42+
43+
Expected output:
44+
45+
```text
46+
[+] hooked: now x0 is 99.
47+
Result: 99
48+
```
49+
50+
## Failure Repro (Long Dylib Name)
51+
52+
Use a longer filename such as `libh.dylib`:
53+
54+
```bash
55+
cp hook/target/release/libclassic_hook.dylib libh.dylib
56+
../../target/release/insert-dylib libh.dylib calc_bin calc_bad
57+
codesign -f -s - ./calc_bad
58+
./calc_bad
59+
```
60+
61+
Common symptom:
62+
63+
- Process exits unexpectedly and does not print the expected final result line.
64+
65+
## Success Repro (Short Dylib Name)
66+
67+
Use a short filename such as `h.dylib`:
68+
69+
```bash
70+
cp hook/target/release/libclassic_hook.dylib h.dylib
71+
../../target/release/insert-dylib --no-strip-codesig h.dylib calc_bin calc_ok
72+
codesign -f -s - ./calc_ok
73+
./calc_ok
74+
```
75+
76+
Expected output:
77+
78+
```text
79+
[+] hooked: now x0 is 99.
80+
Result: 99
81+
```
82+
83+
## Why This Happens
84+
85+
For `LC_LOAD_DYLIB`, command size is:
86+
87+
- `cmdsize = 24 + align8(path_len + 1)`
88+
89+
Where:
90+
91+
- `24` is the fixed size of `struct dylib_command`
92+
- `path_len + 1` includes the trailing `\0`
93+
94+
In this case:
95+
96+
- available header space is about 32 bytes
97+
- `libh.dylib` needs 40 bytes (overflow)
98+
- `h.dylib` needs 32 bytes (fits exactly)
99+
100+
That is why long path injection fails while short path injection succeeds.
101+
102+
## Practical Notes
103+
104+
- When header padding is tight, prefer short install names.
105+
- In this case, prefer `--no-strip-codesig`.
106+
- If you can rebuild the target, add `-Wl,-headerpad_max_install_names`.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <stdio.h>
2+
3+
// no inline
4+
__attribute__((noinline)) int calculate(int a, int b) {
5+
int result = a + b;
6+
return result;
7+
}
8+
9+
int main(void) {
10+
int result = calculate(3, 66);
11+
printf("Result: %d\n", result);
12+
return 0;
13+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "classic_hook"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[lib]
7+
crate-type = ["cdylib"]
8+
9+
[dependencies]
10+
libc = "0.2"
11+
sighook = "0.4.0"
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use sighook::{HookContext, instrument_no_original};
2+
3+
const ADD_INSN_OFFSET: u64 = 0;
4+
const LOG_MSG: &[u8] = b"[+] hooked: now x0 is 99.\n";
5+
6+
extern "C" fn replace_logic(_address: u64, ctx: *mut HookContext) {
7+
unsafe {
8+
let _ = libc::write(
9+
libc::STDERR_FILENO,
10+
LOG_MSG.as_ptr() as *const libc::c_void,
11+
LOG_MSG.len(),
12+
);
13+
(*ctx).regs.named.x0 = 99;
14+
}
15+
}
16+
17+
#[used]
18+
#[unsafe(link_section = "__DATA,__mod_init_func")]
19+
static INIT_ARRAY: extern "C" fn() = init;
20+
21+
extern "C" fn init() {
22+
unsafe {
23+
let target_address = {
24+
let symbol = libc::dlsym(libc::RTLD_DEFAULT, c"calculate".as_ptr());
25+
if symbol.is_null() {
26+
return;
27+
}
28+
symbol as u64 + ADD_INSN_OFFSET
29+
};
30+
31+
let _ = instrument_no_original(target_address, replace_logic);
32+
}
33+
}

0 commit comments

Comments
 (0)