赞
踩
从IOS3.1开始,为了提高性能,绝大部分的系统动态库文件都打包存放到一个缓存文件夹中(dyld shared cache
)
缓存文件的路径: /System/Library/Caches/com.apple.dyld/dyld_share_cache_armX(X: 代表ARM处理器指令集架构)
所有的框架的可执行文件(Mach-o
)文件都不在/System/Library/Frameworks/xxxx.framework
中,改路径下存放都的都是各个各个framework
的一些资源配置文件,可执行文件都合并到一个caches中,这样做的好处是:节省内存
/usr/lib/dyld
程序来加载动态库dyld
:
dynamic link editor
: 动态链接编辑器dynamic loader
: 动态加载器dyld_share_cache_armX(动态库)
文件dsc_extractor.cpp
文件,该文件所在路径(源码823.73版本)dyld/shared_cache
文件加下dsc_extractor.cpp
类// test program #include <stdio.h> #include <stddef.h> #include <dlfcn.h> typedef int (*extractor_proc)(const char* shared_cache_file_path, const char* extraction_root_path, void (^progress)(unsigned current, unsigned total)); int main(int argc, const char* argv[]) { if ( argc != 3 ) { fprintf(stderr, "usage: dsc_extractor <path-to-cache-file> <path-to-device-dir>\n"); return 1; } //void* handle = dlopen("/Volumes/my/src/dyld/build/Debug/dsc_extractor.bundle", RTLD_LAZY); void* handle = dlopen("/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/usr/lib/dsc_extractor.bundle", RTLD_LAZY); if ( handle == NULL ) { fprintf(stderr, "dsc_extractor.bundle could not be loaded\n"); return 1; } extractor_proc proc = (extractor_proc)dlsym(handle, "dyld_shared_cache_extract_dylibs_progress"); if ( proc == NULL ) { fprintf(stderr, "dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n"); return 1; } int result = (*proc)(argv[1], argv[2], ^(unsigned c, unsigned total) { printf("%d/%d\n", c, total); } ); fprintf(stderr, "dyld_shared_cache_extract_dylibs_progress() => %d\n", result); return 0; }
变成该文件之后,我们需要在终端执行该文件, 那么你在执行这个文件的时候需要指明路径,然后开始拆分库:./dsc.extractor(可执行键) dyld_shared_cachexx(缓存文件路径) 拆分文件路径
:/dsc_extractor dyld_shared_cache_arm64 arm64
,最后拆分:
拆解完成之后,你可以使用hopper disassemble
来分析你想分析的框架
Mach-O是Mach object的缩写,是Mac\iO上用于储存程序、库的标准格式
Mach-O格式的文件类型有:
可以在xnu源码中,查看到Mach-O格式的详细定义源码地址
xnu
: Mac系统的一些内核源码,先下载xnu的源码(xnu-7195.81.3版本
),然后把整个源码拖拽到sublineText工具中,根据下面路径可以查看Mach-o
类包含的文件类型
我们在在终端,输入命令file xxx
查看某个文件的类型
.o
文件被称为目标文件:.c 文件 ---(编译)---> .o文件(目标文件)-----(链接)----> 可执行文件
(文件的编译过程)
MH_OBJECT
: 目标文件(.o)、静态库文件(.a),静态库其实就是N个.o
合并在一起
.o
文件其实是Mach-O文件类型,可以通过同样的方式查看.a
文件的类型MH_EXECUTE
:可执行文件
MH_DYLIB
:动态库文件 ;.dyld
、.framework/xx
MH_DYLINKER
:动态链接编辑器;/usr/lib/dyld
MH_DSYM
:存储这二进制文件符号信息的文件,.dSYM.Content/Resources/DWAFR/xx
常用于分析APP的崩溃信息
find . -name "*.a"
: 在当前路径下查找后缀名为.a的文件 (. 代表当前路径)
在Xcode中查看target的Mach-O类型:
通用二进制文件 :
因为需要储存多种代码的结构,通用二进制文件通常比单一平台的二进制文件的程序要大
由于两种架构的有共同的一些资源,所以并不会达到单一版本的两倍之多
由于执行过程中,只调用一部分代码,运行起来也不需要额外的内存
因为文件比原来的大,也被称为胖二进制文件(Fat Binary)
$(ARCHS_STANDARD)
: Xcode内置的环境变量,不同版本的Xcode的值不一样,通过的一些架构值
$(ARCHS_STANDARD)
值可能是arm64、armv7$(ARCHS_STANDARD)
值可能是armv7Xcode
打包出的二进制
(Mach-O类型)文件包含的架构是这两个变量的交集,也就是说两个变量同时支持的架构,打包出来的文件才会支持这个架构
在开发中我们一般不会去修改两个参数,但是比如在开发静态库的时候, 你可以通过设置这两个参数,来设置静态库支持什么架构
如何拆分和合并多个架构的二进制文件:
file 文件路径
查看Mach-O特定部分和段的内容
,苹果自带的工具lipo -info 文件路径
lipo 文件路径 -thin 架构类型(如:arm64) -output 输出文件路径
lipo -create 文件路径1 文件路劲2 -output 输出文件路径
Header
: 文件类型、目标架构类型,等Load commands
:描述文件在虚拟内存中的逻辑结构、布局(有点类似指针的作用,告诉原始数据存放的一些位置信息)Raw segement data
: 在Load commands
中定义的Segement的原始数据我么只有了解了Mach-O文件的结构,你才可能往里面注入自己的代码,达到修改app的效果
我们可以通过苹果自带的命令 来查看Mach-O
的文件的结构信息:
除了苹果自带的otools
命令,我们还可以通过MachOView
工具来查看,可以按照上面下载地址下载源码然后运行项目,如果运行项目报SDK
错误,我们要修改工程中的SDK版本,选择最新版本,在运行即可:
dyld
:也是Mach-O类型的文件,属于MH_DYLINKER
类型(动态连接器),dyld
可以加载三种类型的Mach-O类型的文件:
MH_EXECUTE
: 可执行文件MH_DYLIB
:动态库MH_BUNDLE
: bundle类型的文件dyld
负责加载的,下面我们从源码中查看dyld
支持加载的Mach-O类型的文件:Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。