推荐序

本文作者杨君,他之前投稿过一篇安全方面的文章:iOS 符号表恢复 & 逆向支付宝,这次他在国庆期间又发来一篇大作。本文介绍的黑科技非常牛逼,可以将别人的 iOS 应用转成动态库。

看完本文,我在想:如果把支付宝、淘宝、京东、当当、一号店都集中到一个 App 中,不知是怎样的体验?至少这种事情在技术上是可以做到的了。

作者介绍:杨君,中山大学计算机系研究生,iOS 开发者,擅长领域 iOS 安全和逆向工程,个人博客:http://blog.imjun.net 。

前言

本文会介绍一个自己写的工具,能够把第三方 iOS 应用转成动态库,并加载到自己的 App 中,文章最后会以支付宝为例,展示如何调用其中的 C 函数和 OC 方法。

工具开源地址:https://github.com/tobefuturer/app2dylib

有什么用

为什么要把第三方应用转成动态库呢?与一般的注入动态库 + 重签名打包的手段有什么不一样呢?

好处主要有下面几点:

  1. 可以直接调用别人的算法
    逆向分析别人的应用时,可能会遇到一些私有算法,如果搞不定的话,直接拿来用就好。
  2. 掌控程序的控制权
    程序的主体是自己的 App,第三方应用的代码只是以动态库的形式加载,主要的控制权还是在我们自己手里,所以可以直接绕过应用的检测代码(文章最后有关于这部分攻防的讨论)。
  3. 同个进程内加载多个应用
    重签名打包毕竟只能是原来的应用,但是如果是动态库的话,可以同时加载多个应用到进程内了,比如你想同时把美图秀秀和饿了么加载进来也是可以的(秀秀不饿,想想去年大众点评那个 APPmixer 的软广 - -! )。

应用和动态库的异同

我们要把应用转成动态库,首先要知道这两者之前有什么相同与不同,有相同的才存在转换的可能,而不同之处就是我们要重点关注的了。

相同点:

可执行文件和动态库都是标准的 Mach-O 文件格式,两者的文件头部结构非常类似,特别是其中的代码段(TEXT), 和数据段(DATA)结构完全一致,这也是后面转换工作的基础。

不同点

不同点就是我们转换工作的重点了,主要有:

  1. 头部的文件类型
    一个是 MH_EXECUTE 可执行文件, 一个是 MH_DYLIB 动态库, 还有各种头部的 Flags,要特别留意下可执行文件中 Flags 部分的 MH_PIE 标志,后面再详细说。
  2. 动态库文件中多一个类型为 LC_ID_DYLIB  的 Load Command, 作用是动态库的标识符,一般为文件路径。路径可以随便填,但是这部分必须要有,是 codesign 的要求。
  3. 可执行文件会多出一个 PAGEZERO 段,动态库中没有。这个段开始地址为 0(NULL 指针指向的位置),是一个不可读、不可写、不可执行的空间,能够在空指针访问时抛出异常。这个段的大小,32 位上是 0x4000,64 位上是 4G。这个段的处理也是转换工作的重点之一,之前有人尝试转换,不成功就是因为没有处理好 PAGEZERO.

实现细节

修改文件类型

第一步是修改文件的头部信息,把文件类型从可执行文件修改成动态库,同时把一些 Flags 修改好。

这里一个比较关键的 Flag 是可执行文件中的 MH_PIE 标志位,(position-independent executable)。

这个标志位,表明可执行文件能够在内存中任意位置正确地运行,而不受其绝对地址影响的特性,这一特性是动态库所必须的一个特性。没有这个标志位的可执行文件是没有办法转换成动态库的。iOS 系统中,arm64 架构下,目前这个标志位是必须的,不然程序无法运行(系统的安全性要求),但是 armv7 架构下,可以没有这个标志位,所以支付宝 armv7 版本的可执行文件是不能转成动态库的,就是这个原因。不过所有的 arm64 的应用都是可以转换的,后面演示时用的支付宝是 arm64 架构的。

头部中添加 LC_ID_DYLIB

直接在文件头部中按照文档格式插入一个 Load Command,并填入合适的数据。这里要注意下插入内容的字节数必须是 8 字节对齐的。

