Why would I need to invest my time in trying to dump the firmware of a cable modem?
The short explanation: that specific firmware was urgently needed to be deployed on more devices (many) in order to correct a critical failure. The official procedure through the vendor support channel was unlikely to be successful within an acceptable time.
I decided to explore the telnet CLI to figure out whether there was a simple way to upload the firmware to another machine using TFTP or FTP, but there is nothing similar. However, the
CM/Flash directory looked promising:
CM/Flash>? autoTest cfi_show close configRegion deinit erase init open read readDirect show write writeArray CM/Flash> show Block kB Address Offset Offset Region Allocation — — — — — — — — — — — — — — — — — — — — — — — — — — — — — - 0 32 0x1badf1a5 0 0 Bootloader 1 32 0x1bae71a5 0x8000 0x8000 Bootloader (65536 bytes) 2 64 0x1baef1a5 0x10000 0 Permanent NonVol (65536 bytes) 3 64 0x1baff1a5 0x20000 0 Image1 … 128 64 0x1c2cf1a5 0x7f0000 0x7d0000 Image1 (8257536 bytes) 129 64 0x1c2df1a5 0x800000 0 Image2 … 254 64 0x1caaf1a5 0xfd0000 0x7d0000 Image2 (8257536 bytes) 255 64 0x1cabf1a5 0xfe0000 0 Dynamic NonVol 256 64 0x1cacf1a5 0xff0000 0x10000 Dynamic NonVol (131072 bytes)
Good news! It is possible to perform read queries on the device flash memory:
CM/Flash> open image1 Opening the flash driver… Flash driver opened. CM/Flash> read 4 64 0 Reading 64 bytes as 4-byte entities, starting at an offset of 0 bytes into the region: a0410005 00030000 54002665 00499cc4 80004000 65333932 352d4531 302d352d 63313230 30723535 39332d31 34303832 39632e62 696e0000 00000000 00000000
Great! I could write a simple program to read out the whole area of memory and then generate the binary firmware file. But…
How could I validate that I’m doing well?
The size of each memory area is ~8MB when images of this specific modem are ~2Mb. In other words, I’d have to process the header of the image to find its size. At this point it was a huge help this awesome post from @bernardomr and to have access to other versions of firmware to reverse engineering.
After some research I came to the following:
- The LZMA file begins at the position 0x5c (0x5d000010 …).
- The 3 bytes in yellow represent the file size: 0x1c0fbc (1,839,036 bytes).
- So I need to read the flash to the byte 0x5c + 0x1c0fbc = 0x1c1018.
Finally, I wrote brcm_firmware_dump.py and was able to get the firmware 🙂