用于反汇编联发科基带的ghidra nanomips isa module

原文:https://www.nccgroup.com/research-blog/ghidra-nanomips-isa-module/
原作者:James Chambers

2023年末至2024年初,NCC集团硬件和嵌入式系统业务部门承接了一项任务,对多款智能手机的基带固件进行逆向工程。这其中包括基于nanoMIPS架构的联发科5G基带固件。我们了解到一些针对Ghidra的nanoMIPS模块是私下开发的,当时并没有公开可用的可靠选项,因此我们自行开发了Ghidra的nanoMIPS反汇编器和反编译器模块。

出于(节省)时间的考虑,我们专注于实现在实际基带固件中遇到的功能和指令,而将尚不需要的复杂P-Code指令模拟保留未实现。虽然该模块仍在开发中,但它仍然能够反编译我们分析过的大部分基带固件。结合一些联发科固件中包含的调试符号信息,该模块在逆向工程过程中发挥了重要作用。

在这里,我们将演示如何将联发科基带固件用我们的 nanoMIPS ISA模块加载到 Ghidra 中,来进行分析。

为了分析固件示例,我们查找了可能搭载带5G支持的联发科SoC的手机。一些相对较新的摩托罗拉机型是不错的选择。(这些设备不属于我们的客户合作范围。)

我们在 https://mirrors.lolinet.com/firmware/lenomola/ 上找到了许多 Android 固件镜像,其中包括摩托罗拉 Moto Edge 2022(代号 Tesla)的固件镜像:
https://mirrors.lolinet.com/firmware/lenomola/tesla/official/。
该型号基于联发科 Dimensity 1050(MT6879)SoC。

固件有一些特定于运营商的版本。我们将随机选择
https://mirrors.lolinet.com/firmware/lenomola/2022/tesla/official/TMO/XT2205-1_TESLA_TMO_12_S2STS32.71-118-4-2-6-3_subsidy-TMO_UNI_RSU_QCOM_regulatory-DEFAULT_cid50_CFC.xml.zip

实际的 nanoMIPS 固件位于从ZIP包里提取的md1img.img中

为了提取md1img的文件内容,我们还编写了一些 Kaitai 结构定义,并使用简单的 Python 脚本来运行结构解析,并将不同部分输出到单独的文件中。
ksy Kaitai定义还可用于通过Kaitai IDE以交互方式探索这些文件。

使用选项–outdir 运行 md1_extract.py, 将提取其中包含的文件md1img.img:

$ ./md1_extract.py ../XT2205-1_TESLA_TMO_12_S2STS32.71-118-4-2-6-3_subsidy-TMO_UNI_RSU_QCOM_regulatory-DEFAULT_cid50_CFC/md1img.img --outdir ./md1img_out/
extracting files to: ./md1img_out
md1rom: addr=0x00000000, size=43084864
        extracted to 000_md1rom
cert1md: addr=0x12345678, size=1781
        extracted to 001_cert1md
cert2: addr=0x12345678, size=988
        extracted to 002_cert2
md1drdi: addr=0x00000000, size=12289536
        extracted to 003_md1drdi
cert1md: addr=0x12345678, size=1781
        extracted to 004_cert1md
cert2: addr=0x12345678, size=988
        extracted to 005_cert2
md1dsp: addr=0x00000000, size=6776460
        extracted to 006_md1dsp
cert1md: addr=0x12345678, size=1781
        extracted to 007_cert1md
cert2: addr=0x12345678, size=988
        extracted to 008_cert2
md1_filter: addr=0xffffffff, size=300
        extracted to 009_md1_filter
md1_filter_PLS_PS_ONLY: addr=0xffffffff, size=300
        extracted to 010_md1_filter_PLS_PS_ONLY
md1_filter_1_Moderate: addr=0xffffffff, size=300
        extracted to 011_md1_filter_1_Moderate
md1_filter_2_Standard: addr=0xffffffff, size=300
        extracted to 012_md1_filter_2_Standard
md1_filter_3_Slim: addr=0xffffffff, size=300
        extracted to 013_md1_filter_3_Slim
