背景
前期readback
什么都都正常,也切出来了各个分区,并制作了scatter。
折腾的时候发现SP Flash Tool
加载preloader的时候有报错:
看日志
1 2 3 4 5 6 7 8 9 10
| 02/23/22 22:35:20.309 BROM_DLL[3848][1900]: DL_HANDLE()::Rom_Load(): ROM loaded, name = preloader (flashtool_handle_internal.cpp:4693)
02/23/22 22:35:20.309 BROM_DLL[3848][1900]: DEBUG: DL_HANDLE::UpdateRomFileInfoByPreloader(): UpdateRomFileInfoByPreloader get bbchiptype : 159 (flashtool_handle_internal.cpp:4359) //chip type 所以应该是强绑定的 02/23/22 22:35:20.309 BROM_DLL[3848][1900]: DL_HANDLE()::UpdateRomFileInfoByPreloader(): Loading SV5 BL, name = preloader (flashtool_handle_internal.cpp:4374)
02/23/22 22:35:20.309 BROM_DLL[3848][1900]: ERROR: DL_HANDLE(0x0BC38DE8)::UpdateRomFileInfoByPreloader(): [0]: preloader - Parse GFH_FILE_INFO error(0x00001008)! (flashtool_handle_internal.cpp:4385) 02/23/22 22:35:20.309 BROM_DLL[3848][1900]: ERROR: DL_HANDLE(0xB7B1A8AC)::File length not match with GFH specified file length (flashtool_handle_internal.cpp:4386) // 不匹配! GFH 指定的文件长度不匹配 02/23/22 22:35:20.309 BROM_DLL[3848][1900]: ERROR: File length (262160) / GFH specified file length (0) (flashtool_handle_internal.cpp:4387) // 这里,文件长度是xxx,但是GFH里指定的不对 02/23/22 22:35:20.309 BROM_DLL[3848][1900]: ERROR: DL_Rom_Load(): <ERR_CHECKPOINT>[232][error][5066]</ERR_CHECKPOINT> [S_DL_PC_BL_INVALID_GFH_FILE_INFO] (flashtool_handle.cpp:941) 02/23/22 22:35:20.309 BROM_DLL[3848][1900]: DL_Rom_Load(): DL_HANDLE->rwlock: WRITE_UNLOCK. (rwlock.cpp:476)
|
所以:
- DL_HANDLE()::Rom_Load() 函数
- 这个平台校验了size字段和实际preloader文件的size
逆向分析
1 2
| $ file libflashtool.v1.so libflashtool.v1.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, BuildID[sha1]=de4c47b0bb41c274fd42efb55ceb476bcc840d7a, not stripped
|
在 DL_HANDLE::UpdateRomFileInfoByPreloader
方法中找到了校验的逻辑:
1 2 3 4 5 6
| if ( *((_DWORD *)var48 + 15) == 7 ) { err_code = DL_HANDLE::UpdateRomFileInfoByPreloader(this, sys_index); if ( err_code ) return err_code; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| erro_code = ROM_ID_Class::LoadGFH((__int64)this + 112, *((_QWORD *)ROM + 123), 0, (__int64)&GFH); if ( erro_code > 0xFFF ) { r12_12 = (const char *)std::string::c_str(ROM); rbx12 = g_hBROM_DEBUG; MetaTrace::MetaTrace( (MetaTrace *)var1030, "FlashToolLib/source/common/handle/src/flashtool_handle_internal.cpp", 4417, 0xFFu, " ERROR:"); MetaTrace::operator()( var1030, rbx12, "DL_HANDLE(0x%08X)::UpdateRomFileInfoByPreloader(): [%u]: %s - Load GFH_FILE_INFO error(0x%08X)! ", this, rom_file_name, r12_12, erro_code); MetaTrace::~MetaTrace((MetaTrace *)var1030); return 5066; } if ( *((_DWORD *)GFH + 8) != *((_QWORD *)ROM + 0x7C) ) {
}
|
ROM + 0x7C
是实际的文件大小
GFH + 8
是 解析preloader 中的GFH结构中的size字段
1 2 3 4
| ROM_ID_Class::LoadGFH GFH_Find(rom_buffer, type, (_QWORD *)st); GFH_Internal_Parser(buff_addr, 0LL, type, GFG_st);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| __int64 __fastcall GFH_Internal_Parser(__int64 buf_addr, __int64 flag_0, int type, _QWORD *GFG_st) { __int64 result; __int64 st; __int64 v8; unsigned int cnt; unsigned int v10; unsigned int i; unsigned int ret; unsigned int reta; char v14; char v15_0;
v15_0 = 0; v14 = 0; if ( flag_0 ) v15_0 = 1; ret = GFH_FILE_INFO_BasicCheck(buf_addr); if ( ret > 0xFFF ) return ret; cnt = *(_DWORD *)(buf_addr + 0x28); for ( i = 0; i < cnt; i = v10 ) { st = buf_addr + i; if ( (*(_DWORD *)st & 0xFFFFFF) != 5066061 ) return 0x1003LL; v10 = i + *(unsigned __int16 *)(st + 4); if ( cnt < v10 ) return 0x1005LL; if ( v15_0 ) { if ( *(_WORD *)(st + 6) <= 0x104u ) { v8 = flag_0 + 24LL * *(unsigned __int16 *)(st + 6) + 8; if ( *(_BYTE *)v8 ) { reta = (*(__int64 (__fastcall **)(__int64, _QWORD))(v8 + 8))(st, *(_QWORD *)(v8 + 16)); if ( reta > 0xFFF ) return reta; v14 = 1; } } } else if ( type == *(unsigned __int16 *)(st + 6) ) { *GFG_st = st; return 0LL; } } if ( v15_0 && v14 ) result = 0LL; else result = 0x1003LL; return result; }
|
[1]
的位置 找到这个结构,然后把指针赋值,分析这段逻辑,其实就是文件头:
指定preloader文件大小是 0x26794
,修改文件大小即可。
解决报错
所以只需要修改 preloader文件的长度为其 +0x24
处 4bytes代表size的字段即可
PS:不能修改这个长度字段
深入研究
之前搞的平台也没注意这个问题,也没报错,但是size对不上,所以需要探究文件格式和为什么检查
什么是preloader
介于boot rom 和 bootloader之间的桥梁
,主要工作是初始化环境,包括c环境,timer,gpio,pmic,uart,i2c等以及装载lk镜像至DRAM中,建立起最基本的运行环境,最重要的就是初始化DRAM。
工作原理 – 启动过程
另一种情况是实现了ATF(Arm Trust Firmware)的时候:
ATF实现原理_chenying126的博客-CSDN博客_atf
- boot rom中执行boot code
- 把preloader加载到 ISRAM中
- 执行preloader,各种初始化的工作(DRAM初始化)
- 把bootloader(uboot, lk)加载到DRAM
- 跳转到lk执行
- lk执行
- 把Linux kernel 和 ramdisk加载到DRAM
- 跳转到kernel
- kernel执行
- 这是在Linux启动过程中使用的一个临时根
preloader 解析
preloader可以看成一个特定格式的可执行文件,所以需要找入口点。
/Users/muhe/Code/MTK6577/mediatek/platform/mt6577/preloader/src/init/init.s
github上找的一个可能是泄漏的基线代码来参考的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| .globl _start
_start :
b resethandler
bss_start:
.word _bss_start
bss_end:
.word _bss_end
stack :
.long sys_stack
stacksz:
.long sys_stack_sz
resethandler : MOV r0, #0 MOV r1, #0 MOV r2, #0 MOV r3, #0 MOV r4, #0 MOV r5, #0 MOV r6, #0 MOV r7, #0 MOV r8, #0 MOV r9, #0 MOV r10, #0 MOV r11, #0 MOV r12, #0 MOV sp, #0 MOV lr, #0
|
这个特征还是很明显的,可以试试看:
上面这个0xEA应该是 b指令,可以借由这个搞定基地址
然后是到main.c
,继续人肉找特征
找到了字符串,但是没有引用关系 :(
通过Ghrida的强制整个binary的分析,然后引用关系确定了main的位置,至此就可以往下看了,对比其他平台preloader的源码,能看个七七八八了。
PS : 基地址编译的时候可以指定的,比如在 linux/bootloader/preloader/platform/mt6735/link_descriptor.ld
1 2 3 4 5 6 7 8 9 10 11
| OUTPUT_ARCH(arm)
ENTRY(_start)
romBase = 0x00201000; ramBase = 0x00102180;
MEMORY { ram : ORIGIN = ramBase, LENGTH = 0xBA80 rom : ORIGIN = romBase, LENGTH = 0x1F000 }
|
还有 : linux/bootloader/preloader/platform/mt6735/default.mak
github真是个好地方啊,还有一个完整的MT6737平台Linux based的基线代码,全套的环境和build产物都有的,可以看到:
- 推测preloader应该是一个elf 经过copyobj之类的处理之后拼接上了特定的文件头
1 2
| $ file *.elf preloader_bd6737m_35g_b_m0.elf: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, with debug_info, not stripped
|
查看相关的makefile可以验证该猜想:
1 2 3 4 5 6 7 8 9 10 11 12
| $(D_BIN)/preloader.elf: $(D_BIN)/$(PL_IMAGE_NAME).elf
$(OBJCOPY) -R .dram $(D_BIN)/$(PL_IMAGE_NAME).elf -O elf32-littlearm $(D_BIN)/preloader.elf
ifeq ($(CFG_PRELOADER_DRAM_USE), 1)
preloader_bin: $(D_BIN)/$(PL_DRAM_IMAGE_NAME).bin
$(D_BIN)/$(PL_DRAM_IMAGE_NAME).bin: $(D_BIN)/$(PL_IMAGE_NAME).elf $(hide) $(OBJCOPY) ${OBJCFLAGS} $(OBJSECOND_FLAG) $(D_BIN)/$(PL_IMAGE_NAME).elf -O binary $(D_BIN)/$(PL_DRAM_IMAGE_NAME).bin
|
遂尝试:
1 2 3 4
| PL_IMG_SECOND_PARTION_SECTION :=.pl_dram.text .pl_dram.data .pl_dram.rodata .pl_dram.start OBJSECOND_FLAG := $(addprefix -j , $(PL_IMG_SECOND_PARTION_SECTION))
objcopy --gap-fill=0xff $OBJSECOND_FLAG input.elf -O binary output.bin
|
addprefix
这个可以忽略
当然,hash想一样还是想多了,毕竟编译环境都不一样,直接上diff:
一共两处:
这个是多了一个GFH结构(说好的 NO_GFH 难道只是说头没有)
preloader_bd6737m_35g_b_m0_LINKED.bin
preloader_bd6737m_35g_b_m0_NO_GFH.bin
- 中间与部分数据不一致
- 比
output.bin
多了一个GFH在尾部
preloader_bd6737m_35g_b_m0.bin
比 preloader_bd6737m_35g_b_m0_NO_GFH.bin
又多了一个GFH头和尾部的签名数据
所以这里可以认定:
- preloader是一个elf,通过copyobj处理后,头、尾添加GFH相关的数据,得到MTK平台的preloader
- 然后MTK平台的preloader再添加EMMC BOOT头,就得到了从EMMC_BOOT_[1,2] 分区中得到的数据
结构
这里以EMMC为例:
EMMC_BOOT + GFH_INFO_EMMC + WTF1 + preloader_code + WTF2
继续看Makefile来分析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| $(D_BIN)/$(PL_IMAGE_NAME).bin: $(D_BIN)/$(PL_IMAGE_NAME)_NO_GFH.bin $(GFH_INFO) $(GFH_HASH) $(PBP_TOOL)
@echo "[ Only for Non-Secure Chip ]" @echo "============================================" @echo "INI_GFH_GEN=NO" @echo "[ Attach $(MTK_PLATFORM) GFH ]" @echo "============================================" @echo " : GFH_INFO - $(GFH_INFO)" @echo " : GFH_HASH - $(GFH_HASH)" cp -f $(GFH_INFO) $@ @chmod 777 $@ cat $< >> $@ cat $(GFH_HASH) >> $@ $(PBP_TOOL) $@ @echo "$(PBP_TOOL) pass !!!!"
// ...
$(D_BIN)/$(PL_IMAGE_NAME).bin: $(D_BIN)/$(PL_IMAGE_NAME)_LINKED.bin
cp -f $< $@
|
遂可以得到:
- EMMC_BOOT
-
MT6737/linux/bootloader/preloader/tools/gen-preloader-img.py
生成
- GFH_INFO_EMMC
-
**linux/bootloader/preloader/platform/mt6735/gfh/default/ns/GFH_INFO_EMMC.txt**
但是这个file size字段是0xffffffff,后续会处理
- WTF1 : GFH_HASH
- GFH_HASH.txt // GFH部分会由PBP_TOOL再次处理
- preloader code
- WTF2 :preloader extension
参考