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 (http://support.linksys.com/en-us/support/routers/WRT54G) 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” . 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 . 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 https://code.google.com/p/firmware-mod-kit/, 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/build-firmware.sh 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 Quitting...
–[ 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
-  http://www.devttys0.com/
-  http://www.devttys0.com/2011/05/reverse-engineering-firmware-linksys-wag120n/