Author Topic: How to open/modify IMG file for Seek-Shot firmware?  (Read 1769 times)

0 Members and 1 Guest are viewing this topic.

Offline Logan

  • Regular Contributor
  • *
  • Posts: 172
  • Country: us
How to open/modify IMG file for Seek-Shot firmware?
« on: August 13, 2021, 02:29:54 pm »
Hi guys.
Here is the firmware update file: https://www.thermal.com/uploads/1/0/1/3/101388544/seekfirmware_2.09.zip
After 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.
 

Offline alexnoot

  • Contributor
  • Posts: 19
  • Country: aq
Re: How to open/modify IMG file for Seek-Shot firmware?
« Reply #1 on: August 14, 2021, 11:03:55 am »
.img files can be several things

From 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.
 
The following users thanked this post: Logan

Offline Logan

  • Regular Contributor
  • *
  • Posts: 172
  • Country: us
Re: How to open/modify IMG file for Seek-Shot firmware?
« Reply #2 on: August 14, 2021, 05:38:12 pm »
Thank you.
I already read that wiki page before asking, but I want to know how to open this exact file.
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 3074
  • Country: fi
    • My home page and email address
Re: How to open/modify IMG file for Seek-Shot firmware?
« Reply #3 on: August 15, 2021, 01:55:41 am »
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.
 
The following users thanked this post: Logan

Offline Logan

  • Regular Contributor
  • *
  • Posts: 172
  • Country: us
Re: How to open/modify IMG file for Seek-Shot firmware?
« Reply #4 on: August 15, 2021, 03:34:08 pm »
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.
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 3074
  • Country: fi
    • My home page and email address
Re: How to open/modify IMG file for Seek-Shot firmware?
« Reply #5 on: August 15, 2021, 05:25:40 pm »
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 00 00 00 00 00  01 00 00 00 00 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 (marked red) contains the sector number, least significant byte first (so 01 23 45 67 = 0x67452301), and offset 0x28 (marked blue) 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 the resource descriptor); 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 00 08 00 00 00  38 00 00 00 00 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 00 40 00 00 00  00 08 00 00 00 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 00 00 20 00 00  00 30 00 00 e0 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 00 00 50 00 00  00 70 00 00 00 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 00 00 c0 00 00  00 10 00 00 00 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 80 00 e0 00 00  00 00 04 00 00 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 80 00 00 05 00  ff ff ff ff 00 00 00 00  |................|
    00000530  00 00 00 00 05 00 00 00  00 00 00 00 00 00 00 00  |................|

To extract say the kernel part to a separate file, we note the offset sector is 00 20 00 00 = 0x00002000, and the number of sectors is 00 30 00 00 = 0x00003000 (remember, least significant byte is listed first!).  Then,
    dd if=Firmware.img of=kernel.bin bs=512 skip=$[0x2000] count=$[0x3000]
Similarly for the other descriptors.

To replace it with file kernel.new, we first create a copy of the original image,
    cp Firmware.img firmware.new
overwrite the old content with zeroes (just in case the new one is shorter),
    dd if=/dev/zero of=firmware.new bs=512 seek=$[0x2000] count=$[0x3000]
noting that this time we use seek instead of skip, because skip= sets input offset, and seek= sets output offset.
Then, assuming the new kernel.new is not too long, we just insert it in:
    dd if=kernel.new of=firmware.new bs=512 seek=$[0x2000]
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 squash
    dev=$(sudo losetup --find --show firmware.new -o $[0x5000*512] --sizelimit $[0x7000*512]) && echo "Using device $dev"
    sudo mount $dev squash
You can now read and write to the squash filesystem under directory squash.  Note that the firmware.new file gets modified when you access and modify the contents.  You can mount it read-only adding -r flag to losetup (sudo losetup -r --find --show ...) and -o ro to mount (sudo mount -o ro $dev squash), in which case the original file is not modified.

When you are done, just run
    sudo umount squash
    sudo losetup -d $dev
which also makes sure all modifications (unless mounted read-only) are committed to the firmware.new file.

The resource descriptor 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 00 0b 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 00 0c 00 00 00  e1 77 01 00 00 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 00 c8 00 00 00  b6 74 00 00 00 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 00 03 01 00 00  b6 74 00 00 00 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 00 3e 01 00 00  b6 74 00 00 00 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 00 79 01 00 00  b6 74 00 00 00 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 00 b4 01 00 00  b6 74 00 00 00 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 00 ef 01 00 00  b6 74 00 00 00 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 00 2a 02 00 00  b6 74 00 00 00 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 00 65 02 00 00  36 b4 04 00 00 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 00 c0 04 00 00  d8 4a 00 00 00 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 00 e6 04 00 00  8a 73 00 00 00 00 00 00  |.........s......|

Note that the red sector number is offset to the starting sector, 0xc000, but the green is 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=$[0x177e1]
and indeed it is.  For example, we can also extract the logo_kernel.bmp via
    dd if=Firmware.img of=logo_kernel.bmp bs=1 skip=$[(0xc000+0x04c0)*512] count=$[0x4ad8]
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=$[0x4ad8]
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.
« Last Edit: August 15, 2021, 05:29:52 pm by Nominal Animal »
 
The following users thanked this post: Logan


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf