How to open/modify IMG file for Seek-Shot firmware? - Page 1
Forum section: electronics / general

author: Logan
author_rank: Frequent Contributor
timestamp: August 13, 2021, 02:29:54 pm
content: Hi guys.Here is the firmware update file:https://www.thermal.com/uploads/1/0/1/3/101388544/seekfirmware_2.09.zipAfter unzip, there's a .img file, but that file cannot be mounted by Windows, nor opened as archive by 7-zip. I looked at it with hex editor, most parts are unrecognizable, but there are several blocks of pure zero, and a big block text looks like "boot/error messages". So is it a flash dump? However it's 26MiB, do not look like a standard flash size.Any ideas? Thank you.

author: alexnoot
author_rank: Contributor
timestamp: August 14, 2021, 11:03:55 am
content: .img files can beseveral thingsFrom your description it sounds like your file is a pure binary file (flash) containing the firmware. To be able to read it would depend on it's system type, it could contain multiple partitions (boot/system) etc.

author: Logan
author_rank: Frequent Contributor
timestamp: August 14, 2021, 05:38:12 pm
content: Thank you.I already read that wiki page before asking, but I want to know how to open this exact file.

author: Nominal Animal
author_rank: Super Contributor
timestamp: August 15, 2021, 01:55:41 am
content: It seems to me the first 0x8000 (32768) bytes of the file contains 512-byte descriptors. If so, "rootfs" should be the 0x7000 sectors (14680064 = 0x00e00000 bytes) starting at sector 0x5000 (10485760 = 0x00a00000 bytes). Indeed, it is a squashfs filesystem, containing a BusyBox Linux system.Because the descriptors refer directly to sector numbers (512-byte offsets from the beginning of this file), I believe it is a direct, complete Flash image, with 512-byte sectors.The other descriptors:vendor: 28,672 bytes of zeros.IDBlock: 1,048,576 bytes of data. U-Boot 2017.09-ga2775d4-dirty, and RockChip DDR 1.03. Also says "Rockchip RV1108 Evaluation board".kernel: Size is 4,879,582 bytes, maximum size is 6,291,456 bytes (the difference is all zeros).resource: 2,097,152 bytes of data. Looks like a simple filesystem with files ENTRrk-kernel.dtb, ENTRbattery_1.bmp, ENTRbattery_2.bmp, ENTRbattery_3.bmp, ENTRbattery_4.bmp, ENTRbattery_5.bmp, ENTRbattery_fail.bmp, ENTRbattery_null.bmp, ENTRlogo.bmp, ENTRlogo_kernel.bmp, and ENTRbattery_0.bmp. This uses a very simple sector-based descriptors (roughly similar to the image file itself), so these are likely images and files available to the loader/bootloader.There are also two 'user' descriptors, but the first one refers to sectors outside the file, and the second one has size -1 sectors.Of these, the rootfs (squashfs) and the resource (ENTRrk-kernel.dtb and the ENTR*.bmp files) can be extracted and modified and reinserted back (using standard Linux tools,dd,losetup,mount,umount; I don't know if there are suitable tools for Windows as I don't use Windows myself at all). I don't know if the image contains a checksum: if there is, a modified image would be rejected.

author: Logan
author_rank: Frequent Contributor
timestamp: August 15, 2021, 03:34:08 pm
content: Thank you so much Nominal Animal.I may try on a Linux machine later.There's a checksum outside the image (in the zip) as plain text, I don't think there will be another. The firmware is full of bugs, I don't think they even bother to add a signature.

