Yet another reverse engineering blog

Friday, December 21, 2007

Hacking the Kindle part 3: root shell and runtime system

Root Shell

After I downloaded and extracted the root fs image, I quickly ran the /etc/shadow file though John the Ripper. In a moment it displayed the root password: "fiona" (which is the codename for the Kindle, by the way). Alas, it didn't work when I tried entering into console. Also, adding "init=/bin/sh" or "single" to the kernel boot arguments didn't work either.
So I started to poke around with the firmware update and after some time was able to run a script which mounted the read-write part of root filesystem and dumped the /etc/shadow from it. Unsurprisingly, it had a different password hash. Apparently the root password is changed somewhere before shipping to the end user. So I quickly adapted the script to replace the shadow file on the device with the original one.
You can find that implementation in this update maker zip.

After replacing the shadow file and a reboot, I was able to get in.

Output of some commands.

[root@kindle root]#ls -la /
drwxr-xr-x 2 root root 592 Oct 30 2007 bin
drwxr-xr-x 1 root root 0 Jan 1 00:00 dev
lrwxrwxrwx 1 root root 7 Oct 30 2007 etc -> opt/etc
drwxr-xr-x 2 root root 3 Oct 30 2007 home
drwxr-xr-x 2 root root 3 Oct 30 2007 initrd
drwxr-xr-x 2 root root 586 Oct 30 2007 lib
lrwxrwxrwx 1 root root 11 Oct 30 2007 linuxrc -> bin/busybox
drwxr-xr-x 5 root root 34 Oct 30 2007 mnt
drwxr-xr-x 10 root root 1024 Nov 5 2007 opt
dr-xr-xr-x 101 root root 0 Jan 1 15:42 proc
drwxr-xr-x 2 root root 506 Oct 30 2007 sbin
drwxr-xr-x 10 root root 0 Jan 1 15:42 sys
drwxrwxrwx 5 root root 0 Jan 1 15:44 tmp
drwxr-xr-x 10 root root 95 Oct 30 2007 usr
drwxr-xr-x 2 root root 55 Oct 30 2007 var

[root@kindle root]# mount
devfs on /dev type devfs (rw)
/dev/bml0/6 on / type squashfs (ro)
/dev/stl0/8 on /opt type ext3 (rw,sync,noatime,nodiratime)
/proc on /proc type proc (rw,nodiratime)
sysfs on /sys type sysfs (rw)
devfs on /dev type devfs (rw)
usbdevfs on /proc/bus/usb type usbdevfs (rw)
devpts on /dev/pts type devpts (rw)
tmpfs on /tmp type tmpfs (rw)
/dev/bml0/7 on /mnt/dc type squashfs (ro)

