Extracting Non-Standard SquashFS Images – /dev/ttyS0

SquashFS is a widely used file system in embedded Linux devices; in fact, it is probably one of the most commonly used file systems among Linux based consumer products.

While many devices use standard SquashFS file systems that can be extracted using the unsquashfs tools provided in the firmware mod kit, you will inevitably encounter some implementations that are…special.

Perhaps the vendor simply changed the SquashFS signature, or maybe they modified something in the de/compression code, but for one reason or another, none of the unsquashfs tools will work.

The D-Link DIR-320 is special.

Upon first inspection of the DIR-320 firmware image, it appears that we have a normal, standard SquashFS image inside the firmware update file:

embedded@ubuntu:~/DIR320$ binwalk DIR-320A1_FW121WWb03.bin 

DECIMAL   	HEX       	DESCRIPTION
-------------------------------------------------------------------------------------------------------
96        	0x60      	LZMA compressed data, dictionary size: 8388608 bytes, uncompressed size: 2240512 bytes
720992    	0xB0060   	PackImg Tag, size: 3157248 bytes
721024    	0xB0080   	Squashfs filesystem, little endian, version 2.0, 2960636 bytes, 984 inodes, blocksize: 65536 bytes, created: Tue Nov 23 01:18:41 2010

But alas, running unsquashfs against the extracted SquashFS image results in a segmentation fault:

embedded@ubuntu:~/DIR320$ dd if=DIR-320A1_FW121WWb03.bin bs=1 skip=721024 count=2960636 of=dir320.squashfs
139136+0 records in
139136+0 records out
139136 bytes (139 kB) copied, 0.407625 s, 341 kB/s
embedded@ubuntu:~/DIR320$ /opt/filesystems/squashfs/unsquashfs-2.1-r2-lzma dir320.squashfs 
Segmentation fault

Since the parsed header information from binwalk looked legitimate, let’s take a quick look at a hexdump from the SquashFS image:

00000000  68 73 71 73 d8 03 00 00  fc 2c 2d 00 f4 2c 2d 00  |hsqs.....,-..,-.|
00000010  f8 2c 2d 00 4e f5 2c 00  e6 0d 2d 00 02 00 00 00  |.,-.N.,...-.....|
00000020  b1 c6 10 00 40 01 01 f1  86 eb 4c 76 19 77 10 00  |[email protected]..|
00000030  00 00 00 00 00 01 00 2f  00 00 00 f0 2c 2d 00 37  |......./....,-.7|
00000040  7a 69 70 00 1e 1d 0a 43  df 5a 7f 1e 0d a3 a0 de  |zip....C.Z......|
00000050  e8 9c 48 47 c7 41 74 56  1d 95 b2 45 21 9b 24 5d  |..HG.AtV...E!.$]|
00000060  44 8c d4 aa d9 3d 19 82  a5 72 62 a4 ec 7a 5d f2  |D....=...rb..z].|
00000070  b1 8b ab 99 c2 ab cc ca  f3 7d fb f2 4f bb a7 a2  |.........}..O...|
00000080  9e 0b 14 7f 27 f4 2f 98  60 95 10 cd c5 d2 d7 31  |....'./.`......1|
00000090  79 dd 2e 06 07 93 34 1f  ef 31 da cd c2 8f db 35  |y.....4..1.....5|

One thing that you’ll notice is that the string ‘7zip’ keeps popping up all over the file system image. Although 7zip is associated with LZMA compression (suggesting that the SquashFS data blocks are in fact LZMA compressed), the 7zip string is certainly not a standard part of LZMA compression.

In times of trouble, source code is your best friend. Although not widely advertised on D-Link’s sites, source for the DIR-320 is available (warning: slow download).

First, let’s locate the SquashFS utilities in the DIR-320 GPL release:

embedded@ubuntu:~/DIR320/dir320$ find . -name *squashfs*
./kernels/bcm5354/include/linux/squashfs_fs_sb.h
./kernels/bcm5354/include/linux/squashfs_fs.h
./kernels/bcm5354/include/linux/squashfs_fs_i.h
./kernels/bcm5354/fs/squashfs
./tools/squashfs-tools-3.0
./tools/squashfs-tools-3.0/mksquashfs.c
./tools/squashfs-tools-3.0/mksquashfs.h
./tools/squashfs-tools-3.0/squashfs_fs.h
./tools/squashfs-tools-3.0/unsquashfs.c
./tools/squashfs-tools
./tools/squashfs-tools/squashfs_fs_sb.h
./tools/squashfs-tools/mksquashfs.c
./tools/squashfs-tools/mksquashfs.h
./tools/squashfs-tools/squashfs_fs.h
./tools/squashfs-tools/squashfs_fs_i.h

