原文地址:http://blog.quarkslab.com/reverse-engineering-samsung-s6-sboot-part-i.html#id17
相当一部分基于Exynos处理器的智能手机都用了一个名为SBOOT的专有引导加载程序。 比如三星Galaxy S7,Galaxy S6和Galaxy A3,可能还有更多已上市的三星Exynos的智能手机[1]。 我有幸在评估各种TEE实现时逆向了这个bootloader程序的一部分。 本文是一系列关于SBOOT文章的第一篇。 文中回顾一些ARMv8概念,讨论了我所遵循的方法以及我做出的或正确或错误的假设,同时,分析了这个没有文档的专门使用在三星Galaxy S6上的blob。
上下文
最近,我有幸参与到可信执行环境(TEE)的几个实现中全职来排查bug。 作为一个边缘项目,我开始挖掘更多的TEE实现,特别是在我的个人或者工作使用的智能手机上,巧合的是,他们来自同一个软件编辑器,即由Trust共同创办的Trustonic [2] G&D和金雅拓。 我手头上智能手机唯一的共同点就是是它们都是基于Exynos处理器。
Trustonic的TEE,又名<t-base,从Mobicore,G&D的TEE演变而来。 据我所知,在这个或以前的版本的TEE中公开的技术细节很少。 分析它突然变得比我最初想的更有挑战性、更有趣。 让我们聚焦三星Galaxy S6深入调查吧!
虽然在文件系统中识别受信任应用程序是这个挑战最简单的一部分, 但在我分析的Exynos智能手机上寻找这个TEE操作系统无异于海底捞针。事实上,你能在其他一些智能手机(比如基于高通SoC的手机)上找到的存储TEE操作系统镜像的专有分区,在这部手机上却找不到。它极有可能存储在其他地方,可能在bootloader上,这就是为什么我开始逆向SBOOT。本文是叙述我的TEE操作系统之旅系列文章的第一篇。我会把重点放在如何确定三星S6 SBOOT基地址还有把它加载到IDA上。
ARMv8 的概念
在启动IDA之前,让我们回顾下ARMv8的一些基础。这些可能对熟悉ARMv7,刚接触ARMv8的同学们有些用处。需要明确完整的文档的,请参阅ARMv8开发者指南[3]。我不是ARMv8专家,如果你看到任何错误或需要搞清楚的地方,随君评论。
异常级别
ARMv8通过定义异常级别的概念来引入一个新的异常模型。 异常级别决定运行软件木块的权限级别(PL0到PL3)和运行它的处理器模式(非安全和安全)。 ELn的运行对应PLn的权限,n越大,运行的权限就越大。
异常向量表
出现异常时,处理器切换到一个异常向量表,运行相应的程序。 在ARMv8中,每个异常级别都有自己的异常向量表。 对于那些习惯于反向工程ARMv7bootloaders的人,你会注意到它的格式与ARMv7完全不同:
聪明的读者可能已经注意到,在ARMv8上异常向量表占用128(0x80)个字节长,而在ARMv7上每个条目只有4字节宽,并且每个条目保存一系列异常处理指令。 虽然异常向量表的位置由ARMv7上的VTOR(向量表偏移寄存器)确定,但ARMv8使用三个VBAR(基于向量的地址寄存器)VBAR_EL3,VBAR_EL2和VBAR_EL1。 请注意,一定程度上,这些句柄(或表入口)的运行将依赖于:
1.异常类型(SError, FIQ, IRQ 或者 Synchronous)。
2.如果异常异常级别相同,则使用堆栈指针(SP0或SPx)。
3.如果异常的级别较低,则执行下一个较低级别(AArch64或AArch32)的执行状态。
一个运行在特定权限的软件模块可以使用专用指令和在底层异常级别上运行的软件交互。 例如,用户模式下的进程(EL0)通过发出超级用户调用(SVC)来执行内核(EL1)的系统调用,内核可以与管理程序调用(HVC)与管理程序(EL2)交互,或者直接与 执行安全监视器调用(SMC)的安全监视器(EL3)等。这些service的调用生成由一个异常向量表同步句柄来处理的同步异常。
我会在接下来的文章中更多地介绍我对这篇文章的一些见解。 让我们试着把SBOOT加载到IDA Pro,然后逆向它吧。
反编译SBOOT
据我所知,SBOOT用的是未知的特有格式。
在IDA Pro中加载SBOOT
三星Galaxy S6是由1.5GHz 64位八核三星Exynos 7420 的CPU驱动。请记住,ARMv8处理器可以运行AArch32和AArch64架构的应用程序。 因此,可以将SBOOT加载为32位或64位ARM二进制。
我先预设,BootROM不切换到AArch32架构,先将它作为64位二进制文件加载到IDA Pro中,保留默认选项:
处理器类型:小端ARM架构[ARM]
作为64位代码反编译: 是的
许多AArch64位指令被自动识别。 解开反汇编的指令,基本模块是有意义的,我感觉我真的在处理AArch64代码:
确定基地址
我花了几天时间来确定正确的基地址。 鉴于给你直接的解决方案是毫无意义的,我首先详细描述我所有的尝试,直到做出能给我真正的基地址的正确假设。 正如谚语所说:“授人以鱼不如授人以渔“。
谷歌一下,你就知道
我刚开始在网上搜索和三星bootloader SBOOT相关的东西。 不幸的是,这个问题的结果很少,只有reverseengineering.stackexchange.com上一个可追溯到2015年3月的线索[5]和这个相关。
这个线程主要给我了两个提示。 J-Cho直觉上认为,这个bootloader从偏移量为0x3F000处开始,还断定它实际上从0x10开始。
因为我想排除我的假设,bootloader的基地址是0x00000000,并且其代码总是从0x10开始,我开始寻找在其他Exynos智能手机中使用的引导加载程序。 魅族智能手机的SBOOT在0x10没有给出有效的指令,证实了我的怀疑:
我还分析了在其他引导程序是否有遗留的任何调试字符串,能给我提示SBOOT通常在内存中哪里加载。 并没有彩头:(但是我有另一个:在魅族的SBOOT的一些字符串建议使用U-Boot,即使U-Boot不使用三星Galaxy S6,这是一个值得探索的方向,我开始进一步挖掘。
U-Boot仓库
U-Boot是开源的,支持一系列Exynos芯片。 例如,Exynos 4和Exynos 5已经支持了5年多了。 Exynos 7的支持尚未完全登陆,但根据其邮件列表[6],Exynos 7 ESPRESSO的开发板存在一些补丁。
我可能错过了,但是遍历ESPRESSO开发板的补丁一无收获:(我尝试了从Exynos 4到Exynos 7板多个已知的基地址没有成功,是时候从另一个角度尝试了。
ARM字节池
如果您熟悉逆向ARM汇编,您一定注意到了大量使用字节池来保存要加载到寄存器中的某些常量值。 这个特性可以帮助我们查找大约在何处加载SBOOT,特别是当从文字池加载分支目标地址时。
我搜索了IDA Pro在操作数中标记有错误的所有分支指令(红色高亮显示)。 由于bootloader程序的代码是自包含的,我可以明确假定大多数分支目标地址必然命中在bootLoader程序本身的代码。 有了这个假设,我可以估算出bootloader的基地址。
从第一条指令,我注意到以下分支错误:
这些代码片段上的有趣的事实是:
跳转指令BR(跳转到寄存器)是没有条件的,而且它不会return。
两个分支的操作数值相同(0x2104010),并且位于引导加载程序的早期。
最后一个字节是0x10,这似乎也正是bootloader的代码开始的偏移量。
我任意假定地址0x2104010是一个复位地址,我试图在0x2104000处加载SBOOT二进制,具有以下选项:
处理器类型: ARM小端[ARM]
ROM起始地址: 0x2104000
加载地址: 0x2104000
作为64位代码反编译: 是的
至少,IDA Pro找出的错误少了一些,表明我的假设可能是正确的。 当然,我还不能确定这个基地址是正确的,我需要逆向来进一步确定。 画外音:我几乎认定它正确:)
ARM系统寄存器
鉴于我可能要得到潜在的基地址了,我继续逆向SBOOT,苍天保佑代码流中没有异常。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课