4

Imagine this:

You have a binary for a piece of ARM firmware. You are nearly 100% it is ARM, and that it runs on the bare metal. You obtained this firmware from a manufacturers update page.

However, you are uncertain of the exact model of chip the binary is intended for. You are unable to find the developer's guide or spec sheet for the chip.

The binary has no known headers, and research suggests that it is not compressed or encrypted. The large section of strings at the bottom of the binary suggests it is a single flat binary file, and not several records compressed together. There are no indications of a file system.

You should reasonably be able to disassemble the code but a few factors are missing to prevent you from retrieving control-flow, and creating a sensical disassembly.

1) You do not know where the initial entry point is. 2) You do not know if there is a ram section, and what address it might start and end at. 3) You do not know if there is a rom section, and what address it might start and end at.

Given these, or similar circumstances, how might a reverse engineer deduce the initial entry point, and location/size of areas like ROM?

I imagine detecting reads and writes from a memory mapped chip of some sort would be possible to infer from a valid disassembly, just by highlighting common memory regions which are frequently referenced, and classifying them into sections. I am hoping someone has come up with an automated method for this sort of analysis.

E.G

"The range 0x7-0x9 is frequently referenced. It may be ROM. The highest address is 0x7998, the area appears to occupy 8 Mbs."

The other area, identifying the entry point, has stumped me so far. Without the documentation for the chip is it possible to infer the initial entry point of the code? This is especially frustrating on chips where the bootloader appears to be stored in ROM separate from the main firmware.

Can the structure of such a firmware image be implied without developer documentation?

2 Answers 2

7

What I usually do:

  • Load the binary at a not too small base address, like 0x10000000.
  • Identify as many functions and strings as possible.
    • you may get lucky starting with only the strings, that is usually less work.
  • create a list of all constant values, immediates, and dword (assuming a 32 bit binary) values.
  • now sort the list of function and string addresses, and calculate the differences between each consecutive address.
  • do the same for the list of constant values.

Now you have two lists of address differences, if you find a sequence of consecutive differences which is in both lists, you have found your base address.

This works most of the time, but you can run in to the problem that both lists may be incomplete. For instance the address list will not have absolute pointers for each function, or maybe you disassembled some functions incorrectly. Maybe you will have better luck focussing on the string addresses.

I usually do this list matching by hand, in vim using regex searches. On some occasions I have written small scripts to help finding a match. ... but i can't find those right now, i will update my post if i find them again.


summary of the chat

The firmware file being discussed: DVDO Matrix6 Firmware 01.01 from dvdo.

I took a look at other binaries from the same site, and found references to the LPC1758 - an ARM based chip.

Indeed IDA does not immediately recognize the binary. The reason is that this binary has only Thumb instructions. IDA expects arm binaries to start with ARM32 code. Thumb code can be recognised from a hex dump by the presence of byte sequences like 70 47 (BX LR), 00 bf (NOP), *0 b5 (PUSH {...}

So after changing the segment type T to 1 using Alt-G. I could disassemble the file.

Finding the offset:

These two commands will generate a list of dwords occurring in the file, and a list of strings occurring in the file:

od -Ax -t x4 Matrix6_Version_01/M6FW0101.BIN | perl -pe 's/^\w+\s+//' | tr " " "\n" | sort|uniq  > dwordlist.txt
strings -10 -o -t x "Matrix6_Version_01/M6FW0101.BIN" > stringlist.txt

Now look at the first real text in the stringlist:

28eaa pGSAC Initiation task finished
28eca SAC Audio Format Discovery task finished
28ef4 SAC volume has changed
28f0c Audio System Logical Address not assigned
28f37 CBUS MUTE received
28f4b CBUS UN-MUTE received
28f62 CBUS VOL UP received
28f78 CBUS VOL DOWN received

You may notice that the first 2 characters of the first string, pG is actually a 70 47 or BX LR instruction.

Now i would load both files in Vim, and in both run this vim-perl script:

:perldo s/^\w+/($x,$p)=(hex($&),$x); sprintf("%s(%8x)", $&, $x-$p)/e

This will lead to a string list looking partially like this:

28eaa(    25dd) pGSAC Initiation task finished
28eca(      20) SAC Audio Format Discovery task finished
28ef4(      2a) SAC volume has changed
28f0c(      18) Audio System Logical Address not assigned
28f37(      2b) CBUS MUTE received
28f4b(      14) CBUS UN-MUTE received
28f62(      17) CBUS VOL UP received
28f78(      16) CBUS VOL DOWN received

now, skipping the first two, because of the incorrect pG start, I search in dwordlist.txt, for consecutive lines with respectively 2a, 18 and 2b, using this regex search:

/ 2a)\n.* 18)\n.* 2b)

This leads me to the following lines matching in both files:

0002ebc3(      80)
0002eeac(     2e9)       28eaa(    25dd) pGSAC Initiation task finished
0002eeca(      1e)       28eca(      20) SAC Audio Format Discovery task finished
0002eef4(      2a)       28ef4(      2a) SAC volume has changed
0002ef0c(      18)       28f0c(      18) Audio System Logical Address not assigned
0002ef37(      2b)       28f37(      2b) CBUS MUTE received
0002ef4b(      14)       28f4b(      14) CBUS UN-MUTE received
0002ef62(      17)       28f62(      17) CBUS VOL UP received
0002ef78(      16)       28f78(      16) CBUS VOL DOWN received
0002ef8c(      14)

Subtracting 0x28eaa from 0x2eeac leads me to an offset of 0x6000.

9
  • I assume you were working off a linear dissassembler like objdump for the initial disassembly? My worry is discerning a legitimate disassembly from one that is somehow corrupt. Oct 24, 2017 at 21:49
  • I would use IDA for both the initial and final disassembly. since that automates much of the function finding. Maybe aided with some idapython scripts to coerce IDA to explore more unreached code. Oct 25, 2017 at 6:33
  • Hmm yes but in a lot of these situations IDA doesn't find the functions in the first place. Hence all the worry about finding the right addresses for the import. In a typical binary like this (for me) IDA will find 2-5 functions and they will be wrong. Oct 25, 2017 at 8:55
  • Maybe you are looking at an encrypted firmware image? can you post a sample? Oct 25, 2017 at 10:22
  • 1
    I think this method would work for any CPU which stores absolute offsets to strings in a binary. Oct 31, 2017 at 15:41
3

I cover some of this in my Recon 2010 presentation (starting around page 48).

To summarize the approaches:

  • self-relocating code which copies itself to expected location

  • initialization code which contains references to expected run-time addresses

  • jump tables with absolute addresses

  • string tables (tables of addresses) matched against actual strings in the binary.

  • symbol tables (if you're lucky)

5
  • I don't suppose any automation exists in this direction? It sounds like not. Oct 24, 2017 at 7:49
  • 1
    I'm not reversing hundreds of binaries each day so I've been doing it mostly manually so far....
    – Igor Skochinsky
    Oct 24, 2017 at 9:26
  • would maddie stone's IDAPython Embedded Toolkit be useful for this kind of task?
    – julian
    Oct 24, 2017 at 12:25
  • @SYS_VL I think her scripts mostly deal with already correctly loaded code, but maybe she has something for this too, I'm not sure... FWIW string table (or function table) search could certainly be automated
    – Igor Skochinsky
    Oct 24, 2017 at 13:49
  • Thanks, good to know. That doesn't really sound like "life-changing magic" but I haven't tried using it yet
    – julian
    Oct 24, 2017 at 16:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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