[root@kindle root]# ps -A f
1 ? S 0:01 [swapper]
2 ? SN 0:00 [ksoftirqd/0]
3 ? S< 0:00 [events/0]
4 ? S< 0:00 \_ [khelper]
20 ? S< 0:10 \_ [kblockd/0]
87 ? S 0:02 \_ [pdflush]
89 ? S< 0:00 \_ [aio/0]
86 ? S 0:00 \_ [pdflush]
10 ? S 0:00 [sleepd]
33 ? S 0:00 [khubd]
88 ? S 0:00 [kswapd0]
676 ? S 0:12 [voltd]
678 ? S 0:02 [pnlcd_animate]
681 ? S 0:00 [kseriod]
710 ? S 0:00 [wantph]
709 ? S 0:00 [wanend]
721 ? S 0:00 [mmcdd]
727 ? S 0:00 [hpdetd]
740 ? Ss 0:00 init
1116 tts/2 Ss 0:00 \_ -sh
2344 tts/2 R+ 0:00 \_ ps -A f
831 ? S 0:00 [kjournald]
884 ? S 0:03 /sbin/syslogd -m 0 -b 1 -S -s 250
887 ? S 0:01 /sbin/klogd
976 ? S 0:00 [eink_fb_apt]
974 ? S 0:04 [eink_fb_udt]
975 ? S 0:00 [eink_fb_sst]
1023 ? S 0:07 [f-s-gadget]
1024 ? S 0:00 [f-s-activity]
1063 ? S 0:00 [wdtpmd]
1071 ? S 0:00 /usr/sbin/watchdogd -k 9 -t 30
1079 ? S 0:00 /usr/sbin/netwatchd -d 20 -t 5 -p
1086 ? S 0:03 /usr/sbin/nomkd -v 80 -r 44 -d 23 cvm
1092 ? S 0:00 crond -l 9 -c /etc/crontab
1097 ? S 0:00 /bin/sh /usr/sbin/tphmonitor
1101 ? S 0:00 \_ /usr/sbin/tphserver -f
1119 ? S 0:00 /bin/sh /usr/sbin/execmonitor
1128 ? S 0:00 \_ /usr/sbin/execserver
1123 ? S 0:00 /bin/sh /opt/amazon/ebook/bin/run_framework
1169 ? S 0:00 \_ /bin/sh /opt/amazon/ebook/bin/
1173 ? SL 0:18 \_ /usr/java/bin/cvm -Xmx16m -Dsun.boot.library.path=/opt/usr/java/lib:/usr/java/lib -cp :/opt/amazon/ebook/lib/MobiCore-impl.jar:/opt/amazon/ebook/lib/MobipocketCoreReader.jar:/opt/amazon/ebook/lib/ReaderSDK.jar:/opt/amazon/ebook/lib/SearchSDK.jar:/opt/amazon/ebook/lib/framework-api.jar:/opt/amazon/ebook/lib/framework-impl.jar:/opt/amazon/ebook/lib/jdbm.jar:/opt/amazon/ebook/lib/json.jar:/opt/amazon/ebook/lib/kxml2.jar:/opt/amazon/ebook/lib/xyml.jar:/opt/amazon/ebook/booklet/AudiblePlayer.jar:/opt/amazon/ebook/booklet/AudioPlayer.jar:/opt/amazon/ebook/booklet/Browser.jar:/opt/amazon/ebook/booklet/ContentManager.jar:/opt/amazon/ebook/booklet/Demo.jar:/opt/amazon/ebook/booklet/Experimental.jar:/opt/amazon/ebook/booklet/Home.jar:/opt/amazon/ebook/booklet/MobiReader.jar:/opt/amazon/ebook/booklet/PictureViewer.jar:/opt/amazon/ebook/booklet/PrefBooklet.jar:/opt/amazon/ebook/booklet/Search.jar:/opt/amazon/ebook/booklet/XymlBooklet.jar:/opt/amazon/ebook/booklet/msp.jar:/opt/usr/java/lib/libjnisystem.jar -Ddebug=1 -Dcheck_comm_stack=true -Dhttp.keepalive.timeout=60000 -Dhttp.maxConnections=16 -Dallow_demo=false -Dawt_fb_enable=0 -Dextkeyboard=false -Dconfig=/opt/amazon/ebook/config/framework-unix.conf -DENABLE_SEARCH_INDEXING_THREAD=true -Dprintdebugtime=false
(around 30 cvm copies skipped)
2298 ? S 0:00 [mmcqd]

As you can see, /opt is writable and so is /etc which points to it. On factory reset, the writable partition is populated from /usr/default/opt.tar.gz file.

Here's the full listing of the filesystem:

Bonus content

The main GUI and most of the back-end code is written in Java. The framework is quite elaborate and can be extended with extra "booklets".
After spending some time investigating it with JAD, I found some undocumented shortcuts, features and easter eggs. Here's a more or less complete list.

Picture viewer

I'm not sure why Amazon didn't make it public (maybe because paging is kinda slow), but there is a basic picture viewer in Kindle.
To activate it:
1) make a folder called "pictures" in the root of Kindle drive or SD card. Kindle also checks for "dcim" made by cameras.
2) put your pictures for a single "book" into a folder inside that. The subfolder name will be used as the "book" name. Supported formats are jpg, png, gif.
3) in Home screen press Alt-Z. A new "book" should appear. Open it to view your pictures.
4) In the local menu you can toggle dithering, resize to fit and full screen mode.

Keyboard shortcuts

Various undocumented/underdocumented keyboard shortcuts. I italicized most interesting ones.

Global keys

Alt-Shift-R reboot Kindle
Alt-Shift-. restart GUI
Alt-Shift-G make screenshot
due to an implementation bug, screenshots can only be stored on SD card, not the main storage. A gif file is saved in the card root.
Shift-Sym start demo
Enabled only if allow_demo=true is passed on the Java commandline. Needs a special demo script present on the SD card.


Alt-Shift-M Minesweeper
Alt-Z rescan picture directories
Alt-T show time


Alt-B toggle bookmark
Alt-T spell out time
Alt-0 enable/disable slideshow
Alt-1 start slideshow (if enabled)
Alt-2 stop slidehow
Alt-PageForward/PageBackward go to next/prev annotation or one "chunk" (1/20th of a book) forward or backward


411 show diagnostics data
511 run loopback call test
611 diagnostic data service call
126 Lab126 team members

Font List

J show/hide justification options

Picture viewer

Alt-Shift-0 set current picture as screensaver
F toggle fullscreen mode


I,J,K,L up,left,down,right
M mark mine
R restart
Space open cell
Scroll move cursor up/down
Alt-Scroll move cursor left/right
H return to Home screen

Text input

