2

I am working on reversing a firmware update from a FujiFilm FinePix S1800 camera. So far I have managed to identify the instruction set (ARCompact) and dump a few things from the camera using the hidden service menu, one of which is called "BOOTCODE".

The firmware update is not encrypted but has a couple of headers that I need to strip off.

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..'|

The BOOTCODE.DMP starts out with the same instructions located at 0x3620-0x36A0, so this might be one of the entry points.

The problem is, I am not sure where the headers finish and the ROM starts. I have tried to brute force the header format and so far have come up with nothing. Short of obtaining a TSOP programmer I have no other method of which to obtain a clean ROM dump of the device.

In short, I am looking for help in two things:

  • Extracting the ROM from the update file
  • Determining the base for the ROM

Update (2019-11-16 18:28)

It seems the firmware dump of the boot code is prefixed by 16 bytes, the first four of which seem to denote the base address.

Update (2019-11-16 19:30)

Further analysis shows that the ROM may start at 0x200 as this de-compiles into the following with a guessed (but incorect) base address. Code that calls this address is also valid.

                 push    blink
                 bl.d    sub_F015C518
                 ld      r0, [unk_EFF0CCA4]
                 bl      sub_F01743E0
                 pop     blink
                 j       [blink]
3

The binary is not a single monolithic block. I figured out the structure but it took a few false starts.

First I loaded the Binary into IDA starting from offset 0x200 as you hinted. The functions were disassembled okay but the data references were mostly off. So I looked at the strings to see if there may be some candidates for a string table. This bunch looked promising:

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

Now, the proper way would be to write a script that would scan the database for a dword array with values having the same difference as string addresses. However, I was lazy so I though to myself, "The image base/shift value is likely a multiple of 0x100, so the last byte of the address will be the same, i.e. I need to look for a table like":

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

This can be easily done via Binary search with the pattern "F3 ? ? ? 07 ? ? ? 1A" (without quotes; '?' denotes a wildcard byte). There was only one hit:

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

So if the first string should be at 35A0F3, we need to rebase the binary by 0x35A0F3-0x30D8F3 = 0x4C800 bytes. This can be done via Edit-Segment-Rebase program..., Shift delta. Once done, our table is nicely displayed, and additional strings line up too:

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"

However, this makes the code start at 4C800 and there is no such value in the image header which made me suspicious it's not the full picture. So I decided to look for code which parses the headers. A good start for this is looking for magic values (like header signatures).

Looking for 52 48 49 52 ("RHIR") did not give any hits (neither did the byte-swapped version). However, I remembered that ARCompact uses a peculiar way of encoding 32-bit immediates (aka "long immediates"): they are stored in mixed endian form (high word followed by low word). Searching for 49 52 52 48, I got one hit and after backing up to find the function start I got the following:

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

We can clearly see the check against 0x52494852 (or "RHIR"), so it seems r18 points to the start of the header. After some reversing the header structure seems to be like this:

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;
};

followed by an array of blocks:

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;
};

the code is checking for blocks of type==1, for which it uses start<<9 as an offset from the header start and size<<9 as the size of the block to calculate the checksum and compare against checksum field.

I made a quick script to parse and print the header and here's the output:

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

So it seems we have these chunks :

# 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 looks very much like a memory address, and indeed if we check the string "LANGUAGE ZONE : ALL" which is at 30DAF3 in the file, this corresponds to chunk #1 and is at the same offset from its start (720F3) as our string in database(0035A0F3) from the hypothetical memory address 002E8000. After splitting the loaded data according to the table and moving chunks of type 1 to the correct addresses, I've got this layout:

CHUNK0  00048000 002E3800 
CHUNK1  002E8000 003CE200 
CHUNK3  004DA000 004DE200   
| improve this answer | |
  • 1
    You sir are incredible! Thank you! I spent many more hours on this after my last post and when I realized things were still not right I went to bed. – Geoffrey Nov 17 '19 at 2:43
  • There is an error in this uint8_t unk7[2]; should be uint8_t unk7[1]; – Geoffrey Nov 17 '19 at 6:10
  • @Geoffrey thanks, fixed – Igor Skochinsky Nov 17 '19 at 11:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.