Uncompressing Cisco X2000 firmware images

I got a refurbed Cisco X2000 [amzn] ADSL router / wireless access point for cheap from Adorama, hoping to combine 2 networking boxes, and reduce clutter and power a bit.  It comes in at 3-4W, and has an efficiency Level V wall wart, so that part’s all good.


Edit: Everything else is bad!  Don’t get this device.  Really.  It’s the buggiest piece of junk I’ve had the misfortune of trying to run on my network.  I’ll leave the rest of this post here for posterity.


Unfortunately it doesn’t seem to do SNMP and there’s no way to monitor traffic.  On a whim I figured I’d look into the firmware and see if it offers any hints.  Just a note to the interwebs about how I got it unpacked:

First I ran binwalk to see what’s in it; that’s a cool tool I didn’t know about!

# binwalk-0.4.5/src/binwalk -m binwalk-0.4.5/src/magic.binwalk FW_X2000_AnnexA_2.0.04.007_20121009.bin

DECIMAL       HEX           DESCRIPTION
-------------------------------------------------------------------------------------------------------
256           0x100         Squashfs filesystem, little endian, non-standard signature,  version 4.0, size: 1746252031 bytes, 1243 inodes, blocksize: 0 bytes, created: Wed Jul  9 20:36:00 2025
352           0x160         LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 65536 bytes
15110         0x3B06        LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 13944 bytes
...

Ok, so maybe squashfs starting 256 bytes in.  So dd that out:

# dd if=FW_X2000_AnnexA_2.0.04.007_20121009.bin bs=256 skip=1 of=FW.squash

But squashfs tools won’t touch it.  What’s in it?

# hexdump -C FW.squash  | head -n 8
00000000  73 68 73 71 db 04 00 00  53 c6 73 50 00 00 01 00  |shsq....S.sP....|
00000010  78 00 00 00 01 00 10 00  c0 00 01 00 04 00 00 00  |x...............|
00000020  3f 1c f1 1d 00 00 00 00  19 6f 68 00 00 00 00 00  |?........oh.....|
00000030  11 6f 68 00 00 00 00 00  ff ff ff ff ff ff ff ff  |.oh.............|
00000040  b0 15 68 00 00 00 00 00  11 3a 68 00 00 00 00 00  |..h......:h.....|
00000050  2c 65 68 00 00 00 00 00  fb 6e 68 00 00 00 00 00  |,eh......nh.....|
00000060  5d 00 00 01 00 00 00 01  00 00 00 00 00 00 3f 91  |].............?.|
00000070  45 84 68 34 8a 09 0a 40  62 ae 9d db 78 c5 ce 30  |E.h4...@b...x..0|
...

Ok, that’s the “shsq” magic…

I tried the various unsquashers from the firmware mod kit but those failed too.  In the end, I grabbed the squashfs-4.2 tools from sourceforge and applied this patch:

diff -Nurp squashfs4.2/squashfs-tools/Makefile squashfs4.2-hack/squashfs-tools/Makefile
--- squashfs4.2/squashfs-tools/Makefile	2012-11-30 12:26:45.958576463 -0600
+++ squashfs4.2-hack/squashfs-tools/Makefile	2012-11-30 12:24:21.969566639 -0600
@@ -26,7 +26,7 @@ GZIP_SUPPORT = 1
 # To build using XZ Utils liblzma - install the library and uncomment
 # the XZ_SUPPORT line below.
 #
-#XZ_SUPPORT = 1
+XZ_SUPPORT = 1

 ############ Building LZO support ##############