author: Nominal Animal
author_rank: Super Contributor
timestamp: August 15, 2021, 05:25:40 pm
content: You're welcome. If you decide to proceed in Linux with this, feel free to ping me (message or email); I can help you with the exact commands you need.For anyone interested, the first 512 bytes (the very first sector) in the image is (using e.g.hexdump -C):00000000 52 4b 46 50 e3 07 08 1d 0e 2d 0c 00 01 00 30 0f |RKFP.....-....0.|00000010 00 02 00 00 01 00 00 00 00 d0 00 00 80 00 00 00 |................|00000020 07 00 00 0000 00 00 0001 00 00 0000 00 00 00 |................|00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|:::::::: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: (all zeros)000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|000001f0 00 00 00 00 00 00 00 00 18 36 b5 c2 ea 96 53 62 |.........6....Sb|Offset 0x24 (markedred) contains the sector number, least significant byte first (so01 23 45 67 = 0x67452301), and offset 0x28 (markedblue) contains the size in sectors, least significant byte first.This is followed by seven 128-byte descriptors in a similar format (and we'll see this again later on, under theresourcedescriptor); the latter half (last 64 bytes) of each descriptor is zero, so I'll omit those.00000200 76 65 6e 64 6f 72 00 00 00 00 00 00 00 00 00 00 |vendor..........|00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|00000220 01 00 00 0008 00 00 0038 00 00 0000 70 00 00 |........8....p..|00000230 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 |................|00000280 49 44 42 6c 6f 63 6b 00 00 00 00 00 00 00 00 00 |IDBlock.........|00000290 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|000002a0 02 00 00 0040 00 00 0000 08 00 0000 b0 07 00 |....@...........|000002b0 00 00 00 00 00 00 00 00 00 bc 15 00 00 37 8c 07 |.............7..|00000300 6b 65 72 6e 65 6c 00 00 00 00 00 00 00 00 00 00 |kernel..........|00000310 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|00000320 04 00 00 0000 20 00 0000 30 00 00e0 74 4a 00 |..... ...0...tJ.|00000330 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|00000380 72 6f 6f 74 66 73 00 00 00 00 00 00 00 00 00 00 |rootfs..........|00000390 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|000003a0 08 00 00 0000 50 00 0000 70 00 0000 00 a6 00 |.....P...p......|000003b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|00000400 72 65 73 6f 75 72 63 65 00 00 00 00 00 00 00 00 |resource........|00000410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|00000420 20 00 00 0000 c0 00 0000 10 00 0000 40 0a 00 | ............@..|00000430 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|00000480 75 73 65 72 00 00 00 00 00 00 00 00 00 00 00 00 |user............|00000490 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|000004a0 00 00 00 8000 e0 00 0000 00 04 0000 00 00 00 |................|000004b0 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 |................|00000500 75 73 65 72 00 00 00 00 00 00 00 00 00 00 00 00 |user............|00000510 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|00000520 00 00 00 8000 00 05 00ff ff ff ff00 00 00 00 |................|00000530 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 |................|To extract say thekernelpart to a separate file, we note the offset sector is00 20 00 00 = 0x00002000, and the number of sectors is00 30 00 00 = 0x00003000(remember, least significant byte is listed first!). Then,dd if=Firmware.img of=kernel.bin bs=512 skip=$ count=$Similarly for the other descriptors.To replace it with filekernel.new, we first create a copy of the original image,cp Firmware.img firmware.newoverwrite the old content with zeroes (just in case the new one is shorter),dd if=/dev/zero of=firmware.new bs=512 seek=$ count=$noting that this time we use seek instead of skip, because skip= sets input offset, and seek= sets output offset.Then, assuming the newkernel.newis not too long, we just insert it in:dd if=kernel.new of=firmware.new bs=512 seek=$noting that we leave count out, so that the entire file is inserted.The rootfs is a Squash filesystem. Since we know it starts at offset 0x5000, i.e. at byte $[0x5000*512] = 10485760, and is 0x7000 sectors long, $[0x7000*512] = 14680064 bytes, we can use the Linux loopback device to mount the filesystem directly in the new image, and modify the files however we like:mkdir squashdev=$(sudo losetup --find --show firmware.new -o $[0x5000*512] --sizelimit $[0x7000*512]) && echo "Using device $dev"sudo mount $dev squashYou can now read and write to the squash filesystem under directory squash. Note that thefirmware.newfile gets modified when you access and modify the contents. You can mount it read-only adding-rflag to losetup (sudo losetup -r --find --show ...) and-o roto mount (sudo mount -o ro $dev squash), in which case the original file is not modified.When you are done, just runsudo umount squashsudo losetup -d $devwhich also makes sure all modifications (unless mounted read-only) are committed to thefirmware.newfile.Theresourcedescriptor refers to 0x1000 sectors starting at sector 0xc000. Sector 0xc000 is all zeros except for an initial 16-byte part,01800000 52 53 43 45 00 00 00 00 01 01 01 000b 00 00 00|RSCE............|That 0x0000000b = 11 seems to indicate 11 sectors follow, each with one entry. It looks like each of these 512-byte sectors contain a file name in the first 256 bytes, padded with zeroes, followed by four zero bytes, the sector offset (32-bit little-endian), file length (in bytes, 32-bit little-endian), and rest of the sector zeroes:01800200 45 4e 54 52 72 6b 2d 6b 65 72 6e 65 6c 2e 64 74 |ENTRrk-kernel.dt|01800210 62 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |b...............|01800300 00 00 00 000c 00 00 00e1 77 01 0000 00 00 00 |.........w......|01800400 45 4e 54 52 62 61 74 74 65 72 79 5f 31 2e 62 6d |ENTRbattery_1.bm|01800410 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |p...............|01800500 00 00 00 00c8 00 00 00b6 74 00 0000 00 00 00 |.........t......|01800600 45 4e 54 52 62 61 74 74 65 72 79 5f 32 2e 62 6d |ENTRbattery_2.bm|01800610 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |p...............|01800700 00 00 00 0003 01 00 00b6 74 00 0000 00 00 00 |.........t......|01800800 45 4e 54 52 62 61 74 74 65 72 79 5f 33 2e 62 6d |ENTRbattery_3.bm|01800810 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |p...............|01800900 00 00 00 003e 01 00 00b6 74 00 0000 00 00 00 |....>....t......|01800a00 45 4e 54 52 62 61 74 74 65 72 79 5f 34 2e 62 6d |ENTRbattery_4.bm|01800a10 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |p...............|01800b00 00 00 00 0079 01 00 00b6 74 00 0000 00 00 00 |....y....t......|01800c00 45 4e 54 52 62 61 74 74 65 72 79 5f 35 2e 62 6d |ENTRbattery_5.bm|01800c10 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |p...............|01800d00 00 00 00 00b4 01 00 00b6 74 00 0000 00 00 00 |.........t......|01800e00 45 4e 54 52 62 61 74 74 65 72 79 5f 66 61 69 6c |ENTRbattery_fail|01800e10 2e 62 6d 70 00 00 00 00 00 00 00 00 00 00 00 00 |.bmp............|01800f00 00 00 00 00ef 01 00 00b6 74 00 0000 00 00 00 |.........t......|01801000 45 4e 54 52 62 61 74 74 65 72 79 5f 6e 75 6c 6c |ENTRbattery_null|01801010 2e 62 6d 70 00 00 00 00 00 00 00 00 00 00 00 00 |.bmp............|01801100 00 00 00 002a 02 00 00b6 74 00 0000 00 00 00 |....*....t......|01801200 45 4e 54 52 6c 6f 67 6f 2e 62 6d 70 00 00 00 00 |ENTRlogo.bmp....|01801210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|01801300 00 00 00 0065 02 00 0036 b4 04 0000 00 00 00 |....e...6.......|01801400 45 4e 54 52 6c 6f 67 6f 5f 6b 65 72 6e 65 6c 2e |ENTRlogo_kernel.|01801410 62 6d 70 00 00 00 00 00 00 00 00 00 00 00 00 00 |bmp.............|01801500 00 00 00 00c0 04 00 00d8 4a 00 0000 00 00 00 |.........J......|01801600 45 4e 54 52 62 61 74 74 65 72 79 5f 30 2e 62 6d |ENTRbattery_0.bm|01801610 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |p...............|01801700 00 00 00 00e6 04 00 008a 73 00 0000 00 00 00 |.........s......|Note that theredsector number is offset to the starting sector, 0xc000, but thegreenis the length in bytes. Assuming ENTR is the identifier for the sector and not part of the file name, then we should expect rk-kernel.dtb start at sector 0xc00c and be 0x177e1 bytes long:dd if=Firmware.img of=rk-kernel.dtb bs=1 skip=$[(0xc000+0x000c)*512] count=$and indeed it is. For example, we can also extract the logo_kernel.bmp viadd if=Firmware.img of=logo_kernel.bmp bs=1 skip=$[(0xc000+0x04c0)*512] count=$which indeed is a 654×258 Windows 3.x format 8-bit bitmap image (Rockchip kernel in white on black background, with yellow stylizing in the dot of the i).If one wants to modify the images, this format is uncompressed, so a replacement image is exactly the same size. (Note that while the images have a palette of 256 colors out of 16M, you probably want to make sure entry 0 is black.)To reinsert a modified logo_kernel.bmp to firmware.new, we simply reverse the command (noting skip becomes seek),dd if=logo_kernel.bmp of=firmware.new bs=1 seek=$[0xc000+0x04c0)*512] count=$We can modify the size and relocate these resource files by modifying that RSCE sector and the corresponding ENTR sectors, but it becomes a bit more complicated. Note that a replacement rk-kernel.dtb can be up to 96256 bytes long (0x17800) before the rest of the resource files need to be bumped to another sector.Note that this is just my initial efforts in reverse engineering this file format out of pure curiosity, and I suspect there might be checksums in some of the sectors to verify the validity. (This would be done by the Rockchip developers, as such checking would be part of the BIOS/loader, not the Linux userspace code managed by those who cobbled this device together.)However, the information above is already sufficient to write a C or Python utility to replace given resource files, the kernel image, or the Squash filesystem. I don't have the hardware, so I can't tell whether such modified firmware images would work or not, but looking at the ~ 500 files in the Squash filesystem (comprising of the Linux system running on the device), I can definitely understand why one would like to experiment: it looks like someone took a RockChip development board dev environment, and did the minimal changes necessary, augmented by spittle, bubblegum and hope, to get a shippable product. Don't take my word for it, though; feel free to take a look yourself.