How to Unpack/MOD/Repack A DD-WRT trx Firmware

DD-WRT firmware is packed as ‘.trx’. The purpose of document is to to show you how to MOD a DD-WRT package, without recompile it from source. All the tool need is a virtual-boxed ubuntu, DD-WRT svn source code, a hex editor, HxD .

A MOD firmware may brick your device, so make sure you know what you are doing by googling enough before apply changes to the router. Doing this on your own risk.

There is one obvious way to modify the package is, to download and modify source code/script, recompile from source code, repack firmware. But it seems to me that DD-WRT maintainers are not very please sharing their build system, the best resource is could found is Compiling DD-WRT. It was a miserable process for me, I don’t know which module need to be built for EA2700 and endless error shows up, when trying to read/modify configuration, guessing, building. I spent a whole weekend keep failing, not even able to run through the configuration. So I gave up this way.

I also found a tool Firmware Mod Kit, but seems that it is out of maintenance, at least not for me.

During the building attemptsssssssssssssss, I acknowledge that for EA2700, the main build script is src/router/Makefile.brcm3x, I was thinking that there must be packing process in this script, so if I want to unpack, I shall be able to find some clues of how the package generated. Here is some piece of code from Makefile.brcm3x:

gzip -c9 mipsel-uclibc/lzma_vmlinus > mipsel-uclibc/lzma_vmlinuz
../../opt/tools/trx -m 32000000 -o $(ARCH)-uclibc/dd-wrt.v24-K3-nandboot.trx $(ARCH)-uclibc/lzma_vmlinuz  -a 1024 $(ARCH)-uclibc/rootfs.squashfs

According to TRX Header, from openwrt,

  0                   1                   2                   3   
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
 |                     magic number ('HDR0')                     |
 |                  length (header size + data)                  |
 |                       32-bit CRC value                        |
 |           TRX flags           |          TRX version          |
 |                      Partition offset[0]                      |
 |                      Partition offset[1]                      |
 |                      Partition offset[2]                      |
  • offset[0] = lzma-loader
  • offset[1] = Linux-Kernel
  • offset[2] = rootfs

It seems that the trx is packing two files, lzma_vmlinuz and rootfs.squashfs. I also verify this by reading source code of trx.c. lzma_vmlinuz should be a gzipped Linux kernel and rootfs.squashfs should be the read-only rootfs, and that is my interest.

Open a prebuilt firmware, using Hxd, we can see the trx header very clear:


trx header

The first part, vmlinuz is at offset: 0x0000001c, the second part, rootfs, should be located at offset:0x0012c800, little endian.

If we just extract rootfs, we could ignore the first part, but as we need to repack firmware after modification, flash to router, without rebuilding from source, we need a standalone lzma_vmlinuz. As we can see in the previous script, lzma_vmlinuz is gzip compressed, so need to have some knowledge of gzip format.

 2.3. Member format

      Each member has the following structure:

         |ID1|ID2|CM |FLG|     MTIME     |XFL|OS | (more-->)

      (if FLG.FEXTRA set)

         | XLEN  |...XLEN bytes of "extra field"...| (more-->)

      (if FLG.FNAME set)

         |...original file name, zero-terminated...| (more-->)

      (if FLG.FCOMMENT set)

         |...file comment, zero-terminated...| (more-->)

      (if FLG.FHCRC set)

         | CRC16 |

         |...compressed blocks...| (more-->)

           0   1   2   3   4   5   6   7
         |     CRC32     |     ISIZE     |

gzip header

Gzip header is very clear, original file name is lzma_vmlinus, the problem is we don’t know where it ends. So we move to position: 0x0012c800, the second file, where rootfs starts:

rootfs head

lzma_vmlinuz ends here

0x0012c800 is where rootfs begun. trx tool padding some zeros to the to the end of each part, according to -a 1024 option. We could not tell exactly where vmlinus ends, but we can guess. According to RFC1952, gzip end with 32bits CRC32 and 32bits ISIZE, the original file size, so the last 4 bytes has meaning. 0x12cd48bc, is too big for a file size, while 0x000012cd is too small, but 0x0012cd40=1,232,200 sounds reasonable. So we copy from offset 0x1c to 0x12c739, open a new tab from HxD, save to a new file, name it ‘vmlinuz‘, using 7zip on windows or gzip on linux to decompress it, check if the guess is correct. In this way, we had lzma_vmlinuz exacted and verified. We don’t really need to decompress lzma_vmlinuz, ’cause we do not actually modify it.

From 0x0012c800, to the end of file, save as another file ‘rootfs.squashfs‘, we could consider it as rootfs, but how to exact and  apply a MOD? Here is another piece from Makefile.brcm3x:

$(LINUXDIR)/scripts/squashfs/mksquashfs-lzma $(ARCH)-uclibc/target $(ARCH)-uclibc/rootfs.squashfs -noappend -root-owned -le

It is said, rootfs.squashfs is a, LZMA compressed, SQUASHFS file system. So how to exact rootfs? I don’t do much reading of SQUASHFS file system spec, I found the source code of mk/unsquashfs-lzma. OK, just build this tool on Ubuntu by typing ‘make‘, you get both tools of mksquashfs-lzma and unsquashfs-lzma. That is way much easier than building DD-WRT. Now we have tools of packing and unpacking squashfs image.

./unsquashfs-lzma rootfs.squashfs

The result is a directory (default name ‘rootfs.squashfs‘, I don’t remember, see help) tree of run-time read-only Linux rootfs:

rootfs tree

I was so exciting about what I saw. The original unpack size is more than 49M. Now we could start a MOD. As my purpose is to reduce size of the whole package, I located big files and relate resources, which I assure unnecessary, remove them from directory tree. You could also add new tool, modify init script, modify services, make symbol link, whatever you needed, I think.

After modification, the last step is to repack it. Things are straightforward:

# packing rootfs.squashfs image
sudo ./mksquashfs-lzma rootfs.squashfs -noappend -root-owned -le
#packing firmware. if you do have trx tool, just build it yourself
sudo ./trx -m 32000000 -o new.trx vmlinuz  -a 1024 rootfs.squashfs

Then you can try upload new firmware using Web GUI, or serial recovery, if you already have serial pin out. The web page including all necessary resource to flashing DD-WRT to router by serial port, but  maybe out of date, I actually using :

flash -noheader : flash1.trx

to flash Linksys ea2700 router. Flashing firmware, especially a MOD version, using CFE, maybe dangerous, but as my understand, as long as CFE is not damage, Linksys router always has a backup ‘known good’ firmware on the other partition, which will automatically recover to active partition, if active partition boots failed for several times, that is why peoples reported that sometimes their Linksys router return back to original factory firmware during flash DD-WRT.


I share a Dropbox folder here, including some tools (I built on Ubuntu 16.04 LTS) and a MOD firmware for Linksys EA2700. I did not list what I remove, but you can always unpack ‘dd-wrt-30949-ea2700.MOD.trx’, then compare rootfs directory tree to the original version download from

This entry was posted in tech and tagged , , , , , , . Bookmark the permalink.

One Response to How to Unpack/MOD/Repack A DD-WRT trx Firmware

  1. Pingback: A way to fix DD-WRT can not run on some Linksys EA2700 router | wreckor

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