Reconstructing the FireOS file system
Jonathan Levin, http://NewAndroidBook.com, 03/10/2018
I couldn't resist getting myself another Kindle on Amazon so I could research how far their "FireOS" has evolved. The device came with 5.6.0 installed, and it provides ADB (using the standard Developer Options trick of tapping multiple times on build info), but no public root exists for it (yet). It does strike me as rootable, but part of the process requires reversing Amazon's custom binaries (and there are many of those). Problem - SELinux contexts restrict access to a lot of those. What to do?
Fortunately, there are already posted links to the FireOS filesystem images on XDA-Developers. Grabbing the 5.6.0 filesystem update, I decided to take a look at what it looks like . The file, a ".bin" is actually a zip, and is easily extractable:
morpheus@Zephyr (~/Downloads/kindle) % unzip ../update-kindle-50.5.9.5_user_595457320.bin
Archive: ../update-kindle-50.5.9.5_user_595457320.bin
signed by SignApk
extracting: system.patch.dat
inflating: META-INF/com/amazon/android/check-binary
inflating: META-INF/com/amazon/android/target.blocklist
inflating: META-INF/com/amazon/android/target.build.prop
inflating: META-INF/com/amazon/android/target.system.devicepath
inflating: META-INF/com/amazon/android/target.system.map
inflating: META-INF/com/amazon/android/target.system.map.sha1
inflating: META-INF/com/android/metadata
inflating: META-INF/com/google/android/update-binary
inflating: META-INF/com/google/android/updater-script
inflating: boot.img
inflating: file_contexts
inflating: images/lk.bin
inflating: images/preloader.bin
inflating: images/preloader.hdr0
inflating: images/preloader.hdr1
inflating: images/tz.img
inflating: ota.prop
inflating: system.new.dat
inflating: system.transfer.list
inflating: system/build.prop
inflating: META-INF/com/android/otacert
inflating: META-INF/MANIFEST.MF
inflating: META-INF/CERT.SF
inflating: META-INF/CERT.RSA
The interesting part is, of course, the
morpheus@Zephyr (~/Downloads/kindle) % file system.new.dat
system.new.dat: Linux rev 1.0 ext4 filesystem data, UUID=57f8f4bc-abf4-655f-bf67-946fc0f9f25b (extents) (large files)
Moving over to a Linux and trying to mount it, however, we get errors:
[root@simulacrum hgfs]# mount -o loop /mnt/hgfs/Android/system.new.dat /mnt1 mount: wrong fs type, bad option, bad superblock on /dev/loop1, missing codepage or helper program, or other error In some cases useful info is found in syslog - try dmesg | tail or so. [root@simulacrum hgfs]# dmesg [17784.635418] EXT4-fs (loop1): bad geometry: block count 413255 exceeds size of device (316916 blocks)
So this is obviously a sparse image of some type, since its size is smaller than the actual filesystem size. Simple math reveals the blocksize to be 1,298,087,936 / 316916 = 4,096. That means the actual image file, if extracted, should be 1,692,692,480. Trying my standard image extraction tool (imgtool
), however, lamentably reports it's not a known sparse image. So it's a different format.
Rummaging around in the extracted files, however, we find some clues. Specifically,
morpheus@Zephyr (~/Downloads/kindle) % cat system.transfer.list
3
316916
0
0
erase 2,0,413255
new 56,0,32767,32768,32770,32873,32875,33372,65535,65536,65538,66035,98303,98304,98306,98409,98411,98908,131071,131072,131074,131571,163839,163840,163842,163945,163947,164444,196607,196608,196610,197107,229302,229376,229378,229481,229483,229980,262143,262144,262146,262643,269673,294912,294914,295017,295019,295516,327679,327680,327682,360448,360450,393216,393218,393715,413254
- The "3" up there is probably some version number. Whatever.
- Note "316916". That's the size of our file in blocks.
- Note "erase 2,0,413255". This seems to imply that as a precursor step to installing the system image, the installer needs to erase the existing system. But what? "0,413255" spans the entire disk, because 1,692,692,480 = 413,255 * 4096! This led me to figure out that "2" is just the size of the parameter array.
- This leaves the most important part, which is the "new,56,...". Indeed, 56 parameters follow, but what are the values?
I tried taking a better look at the dd
supports hex block sizes), we have:
morpheus@Zephyr (~/Downloads/kindle) % dd if=system.new.dat bs=0x1000 skip=32767 count=1 of=block32767 morpheus@Zephyr (~/Downloads/kindle) % hexdump -C block32767 00000000 d0 93 01 00 47 4e 06 00 00 00 00 00 00 00 00 00 |....GN..........| 00000010 00 00 00 00 00 00 00 00 02 00 00 00 02 00 00 00 |................| 00000020 00 80 00 00 00 80 00 00 10 1f 00 00 00 00 00 00 |................| 00000030 00 00 00 00 00 00 ff ff 53 ef 01 00 02 00 00 00 |........S.......| 00000040 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 |................| 00000050 00 00 00 00 0b 00 00 00 00 01 01 00 1c 00 00 00 |................| 00000060 42 00 00 00 13 00 00 00 57 f8 f4 bc ab f4 65 5f |B.......W.....e_| 00000070 bf 67 94 6f c0 f9 f2 5b 00 00 00 00 00 00 00 00 |.g.o...[........| 00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
The cryptic values up there: 0x193d0 = 103376 and 0x64e47 = 413255. The latter is most definitely the number of blocks. That alone didn't tell me much, but what did is the first block:
morpheus@Zephyr (~/Downloads/kindle) % hexdump -C system.new.dat| head
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000400 d0 93 01 00 47 4e 06 00 00 00 00 00 04 5f 01 00 |....GN......._..|
00000410 16 8a 01 00 00 00 00 00 02 00 00 00 02 00 00 00 |................|
00000420 00 80 00 00 00 80 00 00 10 1f 00 00 00 00 00 00 |................|
00000430 00 00 00 00 00 00 ff ff 53 ef 01 00 02 00 00 00 |........S.......|
00000440 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 |................|
00000450 00 00 00 00 0b 00 00 00 00 01 00 00 1c 00 00 00 |................|
00000460 42 00 00 00 13 00 00 00 57 f8 f4 bc ab f4 65 5f |B.......W.....e_|
00000470 bf 67 94 6f c0 f9 f2 5b 00 00 00 00 00 00 00 00 |.g.o...[........|
We have almost the exact same values starting at offset 0x400. And that is exactly where the ext2/3/4 superblock is! (Astute readers can see the ext magic, 0xef53, in both outputs - I didn't..). So this means that block 37,267 is a superblock backup!
HOWEVER... The first superblock backup is traditionally at the nicer number of 32,768 (0x8000 really is nicer than 0x7fff, right?). This also happens to be the next entry (third) in the "new" from
All I needed at this point was a quick tool to do this. So, without further ado:
#include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <sys/mman.h> // Linux needs to define this since I can't remember which header it's from: typedef unsigned long uint64_t; typedef unsigned int uint32_t; int list [] = { 0,32767,32768,32770,32873,32875,33372,65535,65536,65538,66035,98303,98304,98306,98409,98411,98908,131071,131072,131074,131571,163839,163840,163842,163945,163947,164444,196607,196608,196610,197107,229302,229376,229378,229481,229483,229980,262143,262144,262146,262643,269673,294912,294914,295017,295019,295516,327679,327680,327682,360448,360450,393216,393218,393715,413254,-1}; int main (int argc, char **argv) { int i = 0; int fd = open (argv[1], O_RDONLY); int out = open ("/tmp/extracted", O_WRONLY | O_CREAT); struct stat stbuf; fstat(fd, &stbuf); char *mmapped = mmap (NULL, stbuf.st_size, PROT_READ, // int prot, MAP_PRIVATE, // int flags, fd, // int fd, 0); // off_t offset); int blockSize = 4096; char *emptyBlock = calloc (blockSize, sizeof(char )); printf("mmapped: %p\n", mmapped); uint32_t fromBlock = list[i]; uint32_t toBlock = list[i+1]; uint32_t padding = 0; uint64_t offset = fromBlock * blockSize; uint64_t inOffset = fromBlock *blockSize; while (fromBlock != -1) { printf ("Writing chunk: From %d to %d\n", fromBlock, toBlock); int rc = 0; while (offset < toBlock *blockSize) { rc = write (out, mmapped + inOffset, blockSize); if (rc < blockSize) { perror ("write");} offset += blockSize; inOffset += blockSize; } fromBlock = list[i+2]; printf("Offset: %lld\n", offset/4096); if (fromBlock == -1) break; printf("Padding from %d till %d\n ", toBlock, fromBlock); padding += (fromBlock - toBlock); while (offset < fromBlock * blockSize) { write (out, emptyBlock, blockSize); offset += blockSize; } toBlock = list[i+3]; i+=2; } printf ("Offset total: %lld\nPadding total: %d\n", offset,padding); // Need to pad last block here since we're one short: write (out, emptyBlock, blockSize); fsync(out); close(out);
morpheus@Zephyr (~/Downloads/kindle) % /tmp/test system.new.dat
mmapped: 0x10ad5c000
Writing chunk: From 0 to 32767
Offset: 32767
Padding from 32767 till 32768
Writing chunk: From 32768 to 32770
Offset: 32770
Padding from 32770 till 32873
Writing chunk: From 32873 to 32875
Offset: 32875
Padding from 32875 till 33372
Writing chunk: From 33372 to 65535
Offset: 65535
Padding from 65535 till 65536
Writing chunk: From 65536 to 65538
Offset: 65538
Padding from 65538 till 66035
Writing chunk: From 66035 to 98303
Offset: 98303
Padding from 98303 till 98304
Writing chunk: From 98304 to 98306
Offset: 98306
Padding from 98306 till 98409
Writing chunk: From 98409 to 98411
Offset: 98411
Padding from 98411 till 98908
Writing chunk: From 98908 to 131071
Offset: 131071
Padding from 131071 till 131072
Writing chunk: From 131072 to 131074
Offset: 131074
Padding from 131074 till 131571
Writing chunk: From 131571 to 163839
Offset: 163839
Padding from 163839 till 163840
Writing chunk: From 163840 to 163842
Offset: 163842
Padding from 163842 till 163945
Writing chunk: From 163945 to 163947
Offset: 163947
Padding from 163947 till 164444
Writing chunk: From 164444 to 196607
Offset: 196607
Padding from 196607 till 196608
Writing chunk: From 196608 to 196610
Offset: 196610
Padding from 196610 till 197107
Writing chunk: From 197107 to 229302
Offset: 229302
Padding from 229302 till 229376
Writing chunk: From 229376 to 229378
Offset: 229378
Padding from 229378 till 229481
Writing chunk: From 229481 to 229483
Offset: 229483
Padding from 229483 till 229980
Writing chunk: From 229980 to 262143
Offset: 262143
Padding from 262143 till 262144
Writing chunk: From 262144 to 262146
Offset: 262146
Padding from 262146 till 262643
Writing chunk: From 262643 to 269673
Offset: 269673
Padding from 269673 till 294912
Writing chunk: From 294912 to 294914
Offset: 294914
Padding from 294914 till 295017
Writing chunk: From 295017 to 295019
Offset: 295019
Padding from 295019 till 295516
Writing chunk: From 295516 to 327679
Offset: 327679
Padding from 327679 till 327680
Writing chunk: From 327680 to 327682
Offset: 327682
Padding from 327682 till 360448
Writing chunk: From 360448 to 360450
Offset: 360450
Padding from 360450 till 393216
Writing chunk: From 393216 to 393218
Offset: 393218
Padding from 393218 till 393715
Writing chunk: From 393715 to 413254
Offset: 413254
Offset total: 1692688384
Padding total: 96338
moving back to Linux for a mount:
[root@simulacrum ~]# mount -o loop /mnt/hgfs/Android/kindleFS /mnt1 [root@simulacrum ~]# cd /mnt1 [root@simulacrum mnt1]# ls -lR .: total 372 drwxr-xr-x. 19 root root 4096 Nov 27 23:20 app drwxr-xr-x. 2 root 2000 4096 Nov 27 23:20 bin -rw-r--r--. 1 root root 7484 Nov 27 23:20 build.prop drwxr-xr-x. 3 root root 4096 Nov 27 23:20 data drwxr-xr-x. 15 root root 4096 Nov 27 23:20 etc lrw-r--r--. 1 root root 15 Nov 27 23:20 fonts -> /mnt/sqfs/fonts drwxr-xr-x. 5 root root 4096 Nov 27 23:20 framework drwxr-xr-x. 8 root root 12288 Nov 27 23:20 lib drwxr-xr-x. 7 root root 8192 Nov 27 23:20 lib64 drwx------. 2 root root 4096 Dec 31 1969 lost+found drwxr-xr-x. 3 root root 4096 Nov 27 23:20 media ./app: total 68 drwxr-xr-x. 3 root root 4096 Nov 27 23:20 AmazonWebView drwxr-xr-x. 4 root root 4096 Nov 27 23:20 Bluetooth drwxr-xr-x. 3 root root 4096 Nov 27 23:20 CertInstaller drwxr-xr-x. 3 root root 4096 Nov 27 23:20 DocumentsUI drwxr-xr-x. 3 root root 4096 Nov 27 23:20 fdrw drwxr-xr-x. 3 root root 4096 Nov 27 23:20 HTMLViewer ... ./xbin: total 4660 -rwxr-xr-x. 1 root 2000 708072 Nov 27 23:20 chkexfat -rwxr-xr-x. 1 root 2000 708032 Nov 27 23:20 chkufsd -rwxr-xr-x. 1 root 2000 59788 Nov 27 23:20 dexdump -rwxr-xr-x. 1 root 2000 1125456 Nov 27 23:20 fsutil -rwxr-xr-x. 1 root 2000 527788 Nov 27 23:20 memalloc -rwxr-xr-x. 1 root 2000 674896 Nov 27 23:20 mkexfat -rwxr-xr-x. 1 root 2000 13856 Nov 27 23:20 showmap -rwxr-xr-x. 1 root 2000 66440 Nov 27 23:20 sqlite3 -rwxr-xr-x. 1 root 2000 1481 Nov 27 23:20 start-ufsd -rwxr-xr-x. 1 root 2000 769732 Nov 27 23:20 test_system -rwxr-xr-x. 1 root 2000 13784 Nov 27 23:20 trapz -rwxr-xr-x. 1 root 2000 83712 Nov 27 23:20 vitals_collection_agent
So off to explore the filesystem in the hopes of rooting this thing :-)
And..
Android Internals Volume II is on track. Aiming for AS SOON AS I'M DONE WITH MOXiI Volume II - which is November. Believe me, I wouldn't be doing all this if I didn't also want to include FireOS aspects in the book as well. Android P just came out in developer preview, and so far I'm seeing only paltry changes. Volume II will be up-to-date when it comes out, I'll also do a Volume I revamp, AND I'll offer both in one book. Stay tuned.
And... (II)
I show a lot of this stuff in Technologeeks' Android Internals Training! There's one opening in Columbia, MD, December 3rd of 2018.