减小ipa体积之删除frameWork中无用mach-O文件

简介: 首先我们来简单的介绍一下mach-O。 什么是mach-O? Mach-O格式全称为Mach Object文件格式的缩写,是mac上可执行文件的格式,类似于windows上的PE格式 (Portable Executable ), linux上的elf格式 (Executable and Linking Format)。 上面第一个图是苹果给出的ma


首先我们来简单的介绍一下mach-O。

什么是mach-O?

Mach-O格式全称为Mach Object文件格式的缩写,是mac上可执行文件的格式,类似于windows上的PE格式 (Portable Executable ), linux上的elf格式 (Executable and Linking Format)。


上面第一个图是苹果给出的mach-O格式的示意图,而第二个图是我们使用machOView来分析某个可执行文件中的armv7的格式。可以看出他们两者的关系是对应的。

在machO这其中包含了很多的有效的信息,包括字符串,代码段,oc类,oc协议等各种的信息,利用这些信息我们也做到分析代码或者程序逻辑的作用,比如,下面这个数据就是我从这个machO文件里面导出来的,获取到了某个framework一个OC类中的所有基本元素。

那什么又是FatFile/FatBinary

简单来说,就是一个由不同的编译架构后的Mach-O产物所合成的集合体。例如上面我就只截取armv7的Mach-O格式座位示例, 而实际上常用的还有arm64/x86_64/i386等格式。

而实际上,包括我们使用的那些framework,大多数也是的。比如下图我们继续用machOView分析一下。 

可以看到arm64/armv7架构的存在。

FrameWork跟最终可执行文件的区别在哪里?

这里我们先随便写一个简单的framework, 在这个framework中我们实现了两个oc类,如下图所示:

紧接着我们干净用machOView来看看这个新鲜出炉的framework,可以看到该FrameWork在armv7的格式下,里面存在多个.o文件。

如果我们选择其中一个继续看看的话,你就会看到一个完整的Mach-O格式的文件。

由此我们可以得知frameWork也是另外一种情况的Mach-O集合体,是由多个不同的子mach-O文件所组合而成的,他们可以单独的拆开,而可执行文件则把同一架构下的所有Mach-O文件都进行了合并,他们不能拆开,如果想要更加清晰的定义的话,可以去研究一下苹果的定义,这里不做过多的阐述。

我们能做什么?

可能到这里你还有点乱,没关系,我们直接来拆开一个framework给大家看看!

到了这一步,我们就已经知道了我们能把FrameWork中的各个子Mach-O文件拆开, 那么我们能不能把这些Mach-O文件中有效的部分重新组装一下, 生成新的一个FrameWork呢?

这必须是可以的,但是其实最重要的关键是在于怎么去确定这个Mach-O文件有没有被我们的程序使用到。

怎么确定一个Mach-O有没有被使用到?

我们直接再来一个简单的demo,尝试一下

此时进行编译


成功.....

然后拆分老的framework文件, 删掉拆分得到的MachOClassA, 并且用剩下的mach-O文件合并生成一个新的framework, 替换到工程中去并进行编译


你没看错, 他确实是失败了, 如果在程序中代码直接使用了某些类或者某些方法, 而其mach-O文件不存在的情况下, 会导致编译不过(找不到对应的方法), 这也就是说, 我们能够使用最简单粗暴的方法来判断这个machO文件是不是被需要的!

不过, 需要注意的是, category的实现方式是不一样的,故如果我们删除了category的方法, 但是直接把Mach-o删除的话, 编译时是不会报错的。有兴趣的同学可以自己去看看oc中关于category的实现。

另外还有在程序运行中动态使用的performselector方法(可以通过查询字符串列表排除)。

下面是相关的流程图。

怎么把Mach-O的删除工具应用到XCode中去

现在我们已经通过编译的手段获得了一堆mach-O文件, 但是很多都是pod中引进的, 这个时候我们需要在代码编译器执行删除.o文件的脚本 刚好Xcode确实有这么一个地方可以设置

成果

在debug模式下大概减少了0.5M, 实际二进制文件减小大概1.2M, 如果计算到最终提交到苹果并且经过DRM加密后, 预计可以减小1M左右。

