需求
做一个自定命令行, 能够打印Mach-O文件的架构
1. main 函数处理
1.1 通过Xcode创建一个Single View App 项目
int main(int argc, char * argv[]) {
@autoreleasepool {
// printf("%s\n", argv[0]);
// 查看参数个数
if (argc == 1) {
printf("-l 查看 Mach-O 信息\n");
return 0;
}
// 比较参数的值
if (strcmp(argv[1], "-l") != 0) {
printf("-l 查看 Mach-O 信息\n");
return 0;
}
// Mach-O路径
NSString *machOPath = @"/private/var/containers/Bundle/Application/2036E1A7-46E6-4205-BD0A-9A305598315A/Fission.app/Fission";
// 1. 创建文件句柄
// 文件句柄是一个字节一个字节地读取, 不需要把整个文件都加载到内存中, 节省内存
NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:machOPath];
NSLog(@"%@", handle);
// 2. 处理文件内容
// ...
// 3. 关闭文件
[handle closeFile];
return 0;
}
}
1.2 编译命令行
- 选择真机设备编译
- 运行环境选择release
1.3 编译, 找到命令行
1.4 测试命令行
- 将可执行文件
TestCL
导入手机路径/usr/bin/
下 注意
: 如果存在TestCL
文件, 1. 先删除 2. 再导入, 不要直接拖进去替换
- 直接使用会报权限问题
- 给文件添加执行权限
chmod +x /usr/bin/TestCL
- 测试成功
2. 读取魔数(magic number)
2.1 magic number
: 魔数, 用来标识架构类型
2.2 查看可执行文件的魔数
- 通过MachOView查看
3. 通过内核源码比对魔数的值
-
下载最新的就可以了
-
文件路径: xnu-xxx -> EXTERNAL_HEADERS -> mach-o -> fat.h 和 loader.h
-
为啥有两个值? 大小端问题
-
魔数对应代码
// 字节长度
/**
按照posix标准,一般整形对应的*_t类型为:
1字节 uint8_t
2字节 uint16_t
4字节 uint32_t
8字节 uint64_t
*/
int length = sizeof(uint32_t);
// 读出最前面的4个字节(magic number)
NSData *magicData = [handle readDataOfLength:length];
// magic number : 魔数, 用来标识架构类型
uint32_t magicNumber;
[magicData getBytes:&magicNumber length:length];
// 输出架构类型
if (magicNumber == FAT_CIGAM || magicNumber == FAT_MAGIC) {
printf("FAT文件\n");
} else if (magicNumber == MH_CIGAM_64 || magicNumber == MH_MAGIC_64) {
printf("arm_64文件\n");
} else if (magicNumber == MH_MAGIC || magicNumber == MH_CIGAM) {
printf("非arm_64文件\n");
} else {
printf("读取失败\n");
}
4. 给TestCL文件添加SpringBoard权限
4.1 权限相关的问题
- 为啥添加SpringBoard的权限,因为它的权限非常高, 可以任意访问其他App的文档路径
- SpringBoard的路径:
- 苹果的权限文件大多以
.entitlements
结尾
.entitlements == .xml == .plist文件
4.2 导出Mach-O文件的权限
// -e : export 导出
// > : 覆盖
// >> : 追加
ldid -e 可执行文件 > 可执行文件.entitlements
4.3 给可执行文件重新签上权限
// 注意: -S和可执行文件的权限文件要紧跟着,没有空格
ldid -S可执行文件权限文件 可执行文件
5. 报错总结
5.1 Bad CPU type in executable
- 没有选择真机编译
5.2 Killed: 9
- 原因1 : 没有选择发布版本(release), 而选择了测试版本(debug)
- 原因2 : 将TestCL文件导入手机时, 直接拖进去替换了原来的TestCL文件
5.3 -sh: /usr/bin/ TestCL: Permission denied
- 没有执行权限, 添加即可
chmod +x /usr/bin/TestCL