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.