md1_filter_4_UltraSlim: addr=0xffffffff, size=300
        extracted to 014_md1_filter_4_UltraSlim
md1_filter_LowPowerMonitor: addr=0xffffffff, size=300
        extracted to 015_md1_filter_LowPowerMonitor
md1_emfilter: addr=0xffffffff, size=2252
        extracted to 016_md1_emfilter
md1_dbginfodsp: addr=0xffffffff, size=1635062
        extracted to 017_md1_dbginfodsp
md1_dbginfo: addr=0xffffffff, size=1332720
        extracted to 018_md1_dbginfo
md1_mddbmeta: addr=0xffffffff, size=899538
        extracted to 019_md1_mddbmeta
md1_mddbmetaodb: addr=0xffffffff, size=562654
        extracted to 020_md1_mddbmetaodb
md1_mddb: addr=0xffffffff, size=12280622
        extracted to 021_md1_mddb
md1_mdmlayout: addr=0xffffffff, size=8341403
        extracted to 022_md1_mdmlayout
md1_file_map: addr=0xffffffff, size=889
        extracted to 023_md1_file_map

最相关的文件是:
md1rom是 nanoMIPS 固件映像
md1_file_map 提供更多关于 md1_dbginfo 文件的上下文:它的原始文件名是DbgInfo_NR16.R2.MT6879.TC2.PR1.SP_LENOVO_S0MP1_K6879V1_64_MT6879_NR16_TC2_PR1_SP_V17_P38_03_24_03R_2023_05_19_22_31.xz
md1_dbginfo 是一个 XZ 压缩二进制文件,含有md1rom 的调试信息(包括符号)

提取调试符号

md1_dbginfo是包含符号、文件名以及对应的地址的另一种二进制文件格式。我们将根据md1_file_map来的文件名,对其进行重命名和解压缩:

$ cp 018_md1_dbginfo DbgInfo_NR16.R2.MT6879.TC2.PR1.SP_LENOVO_S0MP1_K6879V1_64_MT6879_NR16_TC2_PR1_SP_V17_P38_03_24_03R_2023_05_19_22_31.xz
$ unxz DbgInfo_NR16.R2.MT6879.TC2.PR1.SP_LENOVO_S0MP1_K6879V1_64_MT6879_NR16_TC2_PR1_SP_V17_P38_03_24_03R_2023_05_19_22_31.xz
$ hexdump DbgInfo_NR16.R2.MT6879.TC2.PR1.SP_LENOVO_S0MP1_K6879V1_64_MT6879_NR16_TC2_PR1_SP_V17_P38_03_24_03R_2023_05_19_22_31 | 头
00000000 43 41 54 49 43 54 4e 52 01 00 00 00 98 34 56 00 |CATICTNR.....4V.|
00000010 43 41 54 49 01 00 00 00 00 00 00 00 4e 52 31 36 |CATI........NR16|
00000020 2e 52 32 2e 4d 54 36 38 37 39 2e 54 43 32 2e 50 |.R2.MT6879.TC2.P|
00000030 52 31 2e 53 50 00 4d 54 36 38 37 39 5f 53 30 30 |R1.SP.MT6879_S00|
00000040 00 4d 54 36 38 37 39 5f 4e 52 31 36 2e 54 43 32 |.MT6879_NR16.TC2|
00000050 2e 50 52 31 2e 53 50 2e 56 31 37 2e 50 33 38 2e |.PR1.SP.V17.P38.|
00000060 30 33 2e 32 34 2e 30 33 52 00 32 30 32 33 2f 30 |03.24.03R.2023/0|
00000070 35 2f 31 39 20 32 32 3a 33 31 00 73 00 00 00 2b |5/19 22:31.s...+|
00000080 ed 53 00 49 4e 54 5f 56 65 63 74 6f 72 73 00 4c |.S.INT_Vectors.L|
00000090 08 00 00 54 08 00 00 62 72 6f 6d 5f 65 78 74 5f |...T...brom_ext_|

