识别未知固件更新文件中的 ROM 段

逆向工程 固件 入口点
2021-06-29 20:40:33

我正在处理FujiFilm FinePix S1800 相机固件更新到目前为止,我已经设法识别指令集 (ARCompact) 并使用隐藏的服务菜单从相机中转储一些东西,其中之一称为“BOOTCODE”。

固件更新未加密,但有几个我需要剥离的标头。

00000000  52 48 49 52 01 10 07 01  00 06 05 00 01 00 a4 01  |RHIR............|
00000010  01 00 00 00 01 00 dc 14  00 80 04 00 5a 04 aa a1  |............Z...|
00000020  01 00 00 00 dd 14 31 07  00 80 2e 00 8a 60 7c 91  |......1......`|.|
00000030  00 00 00 00 0e 1c 4c 05  00 00 43 00 cd 37 11 ea  |......L...C..7..|
00000040  01 00 00 00 5a 21 21 00  00 a0 4d 00 f9 64 e2 8a  |....Z!!...M..d..|
00000050  00 00 00 00 7b 21 a6 02  00 a0 4d 00 3f d3 06 86  |....{!....M.?...|
00000060  00 00 00 00 22 24 34 00  00 a0 4d 00 3b 3c 2a 80  |...."$4...M.;<*.|
00000070  02 00 00 00 20 b4 04 00  00 00 00 00 00 00 00 00  |.... ...........|
00000080  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000001f0  4b 48 49 52 80 9b fc 28  7f 64 03 d7 04 00 2f 01  |KHIR...(.d..../.|
00000200  f1 c0 3a 09 e1 27 30 d0  fa 0f 81 33 d1 c0 e0 7e  |..:..'0....3...~|
00000210  f1 c0 c6 0d 01 29 fc 1c  c8 b6 2c d0 00 88 0a 23  |.....)....,....#|
00000220  00 37 80 e0 b8 08 02 00  26 0c 61 2a 00 dd 08 77  |.7......&.a*...w|
00000230  02 b8 07 e0 84 20 3f 0f  2a 0c 61 2a 02 24 1c 30  |..... ?.*.a*.$.0|
00000240  8b 70 1a 70 17 f0 0e 0c  61 2a a9 70 8e 0e e1 26  |.p.p....a*.p...&|
00000250  3a 70 04 e0 84 20 3f 0f  0a 0c 61 2a 02 24 1c 30  |:p... ?...a*.$.0|
00000260  8b 76 c9 70 0a 0f e1 26  2a 71 15 20 4c 23 c0 a4  |.v.p...&*q. L#..|
00000270  01 e5 d7 0d c2 93 00 d9  15 20 cc 23 20 a4 64 d9  |......... .# .d.|
00000280  12 d0 22 a0 e9 70 5e 0c  a0 01 0a 71 42 0d 81 27  |.."..p^....qB..'|

BOOTCODE.DMP与位于同一指令开始了0x3620-0x36A0,所以这可能是入口点之一。

问题是,我不确定标头在哪里结束,ROM 在哪里开始。我试图强行使用标题格式,但到目前为止一无所获。除了获得 TSOP 编程器之外,我没有其他方法可以获得设备的干净 ROM 转储。

简而言之,我正在寻求两件事的帮助:

  • 从更新文件中提取 ROM
  • 确定 ROM 的基础

更新 (2019-11-16 18:28)

启动代码的固件转储似乎以 16 个字节为前缀,其中前四个似乎表示基地址。

更新 (2019-11-16 19:30)

进一步分析表明,ROM 可能从 0x200 开始,因为这会反编译为以下带有猜测(但不正确)的基地址。调用此地址的代码也是有效的。

                 push    blink
                 bl.d    sub_F015C518
                 ld      r0, [unk_EFF0CCA4]
                 bl      sub_F01743E0
                 pop     blink
                 j       [blink]
1个回答

二进制不是单个整体块。我弄清楚了结构,但花了一些错误的开始。

首先,我按照您的提示从偏移量 0x200 开始将二进制文件加载到 IDA 中。函数被反汇编好了,但数据引用大部分都关闭了。所以我查看了字符串,看看是否可能有一些字符串表的候选对象。这一堆看起来很有希望:

ROM:0030D8F3 aLanguageZoneAl:.ascii "LANGUAGE ZONE : ALL"
ROM:0030D8F3                 .byte 0
ROM:0030D907 aLanguageZoneJp:.ascii "LANGUAGE ZONE : JP"
ROM:0030D907                 .byte 0
ROM:0030D91A aLanguageZoneUs:.ascii "LANGUAGE ZONE : US"
ROM:0030D91A                 .byte 0
ROM:0030D92D aLanguageZoneEu:.ascii "LANGUAGE ZONE : EU"
ROM:0030D92D                 .byte 0

现在,正确的方法是编写一个脚本来扫描数据库中的 dword 数组,该数组的值与字符串地址具有相同的差异。然而,我很懒,所以我对自己说,“图像基数/移位值可能是 0x100 的倍数,所以地址的最后一个字节将是相同的,即我需要寻找一个类似的表”:

.. .. .. F3
.. .. .. 07
.. .. .. 1A

这可以通过使用模式“F3 ? ? ? 07 ? ? ? 1A”(不带引号;'?' 表示通配符字节)的二进制搜索轻松完成只有一击:

ROM:001A8DC4   .long unk_35A0F3
ROM:001A8DC8   .long unk_35A107
ROM:001A8DCC   .long unk_35A11A

因此,如果第一个字符串应该在 35A0F3,我们需要将二进制文件重新设置为 0x35A0F3-0x30D8F3 = 0x4C800 字节。这可以通过 Edit-Segment-Rebase 程序...,Shift delta 来完成。完成后,我们的表格会很好地显示出来,并且还排列了其他字符串:

ROM:001F55C4   .long aLanguageZoneAl   # "LANGUAGE ZONE : ALL"
ROM:001F55C8   .long aLanguageZoneJp   # "LANGUAGE ZONE : JP"
ROM:001F55CC   .long aLanguageZoneUs   # "LANGUAGE ZONE : US"
ROM:001F55D0   .long aLanguageZoneEu   # "LANGUAGE ZONE : EU"
ROM:001F55D4   .long aLanguageZoneEg   # "LANGUAGE ZONE : EG"
ROM:001F55D8   .long aLanguageZoneEe   # "LANGUAGE ZONE : EE"
ROM:001F55DC   .long aLanguageZoneE1   # "LANGUAGE ZONE : E1"
ROM:001F55E0   .long aLanguageZoneCh   # "LANGUAGE ZONE : CH"
ROM:001F55E4   .long aLanguageZoneUn   # "LANGUAGE ZONE : Unknown"

但是,这使得代码从 4C800 开始,并且图像标题中没有这样的值,这让我怀疑这不是全貌。所以我决定寻找解析标题的代码。一个好的开始是寻找魔法值(如标头签名)。

寻找 52 48 49 52 ("RHIR") 没有得到任何命中(字节交换版本也没有)。但是,我记得 ARCompact 使用一种特殊的方式来编码 32 位立即数(又名“长立即数”):它们以混合字节序存储(高位字后跟低位字)。搜索 49 52 52 48,我得到一击,备份找到函数开始后,我得到以下信息:

ROM:0018538E sub_18538E:
ROM:0018538E
ROM:0018538E var_4= -4
ROM:0018538E
ROM:0018538E     st.a    fp, [sp,var_4]
ROM:00185392     mov     fp, sp
ROM:00185396     sub     sp, sp, 4
ROM:00185398     mov     r18, r0
ROM:0018539A     mov     r16, r1
ROM:0018539C     sub     r1, fp, 4
ROM:001853A0     mov     r0, 6
ROM:001853A2     bl      sub_6CC44
ROM:001853A6     ld      r0, [fp,var_4]
ROM:001853AA     cmp     r0, r16
ROM:001853AC     bhs     loc_1853B2
ROM:001853AE     ld      r0, =0x1C00114
ROM:001853B0     b       loc_18541E
ROM:001853B2 # ---------------------------
ROM:001853B2
ROM:001853B2 loc_1853B2:                  
ROM:001853B2     ld      r0, [r18]
ROM:001853B6     mov     r13, r18
ROM:001853B8     mov     r17, r18
ROM:001853BC     cmp     r0, 0x52494852
ROM:001853C2     bne     loc_1853D8
ROM:001853C4     ld      r0, [r17,8]
ROM:001853C8     and     r20, r0, 0xFFFF00
ROM:001853D0     bl      sub_50814
ROM:001853D4     cmp     r0, r20
ROM:001853D6     beq     loc_1853DC
ROM:001853D8
ROM:001853D8 loc_1853D8:                  
ROM:001853D8     ld      r0, =0x1C00111
ROM:001853DA     b       loc_18541E
ROM:001853DC # ---------------------------
ROM:001853DC
ROM:001853DC loc_1853DC:                  
ROM:001853DC     add     r13, r13, 0x10
ROM:001853DE     mov     r15, 0
ROM:001853E0
ROM:001853E0 loc_1853E0:                  
ROM:001853E0     ldb     r0, [r17,6]
ROM:001853E4     cmp     r15, r0
ROM:001853E6     bge     loc_18541C
ROM:001853EA     ldw     r0, [r13]
ROM:001853EC     mov     r14, r13
ROM:001853EE     cmp     r0, 1
ROM:001853F0     bne     loc_185416
ROM:001853F2     ldw     r1, [r14,4]
ROM:001853F4     ldw     r2, [r14,6]
ROM:001853F6     asl     r1, r1, 9
ROM:001853F8     asl     r2, r2, 9
ROM:001853FA     mov     r19, r1
ROM:001853FC     add     r0, r1, r18
ROM:00185400     st      r2, [fp,var_4]
ROM:00185404     mov     r1, r2
ROM:00185406     bl      sub_24C9A4
ROM:0018540A     ld      r1, [r14,0xC]
ROM:0018540C     cmp     r0, r1
ROM:0018540E     beq     loc_185414
ROM:00185410     ld      r0, =0x1C00111
ROM:00185412     b       loc_18541E
ROM:00185414 # ---------------------------
ROM:00185414
ROM:00185414 loc_185414:                  
ROM:00185414     add     r13, r13, 0x10
ROM:00185416
ROM:00185416 loc_185416:                  
ROM:00185416     add     r15, r15, 1
ROM:00185418     sexw    r15, r15
ROM:0018541A     b       loc_1853E0
ROM:0018541C # ---------------------------
ROM:0018541C
ROM:0018541C loc_18541C:                  
ROM:0018541C     mov     r0, 0
ROM:0018541E
ROM:0018541E loc_18541E:                  
ROM:0018541E                              
ROM:0018541E     mov     sp, fp
ROM:00185422     ld.ab   fp, [sp,8+var_4]
ROM:00185426     b       __ac_pop_13_to_20
ROM:00185426 # End of function sub_18538E

我们可以清楚地看到检查0x52494852(或“RHIR”),所以它似乎r18指向标题的开头。经过一些颠倒的标题结构似乎是这样的:

struct ImageHeader
{
/* 00 */  uint32_t Signature;
/* 04 */  uint8_t unk4[2];
/* 06 */  uint8_t nblocks;
/* 07 */  uint8_t unk7[1];
/* 08 */  uint32_t platform;
/* 0C */  uint32_t unkC;
};

后跟一个块数组:

struct ImageBlock
{
/* 00 */    uint16_t type;
/* 02 */    uint8_t pad2[2];
/* 04 */    uint16_t start;
/* 06 */    uint16_t size;
/* 08 */    uint32_t unk8;
/* 0C */    uint32_t checksum;
};

代码正在检查类型==1 的块,它使用它start<<9作为从头开始的偏移量和size<<9块的大小来计算校验和并与checksum字段进行比较

我制作了一个快速脚本来解析和打印标题,这是输出:

unk4: 00001001
blocks: 7
platform: 00050600
unkC: 01A40001
block 0 type: 1
  offset: 1 (200)
  size: 14dc (29b800)
  unk8: 00048000
  checksum: A1AA045A
block 1 type: 1
  offset: 14dd (29ba00)
  size: 731 (e6200)
  unk8: 002E8000
  checksum: 917C608A
block 2 type: 0
  offset: 1c0e (381c00)
  size: 54c (a9800)
  unk8: 00430000
  checksum: EA1137CD
block 3 type: 1
  offset: 215a (42b400)
  size: 21 (4200)
  unk8: 004DA000
  checksum: 8AE264F9
block 4 type: 0
  offset: 217b (42f600)
  size: 2a6 (54c00)
  unk8: 004DA000
  checksum: 8606D33F
block 5 type: 0
  offset: 2422 (484400)
  size: 34 (6800)
  unk8: 004DA000
  checksum: 802A3C3B
block 6 type: 2
  offset: b420 (1684000)
  size: 4 (800)
  unk8: 00000000
  checksum: 00000000

所以看起来我们有这些块:

# type offset   size  unk8
------------------------------
0 1    200     29b800 00048000
1 1    29ba00   e6200 002E8000
2 0    381c00   a9800 00430000
3 1    42b400    4200 004DA000
4 0    42f600   54c00 004DA000
5 0    484400    6800 004DA000
6 2    1684000    800 00000000

unk8 看起来很像一个内存地址,事实上,如果我们检查文件中 30DAF3 处的字符串“LANGUAGE ZONE : ALL”,这对应于块 #1,并且与我们的开始(720F3)的偏移量相同来自假设内存地址 002E8000 的数据库 (0035A0F3) 中的字符串。根据表拆分加载的数据并将类型 1 的块移动到正确的地址后,我得到了这个布局:

CHUNK0  00048000 002E3800 
CHUNK1  002E8000 003CE200 
CHUNK3  004DA000 004DE200