在VIM3开发板上运行无修改的iOS内核镜像

本文记录了在VIM3开发板上通过虚拟化技术运行未经修改的iOS内核的过程,涉及调试环境搭建、ARM-FVP和VIM3开发板上的调试阶段。在解决了一系列问题,如页表支持、DC ZVA指令模拟和SHA256指令支持后,成功挂载了Ramdisk根文件系统,但系统卡在启动的某个阶段。这次实验加深了对ARM架构的理解并探索了逆向工程技巧。
摘要由CSDN通过智能技术生成

在VIM3开发板上运行无修改的iOS内核镜像

之前在网上有看到过人使用QEMU成功的把IOS内核运行起来且成功挂载根文件系统的相关文章。理论上能在QEMU上跑成功,在真实的ARMv8开发板上运行起来也不是问题。本着研究IOS内核启动流程以及IOS 内核相关安全措施的目的,经过一个多星期的研究和调试,终于成功的把IOS内核通过虚拟化的方式在Khadas VIM3开发板上跑起来了,以下记录了下调试过程中碰到的一些问题。

准备和开发阶段

调试环境搭建

IOS使用的是xnu内核,当前苹果已经开源了xnu源码(但和A系列芯片的相关驱动代码都没有开放),具体源码可以在以下Github仓库中找到:https://github.com/apple/darwin-xnu.git

以下是在QEMU上实验的一些文章,通过以下文章可以了解到很多关于XNU及IOS启动的基本知识,可以直接享用牛人的研究成果,避免重复造车轮子:

Zhuowei : https://worthdoingbadly.com/xnuqemu2/

Jonathan Afek:https://alephsecurity.com/2019/06/25/xnu-qemu-arm64-2/

以上文章写的非常的详细,每个系列都分为两篇,第一篇介绍怎么做,第二批记录了碰到的问题以及问题如何解决的,两位牛人都提供了很多工具用来从IOS升级包中提取所需要的固件。

接下来将介绍基于Zhuowei和Afek的研究成果基础之上,把IOS内核通过Hypervisor运行到Khadas VIM3开发板上的过程,首先第一步是搭建基本的调试环境:

  • Hypervisor使用的是Minos - https://github.com/minosproject/minos

  • 由于IOS解压出来的内核文件保留的内核symbol越来越少,且留下来的symbol对调试几乎没有帮助,基本上都是syscall的地址,如果所有的问题已经被Zhuowei和Afek都记录下来的话,理论上只需要把相关的实现代码移植过来,就可以跑起来。但往往情况会比较复杂,所以一定要有个可以调试的工具做支持,故采取以下思路

    1. 使用ARM DS5作为调试工具,DS5是ARM官方出品的一款非常强大的ARM架构的集成开发调试工具,用户可以免费申请一个30天的试用
    2. 前期在ARM FVP上进行调试,在FVP跑通后基本上可以直接移植到VIM3上

IOS OTA文件选择

苹果从A7芯片开始使用64位的ARMv8架构,以下是苹果每一代芯片的一些基本信息

Screenshot from 2020-02-12 17-05-38

苹果的SOC的每次升级换代还是挺快的,且紧跟最新的ARM指令集,最新的SOC采用了ARMv8.3指令集。由于当前下载的DS5自带的模拟器只支持ARMv8.0指令集(事实上当前市面上的开发板也都是ARMv8.0指令集,包括VIM3),所以只能选择前三个,这里我选择了Apple Typhoon,也就是Apple A8处理器,Iphone 6使用的就是A8处理器,因此从以下地址下载了Iphone 6 IOS 12.2的固件做为调试固件

http://updates-http.cdn-apple.com/2019SpringFCS/fullrestores/041-06917/ECD402F4-499F-11E9-85D3-E64576CE070F/iPhone_4.7_12.2_16E227_Restore.ipsw

按照Jonathan Afek第一篇文章里面的教程,下载相关脚本,得到所需要的image。这里没有完全使用教程里面提到的所有image,有两个image和Afek提到的有差异

  1. secure monitor - 由于我们会使用虚拟机的形式启动ios内核,所以这里我们将在hypervisor中模拟相关SMC调用和一些寄存器的访问模拟
  2. trust cache - Afek教程里面这部分有点复杂,且需要切换到Mac系统(相关工具也提供linux版本,但测试下来不好使),所以选择直接加载recovery ramdisk来作为根文件系统.