为了从调试信息文件中提取信息,我们制作了另一个 Kaitai 定义和脚本,来提取符号并以与 Ghidra 脚本ImportSymbolsScript.py兼容的文本格式输出它们:

$ ./mtk_dbg_extract.py md1img_out/DbgInfo_NR16.R2.MT6879.TC2.PR1.SP_LENOVO_S0MP1_K6879V1_64_MT6879_NR16_TC2_PR1_SP_V17_P38_03_24_03R_2023_05_19_22_31 | tee dbg_symbols.txt
INT_Vectors 0x0000084c l
brom_ext_main 0x00000860 l
INT_SetPLL_Gen98 0x00000866 l
PLL_Set_CLK_To_26M 0x000009a2 l
PLL_MD_Pll_Init 0x000009da l
INT_SetPLL 0x000009dc l
INT_Initialize_Phase1 0x027b5c80 l
INT_Initialize_Phase2 0x027b617c l
init_cm 0x027b6384 l
init_cm_wt 0x027b641e l
...

(目前脚本设置为仅输出标签定义而不是函数定义,因为不知道所有符号是否都用于函数。)

安装扩展

首先,我们需要为 Ghidra 安装 nanoMIPS 模块。在 Ghidra 主窗口中,前往“File > Install Extensions”,
点击“Add Extension”加号按钮,然后选择模块的 Zip 文件(例如ghidra_11.0.3_PUBLIC_20240424_nanomips.zip)。
然后重启 Ghidra。

初始加载
加载 md1rom 为原始二进制镜像(raw binary image)。
从md1img.img的解压目录中选择000_md1rom,并保留“Raw Binary”作为格式。
对于语言,点击“Browser”省略号,并使用过滤器 找到小端 32 位 nanoMIPS 选项 (nanomips:LE:32:default ),然后点击“确定”。

我们将在偏移量 0 处加载image,因此无需其他选项。再次单击“确定”即可加载原始二进制文件。

当 Ghidra 询问您是否要进行初始自动分析时,选择“否”。我们必须首先设置镜像内存地址空间0x90000000。

内存映射

打开“内存映射”窗口,然后单击“添加内存块”的加号。

我们将新块命名为“mirror”,将起始地址设置为ram:90000000,长度与基础映像“ram”块的长度(0x2916c40)匹配,权限为读取和执行,并将“块类型”设置为“字节映射”,源地址为 0,映射率为 1:1。

还要将原始“ram”块的权限更改为仅读取和执行。保存内存映射更改并关闭“内存映射”窗口。

请注意,此内存映射是不完整的;它只是使反汇编工作所需的最小设置。

调试符号

接下来,我们将加载调试符号。打开脚本管理器窗口并搜索ImportSymbolsScript.py。运行脚本并选择mtk_dbg_extract.py之前生成的文本文件 ( dbg_symbols.txt)。这将创建一堆标签,其中大部分位于镜像地址空间中。

反汇编

现在我们可以开始反汇编了。地址 0 处有一条跳转指令,可以引导我们开始,因此只需选中地址 0 处的字节,然后按“d”键或右键单击并选择“反汇编”即可。借助调试符号,您可能会注意到该指令跳转到了INT_Initialize_Phase1相应的函数。
基于流程的反汇编现在将开始发现一堆代码。初始反汇编可能需要几分钟才能完成。

然后,我们可以通过“分析 > 自动分析…”运行常规的自动分析。这应该也能发现更多代码,并需要几分钟的时间进行反汇编和反编译。
我们发现,“Non-Returning Functions”分析器在这些固件映像中使用默认配置会产生许多误报,从而扰乱代码流,因此我们建议在初始分析时禁用它。一次性“Decompiler Parameter ID”分析器是下一步运行的一个很好的选择,可以更好地检测函数输入类型。

结论

尽管该模块仍在开发中,但其结果已经可用于分析,并使我们能够对基带处理器中的一些关键功能进行逆向工程。

nanoMIPS Ghidra 模块和联发科二进制文件解包器可以在我们的 GitHub 上找到:

https://github.com/nccgroup/ghidra-nanomips
https://github.com/nccgroup/mtk_bp

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注