diff -Nurp squashfs4.2/squashfs-tools/squashfs_fs.h squashfs4.2-hack/squashfs-tools/squashfs_fs.h
--- squashfs4.2/squashfs-tools/squashfs_fs.h	2011-02-11 09:49:24.000000000 -0600
+++ squashfs4.2-hack/squashfs-tools/squashfs_fs.h	2012-11-30 12:24:49.310566558 -0600
@@ -270,10 +270,10 @@ struct meta_index {
 typedef long long		squashfs_block;
 typedef long long		squashfs_inode;

-#define ZLIB_COMPRESSION	1
-#define LZMA_COMPRESSION	2
-#define LZO_COMPRESSION		3
-#define XZ_COMPRESSION		4
+#define LZMA_COMPRESSION	1
+#define XZ_COMPRESSION		2
+#define ZLIB_COMPRESSION	3
+#define LZO_COMPRESSION		4

 struct squashfs_super_block {
 	unsigned int		s_magic;
diff -Nurp squashfs4.2/squashfs-tools/unsquashfs.c squashfs4.2-hack/squashfs-tools/unsquashfs.c
--- squashfs4.2/squashfs-tools/unsquashfs.c	2011-02-28 16:27:06.000000000 -0600
+++ squashfs4.2-hack/squashfs-tools/unsquashfs.c	2012-11-30 12:28:31.793690803 -0600
@@ -1460,10 +1460,10 @@ int read_super(char *source)
 	 */
 	read_fs_bytes(fd, SQUASHFS_START, sizeof(struct squashfs_super_block),
 		&sBlk_4);
-	swap = sBlk_4.s_magic != SQUASHFS_MAGIC;
+	swap = 0;
 	SQUASHFS_INSWAP_SUPER_BLOCK(&sBlk_4);

-	if(sBlk_4.s_magic == SQUASHFS_MAGIC && sBlk_4.s_major == 4 &&
+	if(!swap && sBlk_4.s_major == 4 &&
 			sBlk_4.s_minor == 0) {
 		s_ops.squashfs_opendir = squashfs_opendir_4;
 		s_ops.read_fragment = read_fragment_4;

And after that hackityhack, it does unpack. And what do you know, it does have busybox:

# ls -l bin | grep busybox
-rwxr-xr-x. 1 root root 323640 Oct  9 01:38 busybox
lrwxrwxrwx. 1 root root      7 Nov 30 12:25 cat -> busybox
lrwxrwxrwx. 1 root root      7 Nov 30 12:25 chmod -> busybox
lrwxrwxrwx. 1 root root      7 Nov 30 12:25 cp -> busybox
lrwxrwxrwx. 1 root root      7 Nov 30 12:25 date -> busybox
lrwxrwxrwx. 1 root root      7 Nov 30 12:25 dd -> busybox
...

And apparently runs the Linux kernel:

# ls lib/modules/
2.6.30

So, ok, Cisco/Linksys, where’s the GPL notifications etc?  I haven’t seen anything obvious, but their GPL download center doesn’t seem to have anything for this box.  Is it a GPL violation?  I sent a request through their normal process, we’ll see what happens.  :(

Sadly, no hints about SNMP in there, either.

Edit:  Although the “GPL code center” has no download for this box, I did obtain source via a request, and apparently there is a source offer buried deep on the setup CD.  I’ll see if the source builds, next, I guess.

13 thoughts on “Uncompressing Cisco X2000 firmware images

  1. Cisco is nasty as that. For work I needed an 802.1x capable switch, so I got a Cisco SG-200 – yet another LinkSys product that has been rebranded just like the X2000 – but after the 802.1x testing was completed, I intended to re-use at our cabinet.

    But when I looked into setting up monitoring for it, I found that they _do_ have an SNMP agent inside, but it’s _not_ exposed on the public interface. You can get the same data through the web interface (and you could technically scrape it but it takes lots of work for nothing), which means that it’s running and counting, but they don’t allow you to access it directly.

    And what do you find googling around? A Cisco representative on their forums that suggest that if you really want SNMP monitoring you have to get the SG-300 instead — which has fiber connections, so it costs a whole lot of money more, as the 200 version is a “cheap” baseline.

    I hate artificial software limitations with a passion.

    • Yep, agreed. I was looking around the firmware & comparing notes with other published source, and there seem to maybe be some mechanisms to get to more info. For example there is a SysInfo.htm which gives you a bit of system detail, but it’s hard-coded to be called with “default” in the webpage, so that’s all you get. There are other targets to give more info, but not accessible AFAICT.

      I’ll keep looking around to see if there is some other way to inject commands via http to make it reveal more info. There doesn’t seem to be an snmp agent actually in this firmware, though. Jon is probably right, I could try re-flashing it; it only cost $35 as a refurb, so I suppose I could afford a brick in the name of hackery. ;)

  2. Keep pushing for that GPL compliance! I know some big company selling enterprise access control systems with Linux controlling their embedded hardware… with no GPL notices whatsoever. I’m reaaly curious if that counts as violation.

  3. shsq isn’t a valid “official” Squashfs magic.

    Squashfs uses the magic number 0x73717368, which when Squashfs supported both little-endian and big-endian layouts was stored as “sqsh” (big endian) or “hsqs” (little endian). Obviously Squashfs used the ordering of the magic to determine the ordering when reading the filesystem.

    Since Squashfs 4.0 the layout is little-endian only, and so the magic for Squashfs 4.0 filesystems is now only “hsqs”.

    So what is going on here with the “shsq” magic? Well that should be sadly obvious, especially to anyone who has downloaded the above mentioned “firmware mod kit” and seen the huge variety of hacked versions of Squashfs (of various official versions of Squashfs, v1, v2, v3 etc.) that embedded vendors have produced. This is yet another unofficial hacked version of Squashfs (and which is probably a violation of my license as the necessary source code changes have probably not been published).

    The probable reason why the “unsquashers” in the firmware mod kit could not unsquash it is because the various hacked versions of Squashfs contained within it mainly date back to Squashfs v3 and older (or at least it did the last time I downloaded it), and from the above patch it is clear this is a hacked Squashfs 4.0 filesystem.

    For anyone not familiar with the Unsquashfs source code and with the Squashfs layout in general, it may not be obvious how the above patch changes Unsquashfs to recognise the filesystem. So the following may be helpful.

    The patch does two things:

    1. It changes Unsquashfs to recognise “shsq” as the magic for a Squashfs 4.0 filesystem which does not need swapping. Given Unsquashfs was likely running on a little-endian system, this means the filesystem is little-endian Squashfs 4.0 format.

    2. It changes the assignment of decompressor algorithms. This is a little more subtle.

    Squashfs 4.0 encodes the compression algorithm used in the “compression” field in the superblock. Default compression is “zlib” which is encoded as “1”. With the release of Squashfs-tools 4.0 (kernel 2.6.29) this was the only compression algorithm supported. Later versions and kernels added support for LZMA (2), LZO (3) and XZ (4).

    What appears to have happened here is the hackers have taken an original version of Squashfs 4.0 when it only supported zlib compression, and they have badly hacked in an LZMA compression algorithm replacing zlib, but keeping the compression encoding as “1”. So effectively in this hacked filesystem compression type of “1” now means LZMA.

    By rearranging the assignment of values to the compression algorithms in the patch above, it has changed Unsquashfs to now recognise “1” as LZMA compression.

    Finally, why doesn’t squashfs-tools recognise any of these hacked variants? Two reasons, firstly to do so would be in effect to officially condone these hacks, and I have no wish to do so. Secondly, these are undocumented hacks with by definition unknown changes and unknown bugs and hence possible subtle behaviour differences, and this is impossible to support.

    BTW Eric, why didn’t you ask me for help? I would have been happy to give it.

    • Hi Phillip, thanks for chiming in!

      Last thing first – I didn’t ask you for help because I knew these were undocumented, hacky aberrations of your work, and I didn’t want to bother you. But if it had taken me much longer, I might have. ;)

      Thanks for your explication of what the patch does – of course I wasn’t proposing that as any kind of squashfs enhancement or whatnot, or wondering why squashfs didn’t recognize it in the first place. I was just un-hacking the hackery that Linksys/Cisco/someplace.tw/whoever did, though TBH it was very seat-of-the-pants on my part.

      The “mod kit” does seem to have some hacked 4.0 versions of squashfs in it, but none of them recognized the image. So yeah, the image seems to identify itself as version 4.0, but with scrambled magic, and compression type “1” but using LZMA. What a mess.

      I don’t know if the busybox folks ever get remunerations from GPL actions, but boy, you should too. The embedded world owes you at least a lot of gratitude, and that’s an understatement.

  4. I have a firmware very difficult to decompress, binwalker displays the following:

    DECIMAL HEX DESCRIPTION
    ——————————————————————————————————-
    10603 0x296B LZMA compressed data, properties: 0xAD, dictionary size: 809631744 bytes, uncompressed size: 65011720 bytes
    39750 0x9B46 LZMA compressed data, properties: 0xC0, dictionary size: 1073741824 bytes, uncompressed size: 1073741568 bytes
    131200 0x20080 LZMA compressed data, properties: 0x6C, dictionary size: 8388608 bytes, uncompressed size: 6323336 bytes
    2097280 0x200080 LZMA compressed data, properties: 0x6C, dictionary size: 8388608 bytes, uncompressed size: 2419340 bytes

    I have tried with “dd” but it creates an invalid lzma file. I’m stuck here

  5. I have similar problem but no one seems to help me, I did stuck with some custom squashfs method, I can extract part of data with offzip because is zlib (78 DA) but I am looking for root file system.. here can check with hex dump

    00520000 68 73 71 73 32 01 00 00 4c 61 85 50 00 00 02 00 |hsqs2…La.P….|
    00520010 04 00 00 00 01 00 11 00 c0 00 01 00 04 00 00 00 |…………….|
    00520020 3b 04 24 08 00 00 00 00 0f 4d b5 00 00 00 00 00 |;.$……M……|
    00520030 07 4d b5 00 00 00 00 00 ff ff ff ff ff ff ff ff |.M…………..|
    00520040 3f 34 b5 00 00 00 00 00 20 3f b5 00 00 00 00 00 |?4…… ?……|
    00520050 08 4a b5 00 00 00 00 00 f9 4c b5 00 00 00 00 00 |.J…….L……|
    00520060 78 da a4 bd 0d 78 54 c7 75 37 7e f6 ee 4a 5a 84 |x….xT.u7~..JZ.|
    00520070 30 57 62 11 8b 11 78 57 ba 5a c9 48 b6 05 91 5d |0Wb…xW.Z.H…]|
    00520080 c5 51 9c b5 24 3e 8c 94 64 01 d9 21 2d 09 42 1f |.Q..$>..d..!-.B.|
    00520090 18 62 01 32 92 6d 9c 38 f6 5a c8 8e 9a 6e 56 32 |.b.2.m.8.Z…nV2|
    005200a0 a1 09 6d dc 74 23 90 4d 12 45 02 8c 6d da ba 89 |..m.t#.M.E..m…|

    similar header appears few offset below..

    007a0000 68 73 71 73 36 01 00 00 02 92 ff 50 00 00 02 00 |hsqs6……P….|
    007a0010 05 00 00 00 01 00 11 00 c0 00 01 00 04 00 00 00 |…………….|
    007a0020 b3 04 42 08 00 00 00 00 c6 c8 b6 00 00 00 00 00 |..B………….|
    007a0030 be c8 b6 00 00 00 00 00 ff ff ff ff ff ff ff ff |…………….|
    007a0040 1a b0 b6 00 00 00 00 00 5f bb b6 00 00 00 00 00 |…….._…….|
    007a0050 50 c6 b6 00 00 00 00 00 b0 c8 b6 00 00 00 00 00 |P……………|
    007a0060 78 da a4 bd 0d 78 54 c7 75 37 7e f6 ee 4a 5a 84 |x….xT.u7~..JZ.|
    007a0070 30 57 62 11 8b 11 78 57 ba 5a c9 48 b6 05 91 5d |0Wb…xW.Z.H…]|
    007a0080 c5 51 9c b5 24 3e 8c 94 64 01 d9 21 2d 09 42 1f |.Q..$>..d..!-.B.|
    007a0090 18 62 01 32 92 6d 9c 38 f6 5a c8 8e 9a 6e 56 32 |.b.2.m.8.Z…nV2|
    007a00a0 a1 09 6d dc 74 23 90 4d 12 45 02 8c 6d da ba 89 |..m.t#.M.E..m…|
    007a00b0 e2 10 d7 6d 68 4b 53 9a c7 ed eb 36 bc 8d 9d b8 |…mhKS….6….|

    so, what the hell?

  6. please i want help,
    i have SquashFS-3.0 File
    how i compress it’s files after editing ?
    it’s seems that there is another values to change in the org f/w file headers beside to (CRC-32) and SquashFS file (Size)…

Leave a Reply to Ravi kumar Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.