Let’s look through the SquashFS tools included with the GPL code for references to the ‘7zip’ string we saw in our hex dump earlier:

embedded@ubuntu:~/DIR320/dir320/tools/squashfs-tools$ grep -R 7zip *
lzma/SRC/7zip/Compress/LZMA_Alone/AloneLZMA.dsp:# Begin Group "7zip Common"
lzma/SRC/7zip/Compress/LZMA_Lib/ZLib.cpp:    strcpy((char*)dest,"7zip");
lzma/SRC/7zip/Compress/LZMA_Lib/ZLib.cpp://+++ I add "7zip" id to make kernel can check if use 7zip to decompress. ---//
lzma/SRC/7zip/Compress/LZMA_Lib/ZLib.cpp:    if ( strncmp((char*)source,"7zip",4) == 0 )
lzma/lzma.txt:SRC/7zip/Compress/LZMA_Alone 
lzma/lzma.txt:  7zip    - files related to 7-Zip Project
lzma/lzma.txt:  SRC/7zip/Compress/LZMA_C
lzma/lzma.txt:You can find C/C++ source code of such filters in folder "7zip/Compress/Branch"
Makefile:LZMAPATH = ./lzma/SRC/7zip/Compress/LZMA_Lib
Makefile:	make -C ./lzma/SRC/7zip/Compress/LZMA_Lib
Makefile:	make -C ./lzma/SRC/7zip/Compress/LZMA_Alone
Makefile:	cp -f ./lzma/SRC/7zip/Compress/LZMA_Alone/lzma ./lzma
Makefile:	make -C ./lzma/SRC/7zip/Compress/LZMA_Lib clean
Makefile:	make -C ./lzma/SRC/7zip/Compress/LZMA_Alone clean

There is a block of code in the ZLib.cpp file that does a string comparison against the ‘7zip’ string and includes a comment about using ‘7zip’ to decompress data.

Looking at the DIR-320’s ZLib.cpp file, it appears that the LZMA library has been modified to look for a ‘7zip’ string at the beginning of a compressed data block, and if it exists to simply skip over the ‘7zip’ string:

//+++ add by siyou ---//
//+++ I add "7zip" id to make kernel can check if use 7zip to decompress. ---//
ZEXTERN int ZEXPORT uncompress OF((Bytef *dest,   uLongf *destLen,
                                   const Bytef *source, uLong sourceLen))
{

    if ( strncmp((char*)source,"7zip",4) == 0 )
    {
       source += 4;
       sourceLen -= 4;
    }
    orig_uncompress(dest,destLen,source,sourceLen);
    return Z_OK;
}

This looks to be our culprit and is a relatively simple modification. We’ll add this code to the LZMA library included with the firmware mod kit and re-build the SquashFS tools (a patch file has been submitted to the FMK project and can be downloaded here):

embedded@ubuntu:~/firmware-mod-kit/trunk/src$ patch -p1 < lzma7zip.patch
patching file src/lzma/C/7zip/Compress/LZMA_Lib/ZLib.cpp
embedded@ubuntu:~/firmware-mod-kit/trunk/src$ make clean && make
...
wrt54gv5_img.cpp: In function ‘int FixImage(const char*)’:
wrt54gv5_img.cpp:653: warning: format ‘%08X’ expects type ‘unsigned int’, but argument 2 has type ‘long unsigned int’
wrt54gv5_img.cpp:658: warning: format ‘%08X’ expects type ‘unsigned int’, but argument 2 has type ‘long unsigned int’
make[1]: Leaving directory `/media/Storage/MORE_BACKUPS/Projects/firmware-mod-kit-orig/trunk/src/wrt_vx_imgtool'

Let’s try extracting the files in the SquashFS image again, this time with our patched binaries:

embedded@ubuntu:~/DIR320$ ../firmware-mod-kit/trunk/src/squashfs-2.1-r2/unsquashfs-lzma dir320.squashfs 

created 825 files
created 56 directories
created 103 symlinks
created 0 devices
created 0 fifos
embedded@ubuntu:~/DIR320$ ls squashfs-root/
bin  dev  etc  home  htdocs  lib  mnt  proc  sbin  sys  tmp  usr  var  www

Success! Not terribly complicated, but necessary, and now we are free to further examine the binaries and PHP files used by the DIR-320.

Bookmark the permalink.

Comments are closed.