More fnacbook hacking
In this followup to my previous post about hacking the Kobo, I present a few other tips.
Reindexing the collection
It turns out that you can reindex the collection on the device without
actually pluging it in a computer. This is very useful if you add or remove
files to the device directly (using sftp, for instance), though you do not need
it if you are just replacing existing files. The idea is that
nickel
uses the FIFO /tmp/nickel-hardware-status
to
get notified about events: if you write usb plug add
or usb
plug remove
to this file, it will act as if the Kobo had been connected
or disconnected to the computer.
Hence, you can create a file reindex.sh
containing:
echo usb plug add >> /tmp/nickel-hardware-status \ && sleep 10 \ && echo usb plug remove
Make it executable and run it with nohup ./reindex.sh
.
nickel
will present a dialog, quickly select "connect", and wait
for the fake disconnection and reindexing to take place. Unfortunately, this
will disconnect the Wi-Fi (hence the nohup
business).
[This one was sent to me by Andreas Heider. Thanks a lot for suggesting this!] I also noticed that, when you download a file through the built-in web-browser, it will get indexed. It could be sensible to run a minimalistic web-server on the device to serve files already stored on the device to make the device index them. Maybe I'll investigate this later.
Automatic reverse ssh
You might want to connect to the device easily without having to find out its
IP or worrying about NATs. The right tool for this problem is
openvpn
, but it has a lot of dependencies and isn't exactly light.
A satisfactory (and probably simpler) alternative is to get the device to
reverse ssh to a trusted server (with key-based authentication) when it connects
to the Internet, so that you can always use this to get a shell.
I already mentioned
how to get dropbear running on the device. To get reverse ssh working, it turns
out that you have to add busybox ifconfig lo up
to the
/etc/init.d/rcS
file because it isn't done by default. Then,
generate a key using dropbearkey -f /root/.ssh/id_rsa -t rsa
and
copy the fingerprint returned by the command to your
authorized_keys
file on the trusted server. Obviously, if you worry
that unauthorized people might have access to your device, you should use an
account with very little permissions.
Now, once you have checked that dbclient -i /root/.ssh/id_rsa
USER@SERVER
works without asking for a password, you can add the following
reverse ssh command at the end of the renew|bound)
section of file
/etc/udhcpc.d/default.script
(note the unsafe "-y" to avoid unknown
host key issues, and adapt if you don't like this):
dbclient -y -i /root/.ssh/id_rsa -R 4080:127.0.0.1:22 USER@SERVER < dev/ptmx &
Now, when obtaining or renewing a DHCP lease, the device should run the reverse ssh. I have found this to be pretty fragile when testing, for unknown reasons: workarounds you might want to try are using the IP address of SERVER directly (in case DNS resolution is not working properly yet) or moving the command to a separate script which sleeps for a few seconds before running the command.
Leaving the Wi-Fi active
I was complaining in my previous post about the Wi-Fi getting disabled after a
few minutes. According to the (very insightful) guide Hacking the Kobo Touch for
Dummies, the responsible for this is nickel
, and, indeed, just
running killall nickel
will fix the problem. Obviously, without
nickel
running, there is little you can do with the device except
from using ssh. When you're done, the /etc/init.d/rcS
file suggests
that you can get nickel
back by issuing something like:
INTERFACE=wlan0 WIFI_MODULE=ar6000 if [ $PLATFORM == ntx508 ]; then INTERFACE=eth0 WIFI_MODULE=dhd fi export INTERFACE export WIFI_MODULE export QWS_MOUSE_PROTO="tslib_nocal:/dev/input/event1" export QWS_KEYBOARD=imx508kbd:/dev/input/event0 export QWS_DISPLAY=Transformed:imx508:Rot90 export NICKEL_HOME=/mnt/onboard/.kobo export LD_LIBRARY_PATH=/usr/local/Kobo export WIFI_MODULE_PATH=/drivers/$PLATFORM/wifi/$WIFI_MODULE.ko export LANG=en_US.UTF-8 export UBOOT_MMC=/etc/u-boot/$PLATFORM/u-boot.mmc export UBOOT_RECOVERY=/etc/u-boot/$PLATFORM/u-boot.recovery /usr/local/Kobo/nickel -qws
Trouble is, this does not seem to work perfectly (the new nickel
instance is a bit broken, especially with regards to Wi-Fi). I would need to
investigate more. Of course, to fix things for real, you can always just reboot
the device.
Displaying stuff on the Kobo from the shell
People have already figured out how to display stuff on the Kobo's screen from the shell. It's pretty minimalistic (only images in a weird format). To spare a click, here is how you do it:
ffmpeg -i $INPUT.png -vf transpose=2 -f rawvideo -pix_fmt rgb565 -s 600x800 -y $OUTPUT.raw cat $OUTPUT.raw | ssh DEVICE /usr/local/Kobo/pickel showpic
Of course, this means that you can replace the images in
/etc/images
and /usr/local/Kobo/slideshow
by other
images. Disappointingly, though, the interesting images (the "powered off" and
"sleeping" logos) don't seem to be there and seem to be displayed in another
way. The most interesting thing you can customize is the boot animation
(/etc/images/on-*.raw.gz
).
Retrieving touchscreen events from the device
The (very insightful) guide Hacking the Kobo Touch for Dummies also explains how to recover the touchscreen input events from the device:
evtest /dev/input/event1
There is a lot you can do by forwarding these events to your computer and interpreting them creatively (a custom remote control, for instance). As always, buffering issues are a problem when trying to retrieve the events synchronously. Here is a possible way to retrieve the useful info, where DEVICE is where you can reach the device:
cat <(echo 'evtest /dev/input/event1;') - | socat EXEC:'ssh DEVICE',pty,ctty,echo=0 STDIO | sed -u 's/$/\n/' | stdbuf -i0 -o0 -e0 cut -d ' ' -f 9,11,13
In case you're wondering, the sed command is because the newline at the end of the last event is outputted when the next one happens, which isn't really convenient if you want to pipe the data to something.
Here is a small Python program to run on your computer which accepts this input and will draw what you do on your touchscreen:
import pygame import pygame.draw import sys pygame.init() def line(x1, y1, x2, y2, r=255, g=255, b=255): global window pygame.draw.aaline(window, (r, g, b), (x1, y1), (x2, y2)) window = pygame.display.set_mode((600, 800)) pre = None while True: l = sys.stdin.readline() if not l: break l = l.rstrip().split(' ')[0:3] if len(l) != 3 or '' in l: continue print l if pre: line(600-int(pre[1]), int(pre[0]), 600-int(l[1]), int(l[0])) if l[2] != '0': pre = l else: pre = None pygame.display.flip()
Obviously, you should try to minimize the network latency if you want this to be pleasant.