Even more Kobo hacking
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