修改 PAGEZERO 段

这部分是最重要的一部分,因为 arm64 上这个段的大小有 4G,直接往内存中加载,会提示没有足够的连续的地址空间,所以必须要调整这个段的大小,而要调整 PAGEZERO 这个段的大小 , 又会引起一连串的地址空间的变化,所以不能盲目的直接改,必须结合 dyld 的源码来对应修改。(注意这里不能直接把 PAGEZERO 这个段给去掉,也不能直接把大小调成 0,因为涉及到 dyld 的 rebase 操作,详细看后面)

1. 所有段的地址都要重新计算

单纯减少 PAGEZERO 段的占用空间,作用不大,因为 dyld 加载动态库的时候,要求是所有的段一起进行 mmap(详细可以查看 dyld 源码的 ImageLoaderMachO::assignSegmentAddresses 函数),所以必须把接下来所有的段的地址都重新计算一次。

同时要保证,前后两个段没有地址空间重叠,并且每个段都是按 0x4000 对齐。因为 PAGEZERO 是所有段中的第一个,所以可以直接把  PAGEZERO 的大小调整到 0x4000,然后后面每一个段都按顺序依次减少同样大小 (0xFFFFC000 = 0x100000000 - 0x4000),同时能保证每个段在文件内的偏移量不变。

修改前:

修改后:

2. 对动态库进行 rebase 操作

这里的 rebase 是系统为了解决动态库虚拟内存地址冲突,在加载动态库时进行的基地址重定位操作。

这一步操作是整个流程里最重要的,因为按照前面的操作,整个文件地址空间已经发生了变化,如果 dyld 依然按照原来的地址进行 rebase,必然会失败。

那么 rebase 操作需要做哪些工作呢?

相关的信息储存在 Mach-O 文件的 LINKEDIT 段中 , 并由 LC_DYLD_INFO_ONLY 指定 rebase info 在文件中的偏移量

详细的 rebase 信息:

红框里那些 Pointer 的意思是说,在内存地址为 0x367C698 的地方有一个指针,这个指针需要进行 rebase 操作 , 操作的内容就是和前面调整地址空间一样,每个指针减去 0xFFFFC000。

3. 为什么不能直接去掉 PAGEZERO 这个段

这个原因要涉及到文件中 rebase 信息的储存格式,上面的图中,可以看出 rebase 要处理的是一个个指针,但是实际上这些信息在文件中并不是以指针数组的形式存在,而是以一连串 rebase opcode 的形式存在,上面看到的一个个指针其实是 Mach O View 这个软件帮我们将 opcode 整理得到的。

这些 opcode 中有一种操作比较关键,REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB。

这个 opcode 的意思是 , 接下去需要调整文件的中的第 2 个段,就是图中 segment(2) 所表示的含义。

所以说,如果把 PAGEZERO 这个段给去掉了,文件中各个段的序号也就都错位了,与 rebase 中的信息就对应不上了。

而且把这个段大小改为 0,也是不行的,因为 dyld 在加载的过程中,会重新自动过滤掉大小为 0 的段,也会导致同样的段序号错位的问题。(有兴趣的同学可以看下 dyld 的源码,在 ImageLoaderMachO 类的构造函数里)

这就是为什么必须要保留 PAGEZERO 这个段,同时大小不能为 0。

修改符号表

正常的线上应用是不存在符号表的,但是如果你之前用了我的另一个工具 restroe-symbol 来恢复符号表的话,这个地方自然也需要做一些处理,处理方法同 rebase 类似,减去 0xFFFFC000.

不过有一些符号需要单独过滤,比如这个:

这个 radr://5614542 是个什么神奇的符号呢,google 就能发现,念茜的 twitter 上提过这个奇葩的符号。(女神果然是女神 , 棒~

