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.
su?
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:
- ro.debuggable
- Allowed to become root, but not necessarily the default. Set to 1 on eng and userdebug builds.
- ro.secure
- 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 ro.secure 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 "ro.secure" 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) { exit(1); } if (setuid(AID_SHELL) != 0) { exit(1); } 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:
- mov.w pushes 2000 onto register 0,
- blx then executes a function (setuid).
- cmp compares the return value in register 0 with zero,
- and bne jumps to the exit function if the comparison failed.
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 app app-asec app-lib app-private backup bugreports dalvik-cache data dontpanic drm g-ESD.log local lost+found media mediadrm misc property resource-cache security share ssh system tombstones user
The Tolino user interface data are in /data/data/de.telekom.epub/. Inspecting them is the next task. See Tolino: Debug Mode, Hotel Mode, no Ads.
Download
You can find my Tolino 12.2.0 boot image with the patched adbd in my binary file archive.
Links
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.
Update 2021-01: Firmware 14.1.0
Rooted boot image for firmware 14.1.0 is available. As before I could use the same previously rooted adb. This also works on the Tolino Vision 3 HD and Tolino Shine 2 HD. It should work on the Tolino Vision 2 and Tolino page because they all have the same update file.
Update 2022-03: Firmware 15.2.0
Rooted boot image for Tolino 4 HD with firmware 15.2.0 is available. As before I could use the same previously rooted adb. This also works on the Tolino Vision 3 HD and Tolino Shine 2 HD. It should work on the Tolino Vision 2 and Tolino page because they all have the same update file.