In previous posts, we saw how we could identify a serial console on a DVR, connect and interact with it, and – if full shell access was enabled – recover the firmware using a USB stick. But what happens if we can’t get a full shell on the device? What happens if the kernel doesn’t have a serial console enabled?
We can use the U-boot console to dump the data out over serial, and rebuild it into a binary file!
Getting to the U-boot console
You want to connect up a serial adapter, start your terminal emulator (minicom) and watch the screen as the device boots. It’s highly likely you will see a message:
Hit any key to stop autoboot:
There will normally also be a countdown timer for a few seconds. This is U-boot prompting to see if you want to go into the U-boot console. Reboot, and press a key when prompted!
We are now at the U-boot console. The “hisilicon” prompt is because this is a Hisilicon SoC using their version of U-boot.
In a later post we will look at ways of getting into the U-boot even when there is no obvious key sequence, by glitching one of the SPI flash signals.
Type help to see what commands are supported.
The number of commands supported varies from device to device, but most low-cost DVRs will have a fairly comprehensive list. Unfortunately (for us), U-boot is mostly concerned with copying data onto the SPI flash, whereas we want to copy data from the SPI flash. The USB commands are not of help.
Frequently a protocol called TFTP is supported – Trivial File Transfer Protocol. On most DVRs this allows data to be uploaded and downloaded. Other embedded devices vary; it’s common to find U-boot only allows data to be download to the device.
There is also a command md – memory display, which we can use to read the memory on the device out over serial.
Let’s look at both methods.
Dumping Memory over TFTP
TFTP is Trivial FTP, a very simple file transfer protocol that U-boot often integrates. We can use this to copy data off the device.
Setting up a TFTP server
In Ubuntu 18.04 and 20.04, the package tftpd-hpa sets up a TFTP server:
sudo apt install tftpd-hpa
That’s it. Files that are received by it will be stored by default in /srv/tftp.
Setting your IP address
U-boot stores settings in something called “environment variables”. If you run
These will be displayed.
You are looking for ipaddr (the DVR’s IP) and serverip (the IP of the TFTP server). These will generally have default values. You can either change the IP of the machine hosting the server, or you can modify the environment variables. To change both, run:
setenv ipaddr 10.42.0.2 setenv serverip 10.42.0.1
Copy the flash to RAM
This device has a 16MByte SPI flash chip. You can look up the size using the part number from the board, or just read it from the boot log.
This means we want to read back 0x1000000 bytes. SPI flash is not directly memory mapped on Hisilicon DVRs, which means we can’t directly access it. We need to copy it into RAM first.
First we need to initialise the flash:
sf probe 0
Now we need to copy the flash into RAM. A suitable address in RAM is nearly always 0x82000000. This command copies 0x1000000 bytes from address 0x0 of the flash into RAM.
sf read 0x82000000 0x0 0x1000000
Copy out with tftp
A quirk of tftp is that you can’t, by default, create files on the server. The easiest way to resolve this is to create an empty file on the server, and let it be overwritten.
cd /srv/tftp sudo touch firmware.bin sudo chmod 666 firmware.bin
Note the file has 0 size and is read/write by everyone.
Then, back in the uboot prompt, we run:
tftp 0x82000000 firmware.bin 0x1000000
And now, heading back to the server – we will have the full flash firmware. Simple!
Dumping Memory over Serial
We can use memory display to show us the contents of flash memory.
If you type the follwing command you with get 0x10 (16) bytes of memory from address 0x0.
md.b 0 10
How do we leverage this to extract the flash?
Capture to file
You want to store the output to a file. In minicom, you use:
And choose a filename.
Now we need to dump the RAM:
md.b 0x82000000 0x1000000
This will take some time. Why? Each row is 80 characters long – 80 bytes – and only contains 16 bytes of data. That means for our 16Mbyte flash memory, we need to transfer 80Mbytes over a serial connection! At 115200bps (where each byte takes 10 bits including start and stop bits), that’s just over 2 hours!
This might seem like a long time, but given how safe serial connections are, and how ubiquitous U-boot is, it’s a highly effective mechanism for smaller SPI flash memories. It’s clearly not workable for the 32GByte+ eMMC found in some devices though – that would take months!
Once this is done, close the capture file using:
Trimming the file
Use a text editor to trim anything before and after the memory dump output itself.
Post-processing the file
We’ll use a tool called uboot-mdb-dump to allow us to convert the verbose md output into a binary.
First, make sure git is installed.
sudo apt install git
Now clone the repository
git clone https://github.com/gmbnomis/uboot-mdb-dump
Go into the cloned directory:
Finally, we can take our capture file (flash.cap in my case), and process it to obtain a binary file representing the flash.
python3 uboot_mdb_to_image.py < flash.cap > flash.bin
We should now have an image of the entire flash, which can be carved up into partitions (details in a later post) or extracted with binwalk.
Unfortunately, serial extraction like this is prone to errors which this tool might not be able to parse. You can generally edit the file manually, maybe using specific md.b commands to correct damaged areas.
Much more advanced work can be carried out with an impressive tool called Depth Charge , which we will cover in another post. The method here just shows the basic process.
That’s another two methods to get firmware from a device. The first is fast, the second slow. The more tools you have in your arsenal to do this, the more effective you will become.