ILDVR INC-MH40D06 or hacking cheap chinese camera part 2 (or unbricking the camera via uBoot).
While was poking around, I managed to brick the camera.
I tried to change the /etc/password on rootfs, unfortunately due to nature jffs2 small changes to file system are not quiet possible if there is not enough space of erase block size increment (in this case it was 64k).
I moved away udevadm to be able to change /etc/passwd only to find I could not move it back or change the /etc/password anyway.
Any attempt led to
No space left on device. I put this to my lack of experience with jffs2. At that stage I knew camera was bricked. I saved udevadm binary which I moved away via wget (I moved it to the
/mnt/flash/web/browse/ which made it accessible via http). I confirmed it was bricked after reboot (no networking due to unable to mount the flash partitions, as udev was not setting up the block devices in /dev).
Now, I knew that not everything was lost, since I still had uBoot working.
Specifically there was tftp tool to upload/download memory chunks to tftp server.
Since I am using Ubuntu I installed tftp server:
apt-get install tftpd-hpa
It took me awhile to figure out how to read and write to flash (the give away was
sf probe 0;sf read 0x82000000 0x50000 0x2b0000;bootm 0x82000000 as bootcmd).
To map the flash memory into a specific address in RAM of specific offset of flash memory (flash address) and specific length (flash partition size):
sf read $MEMORY_ADDRESS $FLASH_OFFSET $SIZE
I use the default address of 0x82000000 (no idea how it is determined normally, I guess it is high enough not to interfere with uBoot?).
For Flash Offset and size I used data from cat
If these things are unknown I guess it is possible to infer them from bootcmd or by dumping the whole flash and then binwalk (I haven't tried this).
/proc/mtd looked like this:
dev: size erasesize name
mtd0: 00050000 00010000 "boot"
mtd1: 002b0000 00010000 "kernel"
mtd2: 00200000 00010000 "rootfs"
mtd3: 00b00000 00010000 "data"
Rest is simple arithmetic:
The partition I was after was mtd2 (rootfs), so the offset worked out to
0x50000+0x2b0000=0x300000 (mtd0 + mtd1) the size is 0x20000.
To map the mtd2 to memory I ran following:
sf probe 0
sf read 0x82000000 0x300000 0x200000
sf probe 0 needed to initialise flash.
I changed the ip address/netmask and tftp server address via this command (my tftp server is on 192.168.1.2):
set env ipaddr 192.168.1.200
set env serverip 192.168.1.2
set env netmask 255.255.255.0
Then I put the mapped flash via tftp to the tftp server:
tftp 0x82000000 rootfs 0x200000
Once I had the image I loaded into mtd device emulation on local machine. This is where erasesize comes handy (found in /proc/mtd earlier): 0x10000 = 64KiB.
To setup mtd device on a linux box you need to do the following:
modprobe mtdram total_size=2048 erase_size=64
dd if=/var/lib/tftp/rootfs of=/dev/mtdblock0
mount -t jffs2 /dev/mtdblock0 /tmp/target/
You might need to install mtd and jffs2 related packages:
apt-get install mtd-utils
If it complains about "wrong superblock" while attempting to mount jffs2 most likely you got size/offset wrong when you ran
Once you have file system mounted copy it into a directory for future manipulations.
In the copy I fixed the /etc/passwd. I copied in the udevadm binary that I moved away.
Then I repacked the directory back into jffs2 image:
mkfs.jffs2 -r ~/rootfs_ipcam -e 64 -n -m size -o /var/lib/tftpboot/rootfs_fixed
Only to find out that the image turned out to be larger then the target size of 2MiB. I guess they used proprietary tools to build their jffs2 to cram it right into 2MiB. I was off by 140KiB or so. I managed to reclaim space by removing mkfs.fat and e2fsck (since this particular camera does not have physical SD-Card slot). Alternative was getting all partitions and re-shuffling the layout (relatively easy, but tedious).
Once I had the image built I confirmed that was OK by mounting the same way as original image.
The final step was downloading new image via tftp and flashing it:
tftp 0x82000000 rootfs_fixed
sf erase 0x300000 0x200000
sf write 0x82000000 0x300000 0x200000
WARNING: Don't get the size/offset wrong as you will have bad times!