黑科技:把第三方 iOS 应用转成动态库相关推荐

  1. 又有黑科技啦,让老照片还原成彩色!ColouriseSG深度学习上色工具

    先前给各位介绍过 AI Image Enlarger黑科技-人工智能无损放大图片素材不失真 ,相信大家都会人工智能有或多或少的了解吧-今天给大家推荐老照片修复三色工具.相信大家都曾经看过老的照片,不管 ...

  2. Android导入第三方静态库.a编译成动态库.so

    http://ikinglai.blog.51cto.com/6220785/1324985 在Android开发的时候,经常会使用到用c或c++编写的第三方的静态库.如果有源码的话,可以直接跟你自己 ...

  3. android.bp编译生成so,Android导入第三方静态库.a编译成动态库.so

    在Android开发的时候,经常会使用到用c或c++编写的第三方的静态库.如果有源码的话,可以直接跟你自己的代码一去编译成动态库so,但是如果没有源码的话,你就必须在自己的动态库so里面将别人生成好的 ...

  4. 属性浏览器控件QtTreePropertyBrowser编译成动态库(设计师插件)

    文章目录 一.回顾 二.动态库编译 1.命令行编译动态库和测试程序 2.vs工具编译动态库和测试程序 3.安装文档 4.测试文档 三.设计师插件编译 1.重写QDesignerCustomWidget ...

  5. suricata 编译成动态库使用

    项目中需求使用suricata 检测功能,只需要获取检测得到的 alert 结果, 需要将suricata的检测功能集成到我们的项目中,并提供接口动态加载规则. 源代码版本 6.0.4 源码 将sur ...

  6. iOS中创建,使用动态库(dylib)

    测试环境: xcode4.5.2   Mac OS X 10.8.2 重要:由于苹果不支持自己创建动态库,所以这里需要替换两个文件 1:iOS Device 需要替换的文件 替换路径:/Applica ...

  7. 将自己写的经常复用的类封装成动态库的方法

    C++的开发不像c#那么给力,基本上引用下就可以了,它的步骤比较繁琐. 第一步:制作动态库 利用VC新建工程时选择win32 dynamic-Link Library(空的工程),然后添加头文件和cp ...

  8. 使用Roslyn将代码编译成单独的网络模块并将它们组装成动态库

    目录 介绍 代码 代码说明 主程序说明 实用方法 CreateCompilationWithMscorelib(...)方法 EmitToArray(...) 方法 总结 GitHub 上的代码 介绍 ...

  9. 算法开发:将合并后的模型编译成动态库(so文件)提供给qt调用

    一 .说明: 模型:分类+分割模型,4个模型合并成一个大模型,参考我之前的博客介绍:算法开发:多模型合并,加快推理速度_喜欢天晴的博客-CSDN博客 环境:tensorrt8.2.3.0,cuda版o ...

最新文章

  1. layDate的使用
  2. zabbix安装配置详解(一)
  3. Cryptodome 安装
  4. 【Python面试】 说说Python可变与不可变数据类型?
  5. React封装一个组件弹出框
  6. 设计模式(2)——观察者模式
  7. kjb文件 解析_微信安装包解析异常
  8. doxygen的特定命令
  9. CSUOJ1238--兵临城下
  10. CAD图纸该如何修改背景颜色呢?
  11. 怎么让input控件,填充满td
  12. 百度翻译api设置 java_Java调用百度API实现翻译
  13. IDEA创建web工程+tomcat配置
  14. Mysql数据库to_days()用法
  15. SAP BASIS ADM100 中文版 Unit 3(2)
  16. ogc是一个非营利性组织_一个非营利组织如何在为用户构建更好的数字平台时省钱...
  17. 大跨度桥梁类毕业论文文献包含哪些?
  18. linux定时at,Linux定时任务之at
  19. 【物联网实训项目】------(四)家庭智慧安防系统之智能温控
  20. 前端综合 油画商城案例

热门文章

  1. 【2.3.3被拒】苹果iOS上架屏幕截图尺寸ps制作教程
  2. 6-4 2018Final静态成员(黑名单)
  3. ubuntu安装和卸载python3.8
  4. Windows蓝牙通信的开发
  5. 如何规划你的职业目标
  6. 广州大学计算机学院谢冬青杀人案,广州大学城故意杀人案嫌犯被批捕:疑多次违规被举报心生怨恨...
  7. Xshell使用指北(持续更新)
  8. CVPR2021 6篇惊艳审稿人的抠图算法代码汇总!附创新点
  9. 1.5输出图案(由*组成的图形·)C程序设计第四版(谭浩强)
  10. oracle修改连接端口,更改Oracle数据库连接端口