随着数字化转型的不断深入,计算机应用领域的不断扩大,程序优化已经成为各行各业的关键技术之一。
尤其是为了预防山寨与盗版的发生,不少 app 开发商主动选择“应用加固”保护知识产权,但给应用戴上“金钟罩”,必然在体积和运行效率上对用户体验造成不同程度的影响。
如何平衡安全性与应用性能是业界的一大难题。网易易盾在加固行业中适配了各种不同行业背景的 app,而对加固性能优化也有一定的沉淀和积累,以提高系统的稳定性、安全性、响应速度等方面的性能。
本文对一些通用的优化方法进行总结,主要分为程序体积优化、apk 体积优化、内存优化、性能优化四大方向,为业界实践提供参考。
01 程序体积优化
程序的体积优化分为两类:一个是代码优化,另一个是编译优化。
代码优化
c 模板:使用模板带来的体积增量可能会超出预期,一开始模板代码的便利和体积膨胀可能成正比。如果面对体积相对敏感的场合,建议不要使用模板,改为实现通用的接口以减少体积。
静态库:常见的静态库有 stl 中的 string、map、vector 等标准模板库,可能开发者只是使用了极少的功能,但会导致体积莫名增大几百 k。因此,可自实现一些 stl 库,仅仅实现自己常用的算法和结构即可。
垃圾代码和重复代码:这和开发者实现和程序迭代有关,一般有较好的编码习惯的人而言,这部分能优化的点并不多。
编译优化
下面按照对体积影响、功能影响的综合因素进行排序,讲解如何优化编译后的程序体积。
strip:strip 是一个用来去除可执行文件或动态连接库中符号表和调试信息等部分内容的工具。它可以将目标文件中的符号表、调试信息、注释等内容去除,从而减小可执行文件的大小,同时也会保护源代码的安全性。
导出符号优化:可执行程序或 so 有其特有的功能,而且开发者有预期的公开接口(符号),默认情况下,导出符号个数远大于开发者预留的口,可以通过编译选项 version-script 控制导出函数特征,从而减少程序体积。
符号哈希优化:可执行文件支持 gnu hash 和 elf hash 两种格式,部分程序会生成两套 hash 值。其中 gnu hash 查找速度快,却可能不兼容一些低版本系统,而 elf hash 没有兼容性问题。一般情况下,自定义的动态链接库不建议导出太多符号,而且少量的符号查找需要优化的空间并不大,所以可以强制使用 elf hash。
编译选项优化代码体积:编译器有专门的编译选项优化体积。测试体积优化较好的参数有下面几个:
o3 优化
参考链接:
https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/optimize-options.html
oz 优化
优化后更加注重代码大小而非代码执行速度。开启选项时,编译器会将重复指令进行抽取,各个程序在调用一些重复指令时,统一通过跳转形式执行代码,执行后返回,牺牲了一定的性能。
同时,因为很多指令被抽取,编译出来的程序经过反编译,可读性较差,程序有问题时,对开发者分析的难度也有一定提升。
主动关闭部分编译选项
通过此举,减少部分程序数据的生成。如-fno-exceptions -fno-asynchronous-unwind-tables -fno-unwind-tables。不建议不了解其功能的人去配置,而且这些配置对体积优化并不明显。
程序体积分析工具推荐使用 bloaty(https://github.com/google/bloaty)进行分析。该工具可用于分析源码体积的占比,分析数据可以作为优化方向。
优化前后代码体积对比:
02 apk体积优化
一般 apk 的体积优化是通过资源文件混淆完成的,因为资源文件数量占比较大,通过将文件名改成短的字符串,达到体积优化的目的。
内容方面,除非存在多余的垃圾文件,体积优化相对而言能改动的较少。不过因为 apk 本身为 zip 格式,需要注意下 zip 中 lzma 和 deflate 的区别。android 默认的压缩方式是 lzma。就压缩率而言,大部分情况 lzma 比 deflate 要高,但解压速率 deflate 比 lzma 要快。
压缩方式参考https://www.cnblogs.com/caimagic/p/5823285.html
优化前后体积对比:
03 内存优化
关于物理内存和虚拟内存的简单描述,主要是以下两点:
1.程序申请的内存,在没有访问之前都是虚拟内存。
2.内存访问时,只有在访问未映射到物理内存的虚拟内存页时,才会触发缺页异常。触发异常后,会分配物理内存。
在使用代码加密的情况下,如 dex 加固,so 加固。
原程序:dex、so 通过 mmap 映射到内存,正常启动不会访问全部内存,只有部分数据会被访问。
加固程序:dex 和 so 需要解密,程序必然会对整个 dex 或 so 大小的内存进行解密,导致物理内存增大,一般预期的增量和 dex 以及 so 大小是有关联的。
由于知道物理内存的分配规则,可以采取以下两种方式:
1.采用分段或部分加密形式处理 dex 或 so,这样可以有效降低物理内存的消耗,而且对性能影响较小。
2.解密后,文件落地,清理内存后,将解密后的文件重新映射到内存并删除。这样对内存几乎没有影响,但性能影响较大。
优化前后内存数据对比:
04 性能优化
数据对齐:以 32 位系统为例,一般一次性处理 4 字节的效率是最高的。如果涉及到字节的数据处理,一般要利用寄存器取 4 字节数据,然后进行偏移或取位运算得到对应的结果,这样会导致多几步运算。当对数据进行对齐后,一般可以直接从内存中拿到想要的数据。
加解密算法:尽可能以寄存器长度为单位进行运算,避免字节处理,这样计算效率可以提高 4-8 倍以上。
业务逻辑:包括数据结构的设计和流程优化,包括但不限于以下几点:
通过调整数据格式和对齐方式,提升数据的存取效率。
重复过程建议添加缓存机制。
加固前后启动性能对比:
总结
以上是我们对程序优化做的一些总结,开发者可根据各自的需求使用相应的策略进行优化和验证。
在代码优化过程中,网易易盾不仅仅关注代码的性能和效率,更要考虑用户的使用体验。确保用户能够更快、更顺畅地使用产品或服务,提高用户满意度和忠诚度。同时,网易易盾一直保持关注用户反馈,不断改进和优化产品或服务,以提供更好的用户体验。
网易易盾应用加固是一项综合性服务,包括网络威胁检测、漏洞扫描、应急响应、风险评估等多个方面。网易易盾业团队根据企业的实际情况,为企业量身定制最合适的加固方案,提供一站式的加固服务,开展实时监控和风险预警,以保障企业的整体安全。即刻申请免费试用易盾app加固服务