Skip to content

Commit e2eef48

Browse files
committed
[enhance] modified examples
1 parent 9cf8b93 commit e2eef48

4 files changed

Lines changed: 219 additions & 4 deletions

File tree

README.cn.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ insert-dylib [options] <dylib_path> <binary_path> [new_binary_path]
4545
- `--strip-codesig` 在可行时强制移除 `LC_CODE_SIGNATURE`
4646
- `--no-strip-codesig` 不移除 `LC_CODE_SIGNATURE`
4747
- `--all-yes` 对所有交互提示自动回答 `yes`
48+
- `--ios` 将 dylib 的 Mach-O 平台字段从 `macOS` 改为 `iOS`(必须配合 `--dylib-path`
49+
- `--dylib-path <path>` 配合 `--ios` 使用的本地 dylib 文件路径,用于平台字段改写
4850

4951
## 示例
5052

@@ -68,6 +70,12 @@ insert-dylib --weak --inplace @rpath/libHook.dylib MyApp
6870
insert-dylib --strip-codesig --overwrite @loader_path/libHook.dylib MyApp MyApp.patched
6971
```
7072

73+
写入 iOS install name,并对本地 dylib 文件执行平台改写:
74+
75+
```bash
76+
insert-dylib --ios --dylib-path libarcaea_function.dylib @executable_path/Frameworks/libarcaea_function.dylib Arc-mobile
77+
```
78+
7179
## 代码签名说明
7280

7381
若移除了 `LC_CODE_SIGNATURE`,原签名会失效。需要的话请对补丁后的文件重新签名:

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ If `new_binary_path` is omitted (and `--inplace` is not used), output defaults t
4545
- `--strip-codesig` force removing `LC_CODE_SIGNATURE` when possible
4646
- `--no-strip-codesig` never remove `LC_CODE_SIGNATURE`
4747
- `--all-yes` auto-answer `yes` to all interactive prompts
48+
- `--ios` rewrite dylib Mach-O platform markers from `macOS` to `iOS` (requires `--dylib-path`)
49+
- `--dylib-path <path>` local dylib file path used by `--ios` for Mach-O platform rewrite
4850

4951
## Examples
5052

@@ -68,6 +70,12 @@ Force code-signature stripping and overwrite output:
6870
insert-dylib --strip-codesig --overwrite @loader_path/libHook.dylib MyApp MyApp.patched
6971
```
7072

73+
Inject iOS install name while rewriting a local dylib file to iOS platform metadata:
74+
75+
```bash
76+
insert-dylib --ios --dylib-path libarcaea_function.dylib @executable_path/Frameworks/libarcaea_function.dylib Arc-mobile
77+
```
78+
7179
## Code Signing Note
7280

7381
If `LC_CODE_SIGNATURE` is removed, the binary's signature is invalidated. Re-sign the patched binary if needed:

examples/classic-tight-headerpad-case/hook/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const LOG_MSG: &[u8] = b"[+] hooked: now x0 is 99.\n";
66
extern "C" fn replace_logic(_address: u64, ctx: *mut HookContext) {
77
unsafe {
88
let _ = libc::write(
9-
libc::STDERR_FILENO,
9+
libc::STDOUT_FILENO,
1010
LOG_MSG.as_ptr() as *const libc::c_void,
1111
LOG_MSG.len(),
1212
);

src/main.rs

Lines changed: 202 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ const LC_LOAD_DYLIB: u32 = 0xc;
2424
const LC_LOAD_WEAK_DYLIB: u32 = 0x80000018;
2525
const LC_SEGMENT_64: u32 = 0x19;
2626
const LC_CODE_SIGNATURE: u32 = 0x1d;
27+
const LC_VERSION_MIN_MACOSX: u32 = 0x24;
28+
const LC_VERSION_MIN_IPHONEOS: u32 = 0x25;
29+
const LC_BUILD_VERSION: u32 = 0x32;
30+
31+
const PLATFORM_MACOS: u32 = 1;
32+
const PLATFORM_IOS: u32 = 2;
2733

2834
const BUFSIZE: usize = 512;
2935

@@ -128,6 +134,26 @@ struct LinkeditDataCommand {
128134
datasize: u32,
129135
}
130136

137+
#[repr(C)]
138+
#[derive(Clone, Copy, Debug)]
139+
struct VersionMinCommand {
140+
cmd: u32,
141+
cmdsize: u32,
142+
version: u32,
143+
sdk: u32,
144+
}
145+
146+
#[repr(C)]
147+
#[derive(Clone, Copy, Debug)]
148+
struct BuildVersionCommand {
149+
cmd: u32,
150+
cmdsize: u32,
151+
platform: u32,
152+
minos: u32,
153+
sdk: u32,
154+
ntools: u32,
155+
}
156+
131157
#[repr(C)]
132158
#[derive(Clone, Copy, Debug)]
133159
struct FatHeader {
@@ -152,6 +178,14 @@ struct Options {
152178
overwrite: bool,
153179
codesig_flag: u8,
154180
all_yes: bool,
181+
ios: bool,
182+
ios_dylib_path: Option<String>,
183+
}
184+
185+
#[derive(Clone, Copy, Debug, Default)]
186+
struct PlatformRewriteStats {
187+
platform_commands: usize,
188+
rewritten_commands: usize,
155189
}
156190

157191
fn is_64_bit(magic: u32) -> bool {
@@ -193,8 +227,9 @@ fn absdiff(lhs: u64, rhs: u64) -> u64 {
193227
fn usage() -> ! {
194228
println!("Usage: insert-dylib dylib_path binary_path [new_binary_path]");
195229
println!(
196-
"Option flags: --inplace --weak --overwrite --strip-codesig --no-strip-codesig --all-yes"
230+
"Option flags: --inplace --weak --overwrite --strip-codesig --no-strip-codesig --all-yes --ios --dylib-path <path>"
197231
);
232+
println!("Note: --ios requires --dylib-path <local_dylib_file>.");
198233
process::exit(1);
199234
}
200235

@@ -599,12 +634,133 @@ fn insert_dylib(
599634
Ok(true)
600635
}
601636

637+
fn rewrite_macho_platform_to_ios_slice(
638+
file: &mut File,
639+
header_offset: u64,
640+
) -> io::Result<PlatformRewriteStats> {
641+
file.seek(SeekFrom::Start(header_offset))?;
642+
643+
let mh: MachHeader = read_struct(file)?;
644+
if mh.magic != MH_MAGIC_64
645+
&& mh.magic != MH_CIGAM_64
646+
&& mh.magic != MH_MAGIC
647+
&& mh.magic != MH_CIGAM
648+
{
649+
return Err(io::Error::new(
650+
io::ErrorKind::InvalidData,
651+
format!("Unknown Mach-O magic: 0x{:x}", mh.magic),
652+
));
653+
}
654+
655+
let commands_offset = header_offset
656+
+ if is_64_bit(mh.magic) {
657+
size_of::<MachHeader64>() as u64
658+
} else {
659+
size_of::<MachHeader>() as u64
660+
};
661+
662+
file.seek(SeekFrom::Start(commands_offset))?;
663+
let ncmds = swap32(mh.ncmds, mh.magic);
664+
let mut stats = PlatformRewriteStats::default();
665+
666+
for _ in 0..ncmds {
667+
let command_offset = file.stream_position()?;
668+
let lc: LoadCommand = peek_struct(file)?;
669+
let cmd = swap32(lc.cmd, mh.magic);
670+
let cmdsize = swap32(lc.cmdsize, mh.magic);
671+
672+
if cmdsize < size_of::<LoadCommand>() as u32 {
673+
return Err(io::Error::new(
674+
io::ErrorKind::InvalidData,
675+
"load command size is smaller than load_command",
676+
));
677+
}
678+
679+
match cmd {
680+
LC_BUILD_VERSION => {
681+
if cmdsize < size_of::<BuildVersionCommand>() as u32 {
682+
return Err(io::Error::new(
683+
io::ErrorKind::InvalidData,
684+
"LC_BUILD_VERSION command is truncated",
685+
));
686+
}
687+
688+
let mut cmd_data: BuildVersionCommand = read_struct(file)?;
689+
let platform = swap32(cmd_data.platform, mh.magic);
690+
stats.platform_commands += 1;
691+
692+
if platform == PLATFORM_MACOS {
693+
cmd_data.platform = swap32(PLATFORM_IOS, mh.magic);
694+
file.seek(SeekFrom::Start(command_offset))?;
695+
write_struct(file, &cmd_data)?;
696+
stats.rewritten_commands += 1;
697+
}
698+
}
699+
LC_VERSION_MIN_MACOSX => {
700+
if cmdsize < size_of::<VersionMinCommand>() as u32 {
701+
return Err(io::Error::new(
702+
io::ErrorKind::InvalidData,
703+
"LC_VERSION_MIN_MACOSX command is truncated",
704+
));
705+
}
706+
707+
let mut cmd_data: VersionMinCommand = read_struct(file)?;
708+
cmd_data.cmd = swap32(LC_VERSION_MIN_IPHONEOS, mh.magic);
709+
file.seek(SeekFrom::Start(command_offset))?;
710+
write_struct(file, &cmd_data)?;
711+
stats.platform_commands += 1;
712+
stats.rewritten_commands += 1;
713+
}
714+
LC_VERSION_MIN_IPHONEOS => {
715+
stats.platform_commands += 1;
716+
}
717+
_ => {}
718+
}
719+
720+
file.seek(SeekFrom::Start(command_offset + cmdsize as u64))?;
721+
}
722+
723+
Ok(stats)
724+
}
725+
726+
fn rewrite_dylib_platform_to_ios(path: &str) -> io::Result<PlatformRewriteStats> {
727+
let mut file = File::options().read(true).write(true).open(path)?;
728+
let magic: u32 = read_struct(&mut file)?;
729+
730+
match magic {
731+
FAT_MAGIC | FAT_CIGAM => {
732+
file.seek(SeekFrom::Start(0))?;
733+
let fat_header: FatHeader = read_struct(&mut file)?;
734+
let nfat_arch = swap32(fat_header.nfat_arch, magic) as usize;
735+
let archs = read_fat_arches(&mut file, nfat_arch)?;
736+
737+
let mut total = PlatformRewriteStats::default();
738+
for arch in archs {
739+
let offset = swap32(arch.offset, magic) as u64;
740+
let stats = rewrite_macho_platform_to_ios_slice(&mut file, offset)?;
741+
total.platform_commands += stats.platform_commands;
742+
total.rewritten_commands += stats.rewritten_commands;
743+
}
744+
Ok(total)
745+
}
746+
MH_MAGIC_64 | MH_CIGAM_64 | MH_MAGIC | MH_CIGAM => {
747+
file.seek(SeekFrom::Start(0))?;
748+
rewrite_macho_platform_to_ios_slice(&mut file, 0)
749+
}
750+
_ => Err(io::Error::new(
751+
io::ErrorKind::InvalidData,
752+
format!("Unknown Mach-O magic: 0x{:x}", magic),
753+
)),
754+
}
755+
}
756+
602757
fn parse_args() -> (Options, Vec<String>) {
603758
let mut options = Options::default();
604759
let mut positional = Vec::new();
605760

761+
let mut args = env::args().skip(1).peekable();
606762
let mut parsing_options = true;
607-
for arg in env::args().skip(1) {
763+
while let Some(arg) = args.next() {
608764
if parsing_options {
609765
if arg == "--" {
610766
parsing_options = false;
@@ -619,7 +775,21 @@ fn parse_args() -> (Options, Vec<String>) {
619775
"--strip-codesig" => options.codesig_flag = 1,
620776
"--no-strip-codesig" => options.codesig_flag = 2,
621777
"--all-yes" => options.all_yes = true,
622-
_ => usage(),
778+
"--ios" => options.ios = true,
779+
"--dylib-path" => {
780+
let path = args.next().unwrap_or_else(|| usage());
781+
options.ios_dylib_path = Some(path);
782+
}
783+
_ => {
784+
if let Some(path) = arg.strip_prefix("--dylib-path=") {
785+
if path.is_empty() {
786+
usage();
787+
}
788+
options.ios_dylib_path = Some(path.to_string());
789+
} else {
790+
usage();
791+
}
792+
}
623793
}
624794
continue;
625795
}
@@ -677,6 +847,35 @@ fn run() -> io::Result<i32> {
677847
return Ok(1);
678848
}
679849

850+
if options.ios_dylib_path.is_some() && !options.ios {
851+
eprintln!("--dylib-path can only be used together with --ios.");
852+
return Ok(1);
853+
}
854+
855+
if options.ios {
856+
let ios_dylib_path = options.ios_dylib_path.as_deref().unwrap_or_else(|| {
857+
eprintln!("--ios requires --dylib-path <local_dylib_file>.");
858+
usage();
859+
});
860+
861+
if fs::metadata(ios_dylib_path).is_err() {
862+
eprintln!("{ios_dylib_path}: not found (--dylib-path)");
863+
return Ok(1);
864+
}
865+
866+
let stats = rewrite_dylib_platform_to_ios(ios_dylib_path)?;
867+
if stats.platform_commands == 0 {
868+
println!("No platform load command found in {ios_dylib_path}; skipped --ios rewrite.");
869+
} else if stats.rewritten_commands == 0 {
870+
println!("{ios_dylib_path} already declares iOS platform; no rewrite needed.");
871+
} else {
872+
println!(
873+
"Rewrote {} platform load command(s) from macOS to iOS in {ios_dylib_path}.",
874+
stats.rewritten_commands
875+
);
876+
}
877+
}
878+
680879
if !options.inplace {
681880
let new_binary_path = if positional.len() == 3 {
682881
positional[2].clone()

0 commit comments

Comments
 (0)