Getting ADB root access on a Tolino

On my quest to get rid of advertisements on my Tolino e-book reader I already made my own boot image to enable adbd, the Android Debug Bridge Daemon on the device.

After I opened a adb shell I saw that it had the "shell" user and group, and that I could not access /data/ and /system/ directories.

Time to get root:

$ adb root
$ adb shell
shell@ntx_6sl:/ $ id
uid=2000(shell) gid=2000(shell) groups=1003(graphics),1004(input),1007(log),1009(mount),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats)

Although "adb root" did not give any error, adbd on the reader was still running as limited user.


The alternative to "adb root" was using a su binary that one can run in the shell to gain root access.

My biggest problem was how to get a sane non-infected su binary.

The 1.9.0 root patch contains one, but it is 380 kiB large (adbd itself is only 160 kiB, and the su on my Debian laptop is 63 kiB), and it requires an accompanying Superuser.apk. That su would speak with the .apk which would show a confirmation dialog on the reader screen that one has to confirm.

I decided against that because I do not know where the su is from and what kind of malicious code is inside that 380 kiB blob.

Always having to type "su" in the shell would also mean that I could not simply run adb copy and adb push on /data/ and /system/, which would slow me down when tinkering with the system.

adb root

There are two settings that determine if adb root works, and they are documented in android-core/adb/daemon/main.cpp:

Allowed to become root, but not necessarily the default. Set to 1 on eng and userdebug builds.
Drop privileges by default. Set to 1 on userdebug and user builds.

So setting ro.debuggable in default.prop is the absolute minimum to get root access via adb.

Setting to 0 only means that adbd will always run as root, making it superfluous to always type adb root at the beginning of a debugging session.

I added ro.debuggable in my boot image, flashed it and .. still was not root.

I switched my android core checkout to branch android-4.4.2_r2, looked into adb.c and found the compile time flag ALLOW_ADBD_ROOT. If that was not set, all the reading of "ro.debuggable" and "" would not even be compiled into the adbd binary.

So the Tolino firmware makers had not enabled root access.

Insecure adb

I did not want to compile adbd for the Tolino myself, because that would require installing the Android SDK, the Android NDK, and finding out what the original compilation options were.

The XDA developers forum has a thread [2014.11.10][ROOT] adbd Insecure v2.00 that lets us download an .apk with adbd compilations for different Android SDK versions.

I tried all of them, but only got an offline status when I listed the adb devices:

$ adb devices
List of devices attached
86346346,2f12e2342c894d9e8afa3cf5564223ac	offline

I think this means that adbd on the Tolino crashed and was thus unreachable.

Chainfire's rootadb

There is an Android app that does live patching on an adbd binary on the device itself: rootadb.

It works by opening the adbd file, searching for system calls to capset(), setuid() and setgid() and replacing them with 0xe3a00000.

Unfortunately there was no pre-built binary on Google Play nor in the Github releases section. Compiling it myself would mean the same steps as outlined above for recompiling adbd, so I ditched that idea.

Binary patching

Rootadb's idea was still valid: Patch out the system calls that drop privileges through changing user and group IDs and removing capabilities.

I installed Ghidra (open source IDA Pro alternative) for the first time and loaded my Tolino's adbd binary into it. It shows the assembler code in a nicer way than hex editors, and I could see the mov, cmp and bne statements with their parameters.

The big question was how to find the place inside the 160 kiB that called the system methods. In parallel I looked at the adb.c source code:

/* then switch user and group to "shell" */
if (setgid(AID_SHELL) != 0) {
if (setuid(AID_SHELL) != 0) {
D("Local port disabled\n");

Now I knew where to look: Search for the "Local port disabled" string and find the place where it is used.

After skipping backwards about all the code generated for the D() function I found the setuid() call at offset 0x1288 (ghidra: 0x9288).

Looking for 0x7d0 ("2000" in hex) was even easier because that's the user id for the "shell" user.

00009288 4f f4 fa 60     mov.w      r0,#0x7d0
0000928c 0e f0 86 ed     blx        FUN_00017d9c
00009290 00 28           cmp        r0,#0x0
00009292 df d1           bne        LAB_00009254

What happens here:

The fastest solution was to patch out all the commands by replacing them with NOP (no operation) commands, which is 00 BF in the little endian ARM code.

Ghidra told me that it does not support 32 bit ARMv7 binaries, and that saving them could break them.

So I opened my hex editor bless, subtracted 0x8000 from the offsets that Ghidra showed me and manually NOPed all the commands.

I did that for the setuid() call only at first, built and flashed a new boot image with the modified adbd, ran it and opened a shell on the Tolino:

$ adb shell
root@ntx_6sl:/ # id
uid=0(root) gid=2000(shell) groups=1003(graphics),1004(input),1007(log),1009(mount),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats)
root@ntx_6sl:/ $ ls /data/
opendir failed, Permission denied

So adbd was running as root user now, but did not yet have the root group, and could still not access /data because it had dropped capabilities.

Now I patched out the calls to setgid(), setgroups() and prctl() and could finally declare success:

$ adb shell
root@ntx_6sl:/ # id
uid=0(root) gid=0(root)
root@ntx_6sl:/ # ls /data

The Tolino user interface data are in /data/data/de.telekom.epub/. Inspecting them is the next task.

Analyzing the ADBD C source code Ghidra showing the setuid() call in assembler Trying to find the assembler instructions in my hex editor


You can find my Tolino 12.2.0 boot image with the patched adbd in my binary file archive.

Update 2020-09: Firmware 13.2.1

I made the rooted boot image for firmware version 13.2.1 which was easy, because 13.2.1 still uses Android 4.4.2 and I could just copy over the previously patched adb. Download it from my binary file archive.

Written by Christian Weiske.

Comments? Please send an e-mail.