PS

  1. category是需要过滤的, 这货有点特别
  2. 删除找出来的.o文件之后, 可能会引起一些特殊的情况, 当然一般是crash, 因为有一些特别的代码他们用法并不是直接引用某个方法, 而是通过NSString相关的方法来获得Sel或者Class
  3. 把查找.o文件的操作放在本地, 而在编译器上进行编译的时候就直接执行删除, 不占用编译器的时间(我们的项目要使用六个小时以上的时间来进行查找)
  4. 建议进行操作再跑一遍回归测试, 确保各个功能模块正常

其他实践与猜想以及做过的尝试

  1. .o文件其实是可以直接引进到工程里面直接编译的, 也就是说其实可以把frameWork拆开, 然后加到工程中, 一样能够正常使用
  2. 一开始其实是想把.o文件中__text段中无用的函数进行删除, 但发现流程过于复杂, 而暂时放弃, 查找程序中无用函数的方法以后有机会再进行分享(如果这个成功的话, 估计会减小至少3~4M左右的ipa大小), 附上查找到的程序中无用方法结果的示例

 

  1. 其实看了上面那种方法之后, 我们紧接着又能想到, 暴力的将.m文件中的代码删除, 然后看看哪些工程中可见的代码是可以删除的(ps. 主要针对非framework, 另外也同样需要注意category以及performselector的问题, 需要配合查找字符串列表一起进行, 或者手工进行判断)。

目录
相关文章
|
3天前
|
Android开发 芯片
Android源代码定制:移除无用lunch|新建lunch|自定义customize.mk
Android源代码定制:移除无用lunch|新建lunch|自定义customize.mk
24 3
|
8月前
怎么删除360base64.dll,这个方法百分百管用
怎么删除360base64.dll,这个方法百分百管用
220 0
|
9月前
|
存储 安全 API
4.9 x64dbg 内存处理与差异对比
LyScript 插件中针对内存读写函数的封装功能并不多,只提供了最基本的`内存读取`和`内存写入`系列函数的封装,本章将继续对API接口进行封装,实现一些在软件逆向分析中非常实用的功能,例如ShellCode代码写出与置入,内存交换,内存区域对比,磁盘与内存镜像比较,内存特征码检索等功能,学会使用这些功能对于后续漏洞分析以及病毒分析都可以起到事半功倍的效果,读者应重点关注这些函数的使用方式。
46 2
4.9 x64dbg 内存处理与差异对比
|
编译器 C语言 C++
g++命令编译出来的文件体积过大解决方案
g++命令编译出来的文件体积过大解决方案
307 0
Cocos2dx的cache细节,资源路径不规范,容易导致cache中存在多份
Cocos2dx的cache细节,资源路径不规范,容易导致cache中存在多份
97 0
|
小程序
uni 小程序 vendor 体积过大。
uni 小程序 vendor 体积过大。
|
安全 搜索推荐 API
LyScript 内存交换与差异对比
LyScript 针对内存读写函数的封装功能并不多,只提供了内存读取和内存写入函数的封装,本篇文章将继续对API进行封装,实现一些在软件逆向分析中非常实用的功能,例如内存交换,内存区域对比,磁盘与内存镜像比较,特征码检索等功能。
182 0
LyScript 内存交换与差异对比
|
Android开发
【Android 逆向】修改运行中的 Android 进程的内存数据 ( 使用 IDA 分析要修改的内存特征 | 根据内存特征搜索修改点 | 修改进程内存 )
【Android 逆向】修改运行中的 Android 进程的内存数据 ( 使用 IDA 分析要修改的内存特征 | 根据内存特征搜索修改点 | 修改进程内存 )
413 0
【Android 逆向】修改运行中的 Android 进程的内存数据 ( 使用 IDA 分析要修改的内存特征 | 根据内存特征搜索修改点 | 修改进程内存 )
|
存储 Java Linux
【Android 内存优化】Android 工程中使用 libjpeg-turbo 压缩图片 ( 初始化压缩对象 | 打开文件 | 设置压缩参数 | 写入压缩图像数据 | 完成压缩 | 释放资源 )
【Android 内存优化】Android 工程中使用 libjpeg-turbo 压缩图片 ( 初始化压缩对象 | 打开文件 | 设置压缩参数 | 写入压缩图像数据 | 完成压缩 | 释放资源 )
202 0
|
安全 Windows
C盘空间一直在减小(每隔几秒减少0.1),windows文件夹下temp文件夹删除后一直在重复创建
C盘空间一直在减小(每隔几秒减少0.1),windows文件夹下temp文件夹删除后一直在重复创建
C盘空间一直在减小(每隔几秒减少0.1),windows文件夹下temp文件夹删除后一直在重复创建