按照Afek教程里面的步骤得到以下image

  1. kernel.out MACH-O格式的ios内核镜像
  2. dt.out 设备device tree,IOS内核也是用device tree,但ios 用的device-tree的格式和linux下的格式不太一样,所以无法直接用dtc工具来反编译。Afek教程中的device tree脚本大致作用是把几乎所有外设的compatible字段变为0,不让系统去加载对应驱动,只留下uart和最基本的platform信息,实际上对于前期的调试我们只需要把uart , timer中断驱动起来就可以
  3. ramdisk.out ramdisk镜像

Bootloader

我们要做的第一步工作就是把提取出来的三个image加载到内存当中,也就是我们要实现一个简易的IOS bootloader,IOS使用的是iboot作为bootloader,这部分的代码是不开放的。还好,Zhuowei已经实现了在qemu中加载MACH-O文件的的相关代码,我们只需把qemu中这些代码移植到Minos中就可以,这里有两个方案:

  1. 使用U-boot作为bootloader,在uboot中实现MACH-O文件的解析和加载,把IOS当做Native VM来启动
  2. 使用Minos自带的虚拟机管理程序mvm来启动,启动一个Guest VM

很显然,以上两种方式采用第二种方式会更简单和更以实现,读取文件的操作会更方便,代码主要实现了以下功能

  1. 解析内核文件获取内核的入口地址
  2. 根据内核MACH-O文件提供的内存布局信息计算出ranmdisk,device-tree,bootarg的地址
  3. 加载内核镜像到对应内存区域
  4. 设置内核启动参数,和command line

为了减少在移植过程中尽可能少的出现问题,这里我给VM非配了和iphone6一样多的内存也就是1G

用1G内存这个配置在实际调试过程中碰到了一个问题,由于VIM3 官方Android镜像使用的还是32位,且没有打开LPAE模式,为了模拟这个情况,ARM FVP也是使用的32位非LPAE模式,由于32位下进程的地址空间只有4G,默认情况下mvm会把VM的内存空间全部映射到自己的进程空间内,这就意味着要在mvm这个进程中找到一段1G连续虚拟地址空间,这个情况几乎不太容易满足。为此,通过修改mvm和hypervisor实现了映射VM特定区域到内存空间的功能,用多少映射多少。bootloader的具体实现可以参考https://github.com/minosproject/minos/blob/master/tools/mvm/os/os_xnu.c

Hypervisor中XNU操作系统的支持

xnu系统启动要求bootloader需要把启动参数传递给内核,通过阅读xnu arm64的启动代码,发现这块的处理比较简单,只需要把内核启动参数的硬件地址存放到x0寄存器就可以,xnu arm64的启动代码可以参照https://github.com/apple/darwin-xnu/blob/master/osfmk/arm64/start.s,启动参数的定义如下,启动参数的设置可以参考mvm中的相关代码

struct xnu_arm64_boot_args {
        uint16_t revision;              /* Revision of boot_args structure */
        uint16_t version;               /* Version of boot_args structure */
        uint64_t virt_base;             /* Virtual base of memory */
        uint64_t phys_base;             /* Physical base of memory */
        uint64_t mem_size;              /* Size of memory */
        uint64_t top_of_kdata;          /* Highest physical address used in kernel data area */
        struct xnu_arm64_boot_video video; /* Video Information */
        uint32_t machine_type;          /* Machine Type */
        uint64_t dtb;                   /* Base of flattened device tree */
        uint32_t dtb_length;            /* Length of flattened tree */
        char cmdline[XNU_ARM64_BOOT_LINE_LENGTH]; /* Passed in command line */
        uint64_t boot_flags;            /* Additional flags specified by the bootloader */
        uint64_t mem_size_actual;       /* Actual size of memory */
};

在Minos中添加了https://github.com/minosproject/minos/blob/master/virt/os/os_xnu.c文件用来支持xnu虚拟机的启动

中断控制器支持

苹果A系列处理器并没有使用ARM的GIC系列,而是自己的AIC,搜遍了整个XNU代码只发现了AIC的头文件定义,头文件中也只定义了相关的寄存器偏移,这下糟了,不了解AIC的终端控制器原理,中断信息怎么发送给虚拟机?还好通过查看前面两篇文章,以及xnu中相关代码了解到:

  1. xnu中system tick timer使用了FIQ并没有使用irq方式
  2. xnu中对于FIQ处理简单粗暴,压根不需要操作AIC
void
sleh_fiq(arm_saved_state_t *state)
{
        unsigned int type   = DBG_INTR_TYPE_UNKNOWN;
#if DEVELOPMENT || DEBUG
        int preemption_level = get_preemption_level();
#endif
#if MONOTONIC
        uint64_t pmsr = 0, upmsr = 0; 
#endif /* MONOTONIC */

#if MONOTONIC
        if (mt_pmi_pending(&pmsr, &upmsr)) {
                type 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值