Reverse Engineering Firmware

This post is a quick intro about the first concepts of reversing firmware. It is not a methodology that can be used to reverse any firmware due to the nature and the variety of devices and firmwares available, however the steps and the tools used in this post are a good starting point for who wants to learn more about this matter. In this article I will be reversing a few router firmware.

Reversing firmware can be fun but also very frustrating. There are firmware that can be easily reversed just by using the right tools at the right time, others in which you need to read lines of assembly code, some may come obfuscated and requires extra effort before being reversed.

The model number of the router is WRT54G v1.1 and here ( you can find the URL to the latest version of the firmware for this model.

The first tool that we are going to use is “binwalk” [1]. Binwalk scans the binary image for file signatures and outputs a table showing at which byte the file starts and with a description of the file’s type. Binwalk recognises common filesystems and common compression files. From my experience I can say that it works very well with routers.An example of binwalk running against our lovely router’s firmware image is the following:

$ binwalk FW_WRT54Gv4_4.21.5.000_20120220.bin
DECIMAL         HEX             DESCRIPTION
32              0x20            TRX firmware header, little endian, header size: 28 bytes,  image size: 3362816 bytes, CRC32: 0xE3ABE901 flags/version: 0x10000
60              0x3C            gzip compressed data, was "piggy", from Unix, last modified: Wed Feb  8 14:40:02 2012, max compression
700660          0xAB0F4         Squashfs filesystem, little endian, version 2.0, size: 2654572 bytes, 502 inodes, blocksize: 65536 bytes, created: Wed Feb  8 14:43:28 2012

At the top, the TRX header. TRX is the format of the kernel image file used in some routers like linksys and open source firmware such as OpenWRT and DD-WRT. At position 0x3C, binwalk indentified a gzip file (was it “piggy”?), this is the gizipped kernel image. In the last line of the output, we have a Squashfs filesystem, little endian and of version 2. Our goal is to extract this file out of the image.

If Binwalk won’t return a legit output, for instance the output doesn’t show any filesystem file, a manual analysis of the binary has to be done. One of the reason why binwalk won’t recognise the filesystem could be because this is not known by binwalk or because the compression of the file has affected the filesystem’s signature [2]. To manually analyse the file we need to read the content of the binary. We can start searching for strings:

$ strings -8 FW_WRT54Gv4_4.21.5.000_20120220.bin > strings.txt

or you can use binwalk with the “S” option:

$ binwalk -S FW_WRT54Gv4_4.21.5.000_20120220.bin

Open the output file in a text editor and verify if it contains any strings that could possibly be related to a filesystem, such as squashfs, cramfs, JFFS2, etc. Don’t panic if the output is awful and you didn’t find a thing. It is possible that the filesystem is compressed and that strings contained in it are compressed as well. If you’ve found the name of the filesystem, keep note of it. The next step is to analyse the binary in hexadecimal format. More specifically, we want to look for file’s signature in the hexadecimal output so to identify the filesystem. If you know already the filesystem type, you want to look for its filesystem signature. The hexdump tool:

$ hexdump -C FW_WRT54Gv4_4.21.5.000_20120220.bin > hexdump.txt

Open the output, and look for “sqsh” (Squashfs), ROMFS etc..

Different sections inside of firmware images are often aligned to a certain size. This often means that there will have to be some padding between sections, as the size of each section will almost certainly not fall exactly on this alignment boundary.An easy way to find these padded sections is to search for lines in our hexdump output that start with an asterisk (‘*’). When hexdump sees the same bytes repeated many times, it simply replaces those bytes with an asterisk to indicate that the last line was repeated many times. A good place to start looking for a file system inside a firmware image is immediately after these padded sections of data, as the start of the file system will likely need to fall on one of these aligned boundaries.

The signature will likely corresponds to the first byte of the filesystem. Compare the output with the hexdump of a filesystem file that you know is of that type, and analyse if the padding spaces are similar. Try extracting the filesystem and verify if your guess was right.

To extract a file from the binary image we can use the dd tool. The syntax of the folowing command is self-explanatory. We want dd to skip to the 700660 byte, read blocks of 1 byte and save the data into a file called “filesystem”. We can perform the same operation for the other files in the image using the same technique and adding the “count” option to dd (though “count” is not strictly necessary for dd to recognise the end of the file, it is recommended the use of it to avoid further headeaches).

$ dd if=FW_WRT54Gv4_4.21.5.000_20120220.bin of=filesystem skip=700660 bs=1
2663180+0 records in
2663180+0 records out
2663180 bytes (2.7 MB) copied, 93.9875 s, 28.3 kB/s

$ dd if=FW_WRT54Gv4_4.21.5.000_20120220.bin of=kernel skip=60 bs=1 count=700600

The filesystem is extracted, let’s check if “file” agrees with the filesystem’s type.

$ file filesystem
filesystem: Squashfs filesystem, little endian, version 2.0, 2654572 bytes, 502 inodes, blocksize: 65536 bytes, created: Wed Feb  8 14:43:28 2012

Ohh yea. Now I could mount this filesystem, but it failed with the following error:

$ sudo mount -t squashfs -o loop filesystem /mnt/
mount: wrong fs type, bad option, bad superblock on /dev/loop0,
       missing codepage or helper program, or other error
       In some cases useful info is found in syslog - try
       dmesg | tail  or so

So, I proceeded unsquash-ing the Squashfs filesystem. There are nice tools available for linux for squashfs and also for cramfs. The package is called squash-tools and it includes utilities to make Squashfs and to unsquash Squashfs.

$ unsquashfs filesystem 
Parallel unsquashfs: Using 8 processors
462 inodes (544 blocks) to write    [==============================================================================================================================================|] 544/544 100%
created 385 files
created 40 directories
created 77 symlinks
created 0 devices
created 0 fifos

The filesystem is in the squashfs-root directory.

$ ll
-rw-rw-r--  1 nessuno nessuno  2663180 Apr 23 12:14 filesystem
-rw-rw-r--  1 nessuno nessuno  3363840 Apr 23 12:04 FW_WRT54Gv4_4.21.5.000_20120220.bin
-rw-rw-r--  1 nessuno nessuno 16566552 Apr 23 12:09 hexdump.txt
-rw-rw-r--  1 nessuno nessuno  1630208 Apr 23 12:23 pigg
drwxr-xr-x 12 nessuno nessuno     4096 Feb  8  2012 squashfs-root/
-rw-rw-r--  1 nessuno nessuno     1006 Apr 23 12:08 string.txt

$ ls squashfs-root/
bin  dev  etc  lib  mnt  proc  sbin  tmp  usr  var  www

What to do from here? Evil things indeed! While I was looking how to backdoor the router, I found that the “firmware-mod-kit”, available from, is useful to unpack and re-pack images. The kit includes, binwalk, unsquashfs utilities, howere these are not updated. What I found useful from the kit are the “extract-firmware” and “build-firmware” utilities. The first does the same job explained above but also keep track of the size of the original image and create status files that are going to be used with the “build-firmware”.

The following is an example of an ignorant in reversing firmware trying to rebuild the image as if it was butter and marmelade on a toast.

$ ~/dir/tools/firmware-mod-kit/ fmk/
Firmware Mod Kit (build-ng) 0.83, (c)2011-2013 Craig Heffner, Jeremy Collake

Building new squashfs file system...
Creating little endian 2.1 filesystem on /home/nessuno/dir/lab/reversingFirmware/rebuild_firmware/fmk/new-filesystem.squashfs, block size 65536.

Little endian filesystem, data block size 65536, compressed data, compressed metadata, compressed fragments
Filesystem size 2815.43 Kbytes (2.75 Mbytes)
    27.42% of uncompressed filesystem size (10268.18 Kbytes)
Inode table size 3893 bytes (3.80 Kbytes)
    34.13% of uncompressed inode table size (11406 bytes)
Directory table size 3333 bytes (3.25 Kbytes)
    51.87% of uncompressed directory table size (6426 bytes)
Number of duplicate files found 7
Number of inodes 504
Number of files 387
Number of fragments 69
Number of symbolic links  77
Number of device nodes 0
Number of fifo nodes 0
Number of socket nodes 0
Number of directories 40
Number of uids 1
    root (0)
Number of gids 0
ERROR: New firmware image will be larger than original image!
       Building firmware images larger than the original can brick your device!
       Try re-running with the -min option, or remove any unnecessary files from the file system.
       Refusing to create new firmware image.

       Original file size: 3363840
       Current file size:  3584244


–[ Belkin Case (Cramfs)

I run binwalk against the bin file, and as always i extracted the filesystem using the dd tool, but this time I had a different filesystem type:

$ file filesystem 
filesystem: Linux Compressed ROM File System data, big endian size 2277376 version #2 sorted_dirs CRC 0x6f9b5994, edition 0, 1759 blocks, 389 files

As you can see we have Cramfs big endian filesystem. Big endian? No, big endian no! no endianness except little on my system, right! no! no! no! OK, calm down, use cramfsswap.

$ cramfsswap filesystem little_filesystem

Now we can mount the filesystem as loopback device just by executing the following command:

$ mount -o loop -t cramfs little_filesystem /mnt




Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s