a3nm's blog

Even more Kobo hacking

— updated

I broke my Kobo Touch (the screen was damaged, probably because the device was crushed against something in a bag, interesting to know that you ought to be careful with it), and bought a Kobo Glo (model N613) to replace it. Here is some info about hacks I've done.

Old stuff

You can check my original post for details about what needs to be done at first, I'm just going to allude to it. You need to fake activation in the usual way, though you might need some more clever choices to make it look plausible for the Kobo (caution though, the last column mentioned by those instructions did not exist in my sqlite file). Install the latest firmware, prepare a fake update to activate a telnet daemon, and get root. Install dropbear, edit /etc/hosts. From the contents of /mnt/onboard/.kobo/Kobo/Kobo eReader.conf I feel safer adding the following to my previous list:

0.0.0.0 www.kobobooks.com webstore.kobobooks.com webstore2.kobobooks.com
0.0.0.0 secure.kobobooks.com ecimages.kobobooks.com social.kobobooks.com
0.0.0.0 partner.kobobooks.com mobilepartner.kobobooks.com

There is no home button anymore, but factory reset can be performed by booting while pressing the light button (the LED will turn to red). Reset button is still here. Interestingly, you need to press the reset button to reboot when nickel is dead, a long press on the power switch will not be enough like I think it used to be on the Touch. (Remember that nickel is the Kobo's proprietary frontend software.) So... have a paperclip ready whenever you kill nickel, or be sure to always use busybox reboot from the shell (and not to drop the connection, of course...).

Connecting to the device via USB

A useful trick from here. Just add the following at the end of /etc/init.d/rcS:

busybox insmod /drivers/ntx508/usb/gadget/arcotg_udc.ko
busybox insmod /drivers/ntx508/usb/gadget/g_ether.ko

Add the following at the end of /usr/local/Kobo/udev/ac and /usr/local/Kobo/udev/plug:

/sbin/ifconfig usb0 192.168.2.2

You should now connect the device to your computer, issue ifconfig usb0 192.168.2.1, and connect to 192.168.2.2. Depending on your network connection manager and the phase of the moon, it might help to rerun this command "occasionnally" (I did it every 2 seconds or so).

Interestingly, this trick does not interfere with the proper workings of nickel, though it will prevent you from mounting /mnt/onboard as UMS.

Putting an offline copy of Wikipedia on the device

I find it pretty cool to have a copy of the entire Wikipedia on my device. I managed to do so using Kiwix, which is comparatively easy, but then some effort is needed to use the built-in browser in offline mode.

Retrieve the ZIM file corresponding to the Wikipedia that you want from this page. For the English Wikipedia without images, the onboard storage of the device will not be sufficient, and you will need a MicroSD card. If the ZIM file is over 4 GB, you will not be able to put it on a FAT32 filesystem. This is not a problem for the Linux kernel running on the device, of course, but by default the device will complain unless the first partition of the SD card isn't a FAT partition.

Fortunately, this isn't managed by nickel and we can do things properly. The file to edit is /usr/local/Kobo/udev: for intance, you can add mount /dev/mmcblk1p2 /mnt/wikipedia before the dosfsck command and umount -l /mnt/wikipedia after the umount command. This assumes that your Wikipedia SD card has a first FAT partition and a second partition containing Wikipedia, and will mount the Wikipedia partition on /mnt/wikipedia (or fail silently if you insert a card with no suitable second partition). You can tune this to your liking. Once you're done, reboot the device and check that your Wikipedia ZIM file is indeed visible at the expected location at boot.

We now need a tool to browse the ZIM file. Fortunately, the Kiwix project has a very nice tool called kiwix-serve which runs as a HTTP server to serve the content of the dump (unlike lots of other offline Wikipedia tools which insist on serving the content with their own crappy user interface that we couldn't use here even if we wanted to). What's even more fortunate, there are ARM binaries of the Kiwix tools available, so we won't need to cross-compile. Retrieve an ARM build of Kiwix from this page. Transfer it to the device (say in /root), and add the following at the end of /etc/init.d/rcS to run the HTTP server:

(sleep 10; /root/kiwix-serve --port=80 /mnt/wikipedia/wikipedia_en_all_nopic_01_2012.zim) &

For convenience, add 127.0.0.1 a to /etc/hosts to make access to localhost easier. It seems that everything's been taken care of and that we just have to access "a" (i.e. localhost) from nickel's built-in web browser in Settings -> Extras... except that, as you will be pleased to notice, this won't work because nickel will require you to connect to a Wifi network to use the browser, even if what you want to do is just access localhost. Damn. Damn!

It seems that the only way around this extremely annoying misfeature is to reverse-engineer and patch nickel. What follows is not my own work (although it's not available online elsewhere to the best of my knowledge): I am extremely grateful to Glyn from Oxford Hackspace who managed to achieve this while I generously volunteered subtly misleading information to make his job a bit harder.

The relevant file to edit is /usr/local/Kobo/libnickel.so. If you have firmware version 2.5.2 (i.e., the SHA1 sum of your copy of this file is 4c3d7d8cdce4927cbffbde8d3d4c6b7bd35de5c1) and you are in a hurry, you can just grab this file and apply it with bspatch to your libnickel.so file (keep a backup copy of the original file to restore it if things go wrong!), and reboot your Kobo, and hopefully things should work. If you're not in a hurry or don't have the same version, I will go into some detail about how this patch was prepared, so that the process can still be applied to different versions of the firmware (assuming that this part of the code doesn't change too much between versions).

We will need to patch at two places. First, in the function _ZN23WirelessWorkflowManager11openBrowserERK4QUrl that is invoked when opening the browser from settings, we need to work around an attempt to connect to a Wifi network. On firmware 2.5.2, the objdump output looks like this:

007e52a4 <_ZN23WirelessWorkflowManager11openBrowserERK4QUrl>:
  7e52a4:       e92d4070        push    {r4, r5, r6, lr}
  7e52a8:       e1a04000        mov     r4, r0
  7e52ac:       e24dd008        sub     sp, sp, #8
  7e52b0:       e1a06001        mov     r6, r1
  7e52b4:       e59f5048        ldr     r5, [pc, #72]   ; 7e5304 <_ZN23WirelessWorkflowManager11openBrowserERK4QUrl+0x60>
  7e52b8:       ebf24996        bl      477918 <_init+0x256b8>
  7e52bc:       e1a01006        mov     r1, r6
  7e52c0:       e2840010        add     r0, r4, #16
  7e52c4:       ebf262a1        bl      47dd50 <_init+0x2baf0>
  7e52c8:       e59f1038        ldr     r1, [pc, #56]   ; 7e5308 <_ZN23WirelessWorkflowManager11openBrowserERK4QUrl+0x64>
  7e52cc:       e59f3038        ldr     r3, [pc, #56]   ; 7e530c <_ZN23WirelessWorkflowManager11openBrowserERK4QUrl+0x68>
  7e52d0:       e08f5005        add     r5, pc, r5
  7e52d4:       e0851001        add     r1, r5, r1
  7e52d8:       e3a0c080        mov     ip, #128        ; 0x80
  7e52dc:       e0853003        add     r3, r5, r3
  7e52e0:       e1a00004        mov     r0, r4
  7e52e4:       e1a02004        mov     r2, r4
  7e52e8:       e58dc000        str     ip, [sp]
  7e52ec:       ebf1ebab        bl      4601a0 <_init+0xdf40>
  7e52f0:       e1a00004        mov     r0, r4
  7e52f4:       e3a01001        mov     r1, #1
  7e52f8:       e28dd008        add     sp, sp, #8
  7e52fc:       e8bd4070        pop     {r4, r5, r6, lr}
  7e5300:       eaf252c3        b       479e14 <_init+0x27bb4>
  7e5304:       00e7eae8        rsceq   lr, r7, r8, ror #21
  7e5308:       ffce57e8                        ; <UNDEFINED> instruction: 0xffce57e8
  7e530c:       ffd28838                        ; <UNDEFINED> instruction: 0xffd28838

That last jump at 0x7e5300 needs to be changed to jump instead to a function called _ZN23WirelessWorkflowManager25openBrowserAfterConnectedEv that is located at offset 0x7e0e88 in version 2.5.2. So, the binary must be patched to change the four bytes at position 0x7e5300 to branch instead to the previously mentioned function. This should involve leaving the fourth byte at 0xea (the opcode for an unconditional jump without link) and changing the first three bytes to the correct offset.

Second, even once the browser has been started, there will be regular checks for an Internet connection. The incriminated function is _ZN19N3BrowserController15checkConnectionEv which looks like this:

00a80914 <_ZN19N3BrowserController15checkConnectionEv>:
  a80914:       e92d4070        push    {r4, r5, r6, lr}
  a80918:       e1a04000        mov     r4, r0
  a8091c:       ebe75f20        bl      4585a4 <_init+0x6344>
  a80920:       e5901000        ldr     r1, [r0]
  a80924:       e5913030        ldr     r3, [r1, #48]   ; 0x30
  a80928:       e12fff33        blx     r3
  a8092c:       e3500000        cmp     r0, #0
  a80930:       18bd8070        popne   {r4, r5, r6, pc}
  a80934:       ebe75f1a        bl      4585a4 <_init+0x6344>
  a80938:       ebe7ecbe        bl      47bc38 <_init+0x299d8>
  a8093c:       e3500000        cmp     r0, #0
  a80940:       1a000004        bne     a80958 <_ZN19N3BrowserController15checkConnectionEv+0x44>
  a80944:       e594002c        ldr     r0, [r4, #44]   ; 0x2c
  a80948:       e3500000        cmp     r0, #0
  a8094c:       0a000005        beq     a80968 <_ZN19N3BrowserController15checkConnectionEv+0x54>
  a80950:       e8bd4070        pop     {r4, r5, r6, lr}
  a80954:       eae787ce        b       462894 <_init+0x10634>
  a80958:       ebe75f11        bl      4585a4 <_init+0x6344>
  a8095c:       e3a01001        mov     r1, #1
  a80960:       e8bd4070        pop     {r4, r5, r6, lr}
  a80964:       eae7e52a        b       479e14 <_init+0x27bb4>
  a80968:       e3a00014        mov     r0, #20
  a8096c:       ebe77415        bl      45d9c8 <_init+0xb768>
  a80970:       e1a01004        mov     r1, r4
  a80974:       e1a05000        mov     r5, r0
  a80978:       ebe771d8        bl      45d0e0 <_init+0xae80>
  a8097c:       e594202c        ldr     r2, [r4, #44]   ; 0x2c
  a80980:       e1520005        cmp     r2, r5
  a80984:       01a00005        moveq   r0, r5
  a80988:       0afffff0        beq     a80950 <_ZN19N3BrowserController15checkConnectionEv+0x3c>
  a8098c:       e284002c        add     r0, r4, #44     ; 0x2c
  a80990:       e1a01005        mov     r1, r5
  a80994:       ebe7b2ba        bl      46d484 <_init+0x1b224>
  a80998:       e594002c        ldr     r0, [r4, #44]   ; 0x2c
  a8099c:       eaffffeb        b       a80950 <_ZN19N3BrowserController15checkConnectionEv+0x3c>
  a809a0:       e1a04000        mov     r4, r0
  a809a4:       e1a00005        mov     r0, r5
  a809a8:       ebe77124        bl      45ce40 <_init+0xabe0>
  a809ac:       e1a00004        mov     r0, r4
  a809b0:       ebe79be5        bl      46794c <_init+0x156ec>

The first blx instruction is calling something else to check if a WiFi connection exists, and the cmp is checking its return value. We need to alter the next instruction so that the result of this comparison is ignored, so that the pop proceeds unconditionally. So, the binary must be patched to change the four bytes starting at 0xa80930 to make the pop unconditional. This amounts to changing the fourth byte from 0x18 to 0xe8.

Now that this has been taken care of, the setup is pretty usable. Just connect to "a" and you should be all set. A word of caution, though: if the browser can't connect to the HTTP server (e.g. if kiwix-serve isn't running properly), it will fail silently. The static Wikipedia copy that you can browse in this way looks like what you would expect from running kiwix-serve on your own machine. I did not try to generate a full-text index, but maybe it is possible to use one (see kiwix-index), though it does seem to take up a lot of space. Oh, another caveat: the article names in the search box need to be typed with an initial capital, because kiwix-serve is too dumb to figure things out otherwise.

Debian chroot

You can install Debian into an image and chroot there, which makes it easier to install software thanks to Debian's package management system. What's more, you can even get an X server to run on the device, including touchscreen management. I won't go into the details of this because it's been covered elsewhere. I will just mention that you might have some commands fail because you need to tweak the PATH and LD_LIBRARY_PATH, i.e. something like:

export PATH="$PATH:/usr/local/sbin:/usr/local/bin:/sbin:/usr/bin:/usr/sbin"
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/lib/arm-linux-gnueabi"

To get the X server running correctly, you should also replace the binaries in the /fb_update folder of the Debian chroot by those from this post. A very fun thing to do is to install openssh-server (don't forget to change its port to something else than 22 if you have the dropbear ssh server running outside the chroot!), install x2x, and, from your PC, assuming that the sshd port is 2222 and the Kobo's IP is 192.168.2.2, run:

ssh -XC -p 2222 root@192.168.2.2 x2x -north -to :0

This allows you to control the Kobo's X server using your computer's keyboard and mouse. You could conceivably use it as some sort of additional screen (keeping in mind that this is only about input events, you couldn't "move" programs from your computer to the Kobo and back).

This shows that there is basically no limit to how much you could tweak your Kobo's interface. For instance, people on the Mobileread forums have gotten alternative PDF readers to run, e.g. to get reflow fuctionality.

From now on, I'll assume that you have a Debian chroot, so that you can easily install Debian programs using apt-get and run them in the chroot. If you don't, you can still download the armel packages from packages.debian.org, extract them (using ar) and install their contents manually.

Wifi AP

Something that I find funny is the idea to use your Kobo's WiFi to serve an access point. The ability to do so depends on the exact WiFi hardware used, but on my hardware at least it works.

To enable an open WiFi AP, ensure that you are running on the USB connection as described above so as not to kill your connection, and issue:

busybox insmod /drivers/ntx508/wifi/dhd.ko
busybox insmod /drivers/ntx508/wifi/sdio_wifi_pwr.ko
pkill wpa_supplicant
wlarm_le up
wlarm_le ap 1
wlarm_le ssid your_ssid

You can also use wlarm_le cur_etheraddr to change your MAC address. While we're at it, I also advise you to run wlarm_le | less and marvel at all those options. I'm not sure they are all operational. For instance I didn't manage to set it in monitor mode for iwconfig's purposes, even though it seems that, when enabling promiscuous and monitor mode and running tcpdump -i eth0, you can receive traffic on the channel that is not addressed to you -- but it looks like this confuses the hell out of tcpdump because the MAC addresses and ethertype appear garbled. I'm a bit curious about wlarm_le PM as it seems the power saving mode isn't being enabled by default for some reason.

The AP will not be very useful unless you install something to serve DHCP leases. You can apt-get install dnsmasq as described here, adjusting the addresses so as not to conflict with that of the USB network connection. However, the sad truth is that the Kobo's kernel has no support for iptables, which severely restricts the use of what you can do to intercept network connections (e.g. to redirect people in a user-friendly way to the web services running on the Kobo), or relay them (e.g. bridging the wireless and USB interfaces to share your computer's Ethernet connection by creating a WiFi access point with your Kobo would be very nice...). I have tried to cross-compile iptables as a module for the Kobo's kernel, but so far I have mostly failed. I'll do a followup if I manage to achieve something there.

How to compile wl

You don't need to do that -- I just didn't realize at once that a functional wl was provided on the device. I'm just providing this for reference.

Retrieve the source archive from the Kobo github repository. We will need the wl tool from this repository to enable SoftAP mode. Sadly, the precomputed version segfaults, so we will need to cross-compile your own.

I will assume you are running Debian. Install the emdebian-archive-keyring package. Add the following to /etc/apt/sources.list, adapting for your version:

deb http://www.emdebian.org/debian wheezy main

Run apt-get update, and install g++-4.7-arm-linux-gnueabi and xapt. Extract the archive that you downloaded above, go in src/wl/exe, and cross-compile:

make -f GNUmakefile CC=arm-linux-gnueabi-gcc-4.7
comments welcome at a3nm<REMOVETHIS>@a3nm.net