Alt-Backspace clear all
Alt-H/Alt-J move cursor
(the following don't work in search field for some reason)
Alt-6 ?
Alt-7 ,
Alt-8 :
Alt-9 "
Alt-0 '


It seems there is a location capability (GPS?) in the CDMA module. I cannot check it as I'm not in USA but the following shortcuts are programmed inside the browser.
Alt-1 show current location in google maps
Alt-2 find gas station nearby
Alt-3 find restaurants nearby
Alt-5 find custom keyword nearby
Alt-D dump debug info to the log and toggle highlight default item
Alt-Z toggle zone drawing and show log

Audio Player

Alt-F next
Alt-P play/stop

Search commands

These command work in the search field. You can enter only beginning of the command if that's enough for it to be unique.

Public commands (always available)


Semiprivate (available but not mentioned in @help)

;dumpMessages dump current debug log into the "documents" directory
;debugOn set log level=2 and enable private commands
;debugOff set log level=1 and disable private commands

Private commands
Note: following commands are clearly not intended for end users. Some of them may damage your Kindle and void your warranty. Enter at your own risk.

`help list private commands
`7777 set version to TOPmk-xyz-77770 (to disable OTA updates?)
`voltLog <1|0> enable/disable voltage table debug
`batteryLoggingDelay set battery logging delay (in seconds)
`pppStop close WAN PPP connection
`allocate [MB]

Thursday, December 20, 2007

Hacking the Kindle part 2: bootloader and firmware updates


The Kindle uses Das U-Boot bootloader. To break into the interactive shell, just press any key right afer reset.
"help" gives a list of available commands

KINDLE> help
? - alias for 'help'
badblocks - print OneNAND bad block info
base - print or set address offset
bbm - BBM sub-system
bdinfo - print Board Info structure
boot - boot default, i.e., run 'bootcmd'
bootd - boot default, i.e., run 'bootcmd'
bootf - boot from various options
bootm - boot application image from memory
cmp - memory compare
coninfo - print console devices and information
cp - memory copy
crc32 - checksum calculation
dcache - enable or disable data cache
diags - execute the User Diagnostics from OneNAND
dsleep - sleep USB device controller
dwake - wake USB device controller
echo - echo args to console
erase - erase FLASH memory
exit - exit script
factory - string [lock] [LLL_RR_PP]
fatinfo - print information about filesystem
fatload - load binary file from a dos filesystem
fatls - list files in a directory (default /)
fb - framebuffer subsystem
flinfo - print FLASH memory information
gain - displays/sets the gain value
go - start application at address 'addr'
help - print online help
hsuspend - suspend the 1761 USB host controller
hwake - wake USB host controller
icache - enable or disable instruction cache
iminfo - print header information for application image
itest - return true/false on integer compare
keys - prints out hex values from device keyboard until console key is pressed
kindle - print info about Kindle's revision
load - load OneNAND page into DataRAM
loadb - load binary file over serial line (kermit mode)
loads - load S-Record file over serial line
loop - infinite loop on address range
loopw - infinite write loop on address range
mbboot - boot bootloader from MMC/SC card
md - memory display
mdc - memory display cyclic
mkboot - boot kernel & initrd from MMC/SD card
mm - memory modify (auto-incrementing)
mmcinit - init mmc card
mtest - simple RAM test
mw - memory write (fill)
mwc - memory write cyclic
nand - NAND sub-system
nboot - boot from NAND device
nm - memory modify (constant address)
ohms - calculates board resistance
onenand - print OneNAND register info
opamp - displays/sets the op-amp offset value
otp - dump/read/write OneNAND OTP
printenv- print environment variables
protect - enable or disable FLASH write protection
reboot - alias of reset to match kernel
reset - perform RESET of the CPU
run - run commands in an environment variable
rve - displays/sets the rve (reference voltage error) value
saveenv - save environment variables to persistent storage
serial - set/display board serial number in OTP
setenv - set environment variables
sleep - delay execution for some time
snuz - put PXA to sleep
test - minimal test like /bin/sh
update - update sub-system (updates images from MMC/SD card to flash)
usb_init - init USB host controller
version - print monitor version
write - write DataRAM buffer to OneNAND page

Let's see what we can do with bbm command

KINDLE> ? bbm
bbm format
- format device
bbm open
- open device
bbm eraseall
- erase all blocks
bbm erase 'start' 'end'
- erase blocks from 'start' to 'end'
bbm load image 'id' ['start']
- load image from partition 'id' into RAM;
image is loaded into RAM at location 0xA2000000
or into 'start' if 'start' is specified (in hex)
bbm save image 'id' ['start'] 'size'
- save image of 'size' to partition 'id';
image should be loaded into RAM at 0xA2000000
or into 'start' if 'start' is specified (in hex)

- Partition Info -
bbm show partition
- show partition information
bbm save partition
- save partition information
bbm del partition
- delete last partition
bbm add partition 'id 'attr' 'blocks'
- add partition 'id' of type 'attr' and of size 'blocks'
KINDLE> bbm show partition
id : Bootloaders, Diagnostics (3)
attr : RW (1)
first_blk : 0 (0x00000000)
no_blks : 12 (1.5 MB)
id : Standard Kernel (17)
attr : RW (1)
first_blk : 12 (0x00180000)
no_blks : 12 (1.5 MB)
id : Recovery Kernel (16)
attr : RW (1)
first_blk : 24 (0x00300000)
no_blks : 12 (1.5 MB)
id : Standard Initrd (15)
attr : RW (1)
first_blk : 36 (0x00480000)
no_blks : 10 (1.3 MB)
id : Recovery Initrd (14)
attr : RW (1)
first_blk : 46 (0x005C0000)
no_blks : 10 (1.3 MB)
id : Read-only Root Filesystem (8)
attr : RW (1)
first_blk : 56 (0x00700000)
no_blks : 96 (12 MB)
id : Default Content (9)
attr : RW (1)
first_blk : 152 (0x01300000)
no_blks : 120 (15 MB)
id : Read/Write Root Filesystem (10)
attr : RW (1)
first_blk : 272 (0x02200000)
no_blks : 144 (18 MB)
id : Userstore (11)
attr : RW (1)
first_blk : 416 (0x03400000)
no_blks : 1584 (198 MB)
id : Environment Variables (4)
attr : RW (1)
first_blk : 2000 (0x0FA00000)
no_blks : 2 (256 KB)

The diagnostics U-Boot image is stored in the first partition.
There are two kernels, standard and recovery with corresponding ramdisks. Recovery kernel is used for firmware update.
There is a read-only root filesystem and read-write part. There is a partition with default content used for factory reset.
Then there is a userstore partition, which is visible as mass storage drive over USB.

There were no commands to copy data from flash to SD/MMC, but I could load flash partitions into memory and dump that.

KINDLE> bbm load image 3
Loading partition "Bootloaders, Diagnostics" into 0xA2000000... Success
KINDLE> base a2000000
KINDLE> md.b 0 100
a2000000: 4e 69 63 6b ff ff ff ff ff ff ff ff ff ff ff ff Nick............
a2000010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
a2000020: 0e 00 00 ea 18 f0 9f e5 2c f0 9f e5 18 f0 9f e5 ........,.......
a2000030: 18 f0 9f e5 18 f0 9f e5 18 f0 9f e5 18 f0 9f e5 ................
a2000040: 60 00 00 a2 20 01 00 a2 80 01 00 a2 e0 01 00 a2 `... ...........
a2000050: 40 02 00 a2 a0 02 00 a2 00 03 00 a2 40 03 00 a2 @...........@...
a2000060: 00 00 0f e1 80 00 c0 e3 00 f0 29 e1 7c 00 9f e5 ..........).|...
a2000070: 21 0a 40 e2 02 0c 40 e2 02 0a 40 e2 0c d0 40 e2 !.@...@...@...@.
a2000080: 70 00 9f e5 70 10 9f e5 00 20 a0 e3 00 20 80 e5 p...p.... ... ..
a2000090: 04 00 80 e2 01 00 50 e1 fb ff ff 1a e6 80 00 ea ......P.........
a20000a0: 00 00 a0 40 00 00 00 00 00 00 00 00 00 00 00 00 ...@............
a20000b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
a20000c0: 28 00 1f e5 18 10 90 e5 01 10 81 e3 18 10 80 e5 (...............
a20000d0: 10 10 90 e5 02 1b 81 e2 02 1b 81 e2 0c 10 80 e5 ................
a20000e0: fe ff ff ea ef be ad de ef be ad de ef be ad de ................
a20000f0: 00 00 00 a2 20 00 00 a2 1c 56 06 a2 98 52 4a a3 .... ....V...RJ.

(Nick probaly refers to Nick Vaccaro of Lab126, who apparently was one of the main Kindle software developers. Hi Nick!)

Due to either my cable or inconsistent terminal settings, any dump of over 256 bytes was returning only some bytes in the beginning and some in the end. So I had to conjure up a little Python script to send dump commands in chunks of 256 bytes, parse the output and write it to a file. It took a few hours per partition, but in the end I was able to dump what I needed. In the initrd image of the recovery kernel I found scripts that performed firmware update and so I could reverse the update file format.

Firmware updates

Firmware update can be performed both over-the-air and from the SD card or mass storage partition.

The firmware update file should match the mask "update*.bin" and reside in the root of the userstore partition (Kindle's USB drive) or SD/MMC card. There should be only one such file present. The file consists of a header and scrambled .tar.gz file with update files.

offset size  value
0 4 signature (OTA: "FC02", manual: "FB01")
4 4 fromVersion (minimal version to update)
8 4 toVersion (maximal version to update)
0C 2 deviceCode (number in 3rd and 4th characters of the serial, i.e. 01)
0E 1 updateOptional (seems unused)
0F 1
10 32 scrambled md5 hash string of the tgz
20000 ? scrambled tgz with update files

Version value is made from the kindle version string. In my unit, it's 292-Kindle-012138 and the version value is 121380292 (12138*10000 + 292).

The manual update doesn't check any fields except signature and md5.

Scramble algorithm:
byte = rol(byte,4)^0x7A;

byte = rol(byte^0x7A,4);

The tgz should contain a text file matching the mask "update*.dat". Its lines have the following format:
id md5 filename block_count display_name

id is the ID of the flash partition to write to. I know the following numbers:
6 base RO fs (squashfs image)
7 default content (squashfs)

block_count is the number of 128K blocks to flash (see bbm show partition output above).

If id is 129, the file is considered a shell script and is executed.
If id is 128, the md5 is checked but nothing is done with the file.

I made a small Python script to assemble an update file with all checksums calculated automagically. It will be included in the next post.

To do a manual update, first put the update bin in the root of Kindle's flash drive or SD card. Then hold Home button while resetting the Kindle. In a while you will see the following menu:
Service menu
2 "Firmware Reset" clears all user-specific data and settings and returns the Kindle to factory state. I'm not sure if it will be usable without extra initialization by Amazon technicians.
3 "Exit" starts normal boot process.
0 (not shown) starts the diagnostics bootloader. Interaction is done mostly over the console, you won't see much on the Kindle screen.
1 "Firmware update" starts the manual update process.

Hacking the Kindle part 1: getting the console

From reading the sources published by Amazon, it was clear that Kindle has a console running at least during boot. And there was an unconnected port available from outside.
Debug Port
Logically, the console would be accessible there. I salvaged a flat cable with a connector from my Rio Karma dock and stripped extra conductors to bring the pin count down to 20. Next I needed a TTL-RS232 converter. I almost bought one from EBay, but then realized that I already have one in the form of a data cable for my Samsung GSM phone. I stripped the phone connector, spent some time to discover the pinout of the cable, and was ready to search for the console. With a multimeter I found grounded pins of the debug connector so I knew which ones I can skip. I then started PuTTY, set port parameters to 115200/8n1 (gleaned from source code), connected ground of the cable to the shield, and started connecting RX of the cable to every pin in order, resetting the Kindle each time. Eventually I was able to see the output of the bootloader.

check_recovery: shift-<r>ecover, shift-<u>pdate, shift-</> reset...
normal boot...

U-Boot 1.1.2 (Oct 29 2007 - 16:35:25)

*** Welcome to Kindle ***

With a bit of solder I fixed it, and then did the same with the TX wire while pressing some keys on the keyboard. As I was at the login prompt at this point, once I had the correct pin I could see the echo in the terminal. Unsurprisingly, the RX pin was right next to the TX.
I wasn't able to solder cable to the connector without shorting (the pins are 0.5mm apart!), so in the end I removed most of the pins, soldered short wires to the removed pins and inserted those I needed back into the connector.
The final pinout:
12 TX (connect PC's RX here)
11 RX (connect PC's TX here)
10 GND (also 7 and 3)

There are probably JTAG pins too, but those are a bit harder to find by trial and error. Also, I don't have a JTAG cable.

In case you want to make your own connector, you'll need:
1) a 20-pin 0.5mm pitch flat flex cable with a connector. Digikey seems to have some.
2) a TTL-RS232 or TTL-USB converter. For the former, make sure you get one that can handle 3.3V levels (i.e. MAX232 analog won't do, you'll need MAX3232 or similar). For the latter, probably any will do.

Friday, December 14, 2007

Sony Reader PRS-505 disassembly

I liked looking into the Reader much more than the Kindle.

Sony Reader PRS-505 disassembly

Kindle disassembly and internals

I know that RapidRepair published their guide recently but I found it not detailed enough, so I tried to take many pictures when disassembling my own Kindle. I hope this will be interesting.

Disassembly with step-by-step photos

Detailed internals with close-ups of various chips

Check photo notes for chip details.

P.S. After I uploaded my photos, I found this post in PC-Doctor blog. He has some notes about the chips used.

Wednesday, December 12, 2007

Mobipocket books on Kindle

We've known for some time already that Amazon's AZW files are actually Mobi files, but Amazon didn't share Kindle's Mobi PID which would allow one to buy encrypted Mobi books for Kindle.
Well, I've discovered the algorithm used to generate the PID and was able to use it on Fictionwise, but there was another catch. AZW files have a flag set in the DRM info which is not present in books bought from other vendors. After fixing that, I could read the book on Kindle.

Linked archive includes two Python scripts. generates Mobi PID from Kindle serial number. You can then add this PID at a Mobipocket vendor site and redownload books with Kindle's PID enabled. It's possible that some vendors will refuse this PID, as it has an asterisk in place of the traditional dollar sign (Fictionwise works fine). "fixes" a Mobi book so that it can be read on Kindle. It should already include Kindle's PID (which you need to specify too). The script will output the fixed book with .azw extension.

Kindle Mobipocket tools 0.1 0.2

Thursday, December 6, 2007

Kindle boot log

So the Kindle has a console running. Lots of interesting info here...

U-Boot 1.1.2 (Oct 29 2007 - 16:35:25)

*** Welcome to Kindle ***

U-Boot code: A3F00000 -> A3F3FA6C BSS: -> A3F77850

REV2 Bootloader: Kindle 400 MHz REV2 Board, Revision #9
Boot block version: 20070525
Checksum: 0x9F132881
Buildrev: 292-Kindle-012138
IOC Firmware Version #63

RAM Configuration:
Bank #0: 0xA0000000 64 MB
Flash Configuration:
Bank #0: 0x00000000 128 MB
Bank #1: 0x04000000 128 MB
NAND Configuration:
256 MB
BML_Init success
BML_Open success
**************** device info *******************
nPgsPerBlk = 64
nSctsPerPg = 4
nNumOfUsBlks = 2002
bml open success
pi->nNumOfPartEntry: 10Success loading partition
load env done
usb_force_sleep: hw_flags.disable_pdc_suspend = 0
waiting for CLKREADY
Mode reg = 0x00000000
soft resetting device controller
power switch is on
Update battery level: >3720mV
Boot battery level: >3680mV
Alert battery level: >3600mV
Current battery level: 3911mV (bootable)

Loading partition "Standard Kernel" into 0xA2000000... Success
Loading partition "Standard Initrd" into 0xA2E20000... Success

iminfo a2000000 a2e20000

## Checking Image at a2000000 ...
Image Name: Linux-2.6.10-lab126
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 1236608 Bytes = 1.2 MB
Load Address: a0008000
Entry Point: a0008000
Verifying Checksum ... OK

## Checking Image at a2e20000 ...
Image Name: initrd_fs.gz
Image Type: ARM Linux RAMDisk Image (gzip compressed)
Data Size: 689536 Bytes = 673.4 KB
Load Address: 00000000
Entry Point: 00000000
Verifying Checksum ... OK

bootm a2000000 a2e20000
## Booting image at a2000000 ...
Image Name: Linux-2.6.10-lab126
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 1236608 Bytes = 1.2 MB
Load Address: a0008000
Entry Point: a0008000
Verifying Checksum ... OK
## Loading Ramdisk Image at a2e20000 ...
Image Name: initrd_fs.gz
Image Type: ARM Linux RAMDisk Image (gzip compressed)
Data Size: 689536 Bytes = 673.4 KB
Load Address: 00000000
Entry Point: 00000000
Verifying Checksum ... OK

Starting kernel ...

Uncompressing Linux................................................................................ done, booting the kernel.
Linux version 2.6.10-lab126 (build@lab126-build) (gcc version 3.4.2) #1 Mon Oct 29 16:38:09 PST 2007
CPU: XScale-PXA255 [69052d06] revision 6 (ARMv5TE)
CPU: D VIVT undefined 5 cache
CPU: I cache: 32768 bytes, associativity 32, 32 byte lines, 32 sets
CPU: D cache: 32768 bytes, associativity 32, 32 byte lines, 32 sets
Machine: Fiona Platform
Fiona Board Resistance : 150 mOhms
Memory policy: ECC disabled, Data cache writeback
Memory clock: 99.53MHz (*27)
Run Mode clock: 398.13MHz (*4)
Turbo Mode clock: 398.13MHz (*1.0, inactive)
Built 1 zonelists
Kernel command line: console=ttyS2,115200n8 root=/dev/rd/0 initrd=/linuxrc rw reboot=hard
reserved 4096 bytes at a3fff000 for boot globals
reserved 122880 bytes at a3fe1000 for framebuffer
PID hash table entries: 512 (order: 9, 8192 bytes)
Console: colour dummy device 80x30
Dentry cache hash table entries: 16384 (order: 4, 65536 bytes)
Inode-cache hash table entries: 8192 (order: 3, 32768 bytes)
Memory: 64MB = 64MB total
Memory: 61456KB available (2070K code, 357K data, 100K init)
Mount-cache hash table entries: 512 (order: 0, 4096 bytes)
CPU: Testing write buffer coherency: ok
checking if image is isn't (no cpio magic); looks like an initrd
Freeing initrd memory: 673K
NET: Registered protocol family 16
Recalculated watermarks:
wan: 3603 (was 3603)
low: 3586 (was 3586)
critical: 3536 (was 3536)
REV2 board detected
Power transition failed err=-5 for pnlcd video display class
SCSI subsystem initialized
usbcore: registered new driver usbfs
usbcore: registered new driver hub
PXA CPU frequency change support initialized
NetWinder Floating Point Emulator V0.97 (extended precision)
FPOW: Fiona Power Management Driver v0.54
squashfs: version 3.0 (2006/03/15) Phillip Lougher
devfs: 2004-01-31 Richard Gooch (
devfs: boot_options: 0x1
Initializing Cryptographic API
WATER: Disabling IOC voltage reporting, initing our own timer...
Battery Voltage: 3881 mVolts (raw: 3854 mVolts)
V[1] : ~3881~
A[1] : ~181~
T[1] : ~26~
OP Amp Offset : Uninitialized
Op Amp Gain Correction : Uninitialized
Voltage Reference Correction Factor : 1015 / 1000
IOC: Fiona IOC Driver v1.0, IOC FW version #63
Battery: 3.911 Volts, 26 Degrees C, 164 mA Draw, DISCHARGING, 72%
PNLCD: Fiona PNLCD Driver v0.94
KEYBOARD: Fiona Keyboard Driver v1.0
ppoke driver: Major: 207 Rev. 4 Kernel v.2.6.10-lab126
/proc/peek initialized.
/proc/poke initialized.
ttyS0 at MMIO 0x40100000 (irq = 15) is a FFUART
ttyS1 at MMIO 0x41600000 (irq = 0) is a HWUART
ttyS2 at MMIO 0x40700000 (irq = 13) is a STUART
io scheduler noop registered
RAMDISK driver initialized: 8 RAM disks of 4096K size 1024 blocksize
loop: loaded (max 8 devices)
PPP generic driver version 2.4.2
wan: AnyDATA DTEV WAN module driver 0.8.4
i2c /dev entries driver
i2c_adapter i2c-0: found device 0x1a
Advanced Linux Sound Architecture Driver Version 1.0.6 (Sun Aug 15 07:17:53 2004 UTC).
soc: version 0.7
wm8971: WM8971 audio codec 0.2.3
soc: WM8971 <-> pxa2xx-i2s mapping ok
ALSA device list:
#0: WM8971 Portable Codec (SoC)
NET: Registered protocol family 2
IP: routing cache hash table of 512 buckets, 4Kbytes
TCP: Hash tables configured (established 4096 bind 8192)
NET: Registered protocol family 1
NET: Registered protocol family 17
RAMDISK: Compressed image found at block 0
VFS: Mounted root (ext2 filesystem).
Mounted devfs on /dev
starting initrd...
eink_fb: /dev/fb/0 frame buffer device, using 471K for video memory
eink_fb: Apollo Display Driver 1.00
xsr: module license 'Samsung Proprietary' taints kernel.
Samsung XSR Core 1.2.1-lab126-2
elevator: using noop as default io scheduler
Samsung STL 1.2.1-lab126-2
Samsung RFS 1.2.1-lab126-2
kjournald starting. Commit interval 5 seconds
EXT3 FS on stl8, internal journal
EXT3-fs: recovery complete.
EXT3-fs: mounted filesystem with ordered data mode.
starting init...
dosfsck 2.11-lab126 (7 Dec 2006)
dosfsck 2.11-lab126, 7 Dec 2006, FAT32, LFN
Checking we can access the last sector of the filesystem
Boot sector contents:
System ID "mkdosfs"
Media byte 0xf8 (hard disk)
512 bytes per logical sector
2048 bytes per cluster
32 reserved sectors
First FAT starts at byte 16384 (sector 32)
2 FATs, 32 bit entries
386048 bytes per FAT (= 754 sectors)
Root directory start at cluster 2 (arbitrary size)
Data area starts at byte 788480 (sector 1540)
96416 data clusters (197459968 bytes)
8 sectors/track, 1 heads
0 hidden sectors
387204 sectors total
Checking for unused clusters.
Checking free cluster summary.
/dev/stl0/9: 293 files, 7117/96416 clusters
mount: Mounting /dev/mmc/blk0/part1 on /mnt/mmc failed: No such file or directory
system: processing module dependencies
system: loading module pdc
pdc: version 1.0
pdc: IRQ 26
system: loading module usbserial
drivers/usb/serial/usb-serial.c: USB Serial support registered for Generic
usbcore: registered new driver usbserial_generic
usbcore: registered new driver usbserial
drivers/usb/serial/usb-serial.c: USB Serial Driver core v2.0
system: initializing eInk driver for rootfs use
eink_fb: /dev/fb/0 frame buffer device, using 588K for video memory
eink_fb: Apollo Display Driver 1.00
eink_fb: write_eink_which, size=50327
eink_fb: curr_count=0 write_size=50327
eink_fb: done, status=1
1+0 records in
1+0 records out
system: initializing random number generator
system: initializing USB gadget driver
g_file_storage gadget: controller 'pdc' not recognized
g_file_storage gadget: File-backed Storage Gadget, version: 28 July 2004
g_file_storage gadget: Number of LUNs=2
system: initializing audio configuration for REV2 or higher
system: initializing audio driver defaults
system: initializing network configuration
system: setting "conservative" processor power mode
system: starting processor watchdog
SA1100/PXA2xx Watchdog Timer: timer margin 60 sec
watchdogd 0.1 Copyright (C) 2007 Lab126, Inc. All rights reserved.
system: starting network connection watchdog
netwatchd 0.5 Copyright (C) 2007 Lab126, Inc. All rights reserved.
system: starting CVM memory watchdog
nomkd 0.7 Copyright (C) 2006-2007 Lab126, Inc. All rights reserved.
system: starting TPH monitor
system: starting tphserver
tphserver 0.2 Copyright (C) 2007 Lab126, Inc. All rights reserved.
system: starting booklet framework

Welcome to Kindle!

kindle login:

Friday, November 23, 2007

Embiid Publishing

Short History

Embiid Publishing was an early e-pub company which started back in 2000. They published some midlist SF and Romance titles, most famous probably being Liaden series by Sharon Lee and Steve Miller. They offered nice prices ($5 in average), and free sampler bundles. At first their books were Windows-only, later they started to offer Rocket format and a book reader for Palm OS. In 2006 the company closed doors, leaving customers with books they could not convert.

The Reader

The Windows reader program could read two formats: UBK and EBK. The former was slightly scrambled but could be read by any reader. The latter was encrypted with a personalized key and could only be read by the personalized reader executable downloadable with the first purchase.
The Reader was written in Delphi and had pretty basic functionality: changeable font, bookmarks, navigation.

File format details

A pseudo-C description of the file header looks like following:
struct EmbiidFile {
/* 00 */ int32 file_seed; //the seed for decrypting header fields
/* 04 */ char type[5]; //file type (encrypted w/ file_seed)

#define FTYPE_UBK "Valid" //non-personalized (text encrypted with file_seed)
#define FTYPE_EBK "EBook" //personalized (text encrypted with user_seed)

/* 09 */ uint32 cover_off; //offset of the cover image (jpeg image)
/* 0D */ uint32 cover_len; //length of the cover image data
/* 11 */ byte version; //format version (encrypted w/ file_seed)


/* 12 */ char title[50]; //book title (encrypted w/ file_seed), space-padded
/* 44 */ char author[954]; //book author (encrypted w/ file_seed), space-padded
/* 3FE */ uint16 nchapters; //number of chapters
/* 400 */ uint32 chap_lens[256]; //chapter lengths
/* 800 */ char book_text[]; //text of the book. UBK: encrypted with file_seed, EBK: encrypted with user_seed

The encryption uses a 1024-byte array to xor the data with. The array is initialized from the seed using a pseudo-random number generator. Here's pseudocode for its generation:
float a = seed/1000.0
for(int i=1;i<0x400;i++)
float b = int(a/127773);
float c = a - b*127773;
a = c*16807 - b*2836;
if (a<0) a+=2147483647;
xor_buf[i] = int(a/2147483647*256)&0xFF;

The decryption uses the file offset of the data to index the array, and it skips bytes that would decrypt to 0x1A (the EOF symbol):

xor_val = xor_buf[file_offset%0x400];
val_out = val_in^xor_val;
if (val_out==0x1A)
val_out = val_in;

While the file_seed is stored directly in the file, user_seed is calculated as Adler32 checksum of a 128-byte user ID, which is stored directly in the personalized EmbiidReader.exe.

t1 = 1;
sum = 0;
i = 0;
t1 = (t1 + user_id[i++]) % 0xFFF1;
sum = (t1 + sum) % 0xFFF1;
while ( i <0x80 );
user_seed = (sum<<16) | t1;

The text of the book uses a small subset of HTML tags for formatting, but the paragraphs are delimited by newlines, not <br> or <p> tags.

Here's a small Python script to convert an Embiid book to HTML. A valid EmbiidReader.exe is necessary to decrypt personalized books.
Google Pages
You will need Python to run it.
Place your books, EmbiidReader.exe and into the same directory and execute from command prompt: <book.ebk>
You should get a <book.html> file with decoded text.

Thursday, November 22, 2007


Hello, visitor.
I am a long-time reverse engineering hobbyist. Most of that time I've been "working for myself" but now I've decided to share some of my findings. My reverse engineering interests include decompilation, file formats and interoperability (and more). I've written a two-part article on Visual C++ reversing for 1, 2.
Right now I'm "into" eBook readers, so the next few posts will probably be on eBook formats.