Disassemble Nav FX firmware

Ford FX, NX and MCA headunits
Post Reply
Go4IT
Posts: 300
Joined: 08 Feb 2019, 12:25

Disassemble Nav FX firmware

Post by Go4IT » 23 Jul 2019, 20:37

Hey guys! I recently loaded one of my FX satnav firmware dumps of the mainboard into IDA-Pro 7. I used these settings:
ARM Little Endian
Processor Kernel Option: ARMv5TEJ
Load as ROM from 0x00000000 onwards.
Convert first bytes to code and go...

The first instruction is a call ("B" = Branch) to memory location 0x0000 0D1C:

Code: Select all

ROM:00000000                 B       loc_D1C         ; DATA XREF: sub_17C42A:loc_17C562↓r
ROM:00000000                                         ; sub_17C42A+140↓w ...
where we find this:

Code: Select all

ROM:00000D1C ; ---------------------------------------------------------------------------
ROM:00000D1C                 CODE32
ROM:00000D1C
ROM:00000D1C loc_D1C                                 ; CODE XREF: ROM:00000000↑j
ROM:00000D1C                 MOV     R0, #0xD3  ; set CPSR to "1101 0011" which will enable the "Supervisor mode" of the ARM9 CPU
ROM:00000D20                 MSR     CPSR_c, R0
ROM:00000D24                 LDR     R0, =0xFFFED400
ROM:00000D28                 LDR     R1, [R0]
ROM:00000D2C                 LDR     R2, =0x3320500
ROM:00000D30                 CMP     R1, R2
ROM:00000D34                 BNE     loc_16E0
ROM:00000D38                 LDR     R0, =0xFFFECE00
ROM:00000D3C                 MOV     R1, #1
ROM:00000D40                 STR     R1, [R0,#0x14]
ROM:00000D44                 LDR     R2, =0xFFFE1804
ROM:00000D48                 LDR     R3, [R2]
ROM:00000D4C                 MOV     R2, #0xE0000
ROM:00000D50                 ANDS    R2, R3, R2
ROM:00000D54                 BNE     loc_DB0
ROM:00000D58                 LDR     R0, =0xFFFE1000
ROM:00000D5C                 LDR     R1, [R0,#0x44]
ROM:00000D60                 LDR     R2, =0x2000FFF0
ROM:00000D64                 STR     R1, [R2]
ROM:00000D68                 MOV     R4, #1
ROM:00000D6C                 STR     R4, [R0,#0x44]
ROM:00000D70                 AND     R1, R1, #1
ROM:00000D74                 CMP     R1, #1
ROM:00000D78                 BNE     loc_DC8
ROM:00000D7C                 LDR     R1, =0xDBDBDBDB
ROM:00000D80                 LDR     R2, =0x2000FFFC
ROM:00000D84                 LDR     R3, [R2]
ROM:00000D88                 CMP     R1, R3
ROM:00000D8C                 BNE     loc_16E4
ROM:00000D90                 LDR     R1, =0x17051973
ROM:00000D94                 LDR     R2, =0x2000FFEC
ROM:00000D98                 LDR     R3, [R2]
ROM:00000D9C                 CMP     R1, R3
ROM:00000DA0                 BNE     loc_16E4
ROM:00000DA4                 LDR     R2, =0x2000FFF4
ROM:00000DA8                 LDR     R1, [R2]
ROM:00000DAC                 B       loc_DEC
ROM:00000DB0 ; ---------------------------------------------------------------------------
Well, whatever this means ;-) I used to do Assembler on Z80 but this is decades ago.

Code: Select all

ROM:00000D1C                 MOV     R0, #0b11010011
ROM:00000D20                 MSR     CPSR_c, R0
Loads the general puropse processor register R0 with the value 0xD3 (0b1101 0011) and this into the CPSR register. "CPSR_c" is a special instruction which only loads the lower 8 control bits into the register.
Bits 0-4 are the current processor mode to use, which is 0b10011 (0x13) and means "SVC (supervisor)".
Bit 5 gives Thumb-Mode if set. Here it is not, so the CPU is not using Thumb, which means every instruction is 4 bytes long.
Bit 6 is FIQ disable which is the case for us. So all Fast-IRQs are disabled and would not be fired during initialization.
Bit 7 is the same for normal IRQ.
So loading "0xD3" means something like "bring the CPU into supervisor mode", where it can access everything.

The next instruction will load the memory address 0xFFFED400 into register R0:

Code: Select all

ROM:00000D24                 LDR     R0, =0xFFFED400
As from the OMAP5912 datasheet this ist the "OMAP32_ID Register" which should contain the processor type and revision, a 32 bit value. This is loaded by the next instruction into register R1:

Code: Select all

ROM:00000D28                 LDR     R1, [R0]
And now comes the funny part, it is compared with the value loaded into R2 (0x3320500)

Code: Select all

ROM:00000D2C                 LDR     R2, =0x3320500
ROM:00000D30                 CMP     R1, R2
and if they don't match (BNE = Branch If Not Equal) the execution is forwarded to address 0x16E0:

Code: Select all

ROM:00000D34                 BNE     loc_16E0
In the datasheet of the OMAP, the OMAP32_ID of 0x03320500 means "POMAP5912" or "OMAP5912B", revision 2.2. So this instruction is to check on which processor the software is running! :-)
You do not have the required permissions to view the files attached to this post.

Go4IT
Posts: 300
Joined: 08 Feb 2019, 12:25

Re: Disassemble Nav FX firmware

Post by Go4IT » 24 Jul 2019, 03:51

After decompiling and converting to C++ (F4 or F5 in IDA), i expect to find an algo subroutine somewhere (there are hundrets of subs in the code, though) and it may look like this example:

Code: Select all

int __fastcall sub_3FC(int a1, int a2)
{
  return *(_DWORD *)(40 * *(unsigned __int16 *)(a1 + 50) + *(_DWORD *)(a1 + 32) + a1 + 16)
       + a1
       + (*(_DWORD *)(40 * a2 + *(_DWORD *)(a1 + 32) + a1) & 0xFFFF);
}

Go4IT
Posts: 300
Joined: 08 Feb 2019, 12:25

Re: Disassemble Nav FX firmware

Post by Go4IT » 24 Jul 2019, 03:53

And what could be the purpose of such function? Looks like init something:

Code: Select all

signed int sub_A10B4()
{
  signed int result; // r0

  if ( (MEMORY[0xFFFECE10] & 2) != 2 )
  {
    MEMORY[0xFFFECE10] = 4;
    MEMORY[0xFFFECE08] |= 0x28u;
    MEMORY[0xFFFEC918] = 5;
    MEMORY[0xE0010000] = 538976288;
    MEMORY[0xE0010004] = 1983120520;
    MEMORY[0xE0010008] = 1979774360;
    MEMORY[0xE001000C] = -922641888;
    MEMORY[0xE0010010] = 538976288;
    MEMORY[0xE0010014] = 1979752600;
    MEMORY[0xE0010018] = -922641888;
    MEMORY[0xE001001C] = 538976288;
    MEMORY[0xE0010020] = -332284415;
    MEMORY[0xE0010024] = 1111905;
    MEMORY[0xE0010028] = 84602;
    MEMORY[0xE001002C] = 538976288;
    MEMORY[0xFFFECE10] = 6;
    MEMORY[0xE1008008] = 38;
    MEMORY[0xE1008014] = 1;
    MEMORY[0x20017FFC] |= 0x100000u;
  }
  result = -79616;
  MEMORY[0xFFFEC900] = 2162475;
  return result;
}

signed int sub_A1200()
{
  signed int result; // r0

  result = -78848;
  MEMORY[0xFFFECC0C] |= 2u;
  MEMORY[0xFFFECC10] = 4369;
  MEMORY[0xFFFECC50] = 1;
  MEMORY[0xFFFECC14] = 0xFFFF;
  MEMORY[0xFFFECC18] = 8747;
  MEMORY[0xFFFECC1C] = -1987964863;
  return result;
}
Those static addresses beginning with 0xFFFE.... seem to be mapped addresses of internal devices of the OMAP. For example "0xFFFECE08" in the code line "MEMORY[0xFFFECE08] |= 0x28u;" addresses the ARM peripheral clock (DPLL). So the first thing i would do is to give those addresses names inside of IDA to get a better understanding of what's going on.

0xFFFECE00 is the base address of the MPU registers.
0xFFFE1804 is the "OMAP Die ID register (OMAP_DIE_ID_1)", which holds a 32-Bit value of the Die of the processor.
0xFFFE1000 is the Perihpals control and offset #44 denotes the "Pulldown control 1 (PULL_DWN_CTRL_1) bits.

So for now i guess this is the memory map we are faced in the FX:

0x0000 0000 - 0x01FF FFFF (32 MB) = Flash
0x2000 0000 - 0x2000 FFFF = internal SRAM of OMAP
0x8000 0000 to 0xEFFF FFFF = external SDRAM
0xFFFE 0000 - 0xFFFF FFFFF = Memory mapped registers

Go4IT
Posts: 300
Joined: 08 Feb 2019, 12:25

Re: Disassemble Nav FX firmware

Post by Go4IT » 25 Jul 2019, 08:31

The very first bytes of the image are the so called "Exception Vector Table" (aka "reset vectors"). An Exception ist thrown by the processor if a special condition is met, e.g. a RESET is issued, or an INTERRUPT. The table ranges from 0x0000 0000 - 0x0000 001C and each vector address is a ARM command and therefore 4 bytes long. The vector table looks like this:

0x0000 0000 = RESET Exception
0x0000 0004 = Undefined Instruction Exception
0x0000 0008 = Software Interrupt Exception
0x0000 000C = Prefetch Abort Exception
0x0000 0010 = Data Abort Exception
0x0000 0014 = -RESERVED-
0x0000 0018 = IRQ Exception
0x0000 001C = FIQ Exception

If we look at those vectors in the image we found that almost all of them lead to an infinite loop, e.g. the "Undefined Instruction Exception"

Code: Select all

ROM:00000004                 B       loc_20   ; Jump to address 0x00000020 (named as marker "loc_20" by IDA Pro)
...
ROM:00000020                 B       loc_20  ; Infinite loop
The only real target is found in the RESET vector. It jumps to 0x0000 0D1C, which i call the "INIT" procedure shown above:

Code: Select all

ROM:00000000                 B       loc_D1C
[code]

Post Reply