(October 2016)
Two years ago, I left my country. There's a sad story there, but let's just say that I chose to spend some time in Malta, working remotely for a nice Irish startup - while living in an apartment 30 meters by the sea :-)
After finishing my work everyday at 16:00, 15 minutes later I would go for a swim.
I miss the glorious Mediterranean sun...
I would then enjoy long afternoon walks by the beach, listening to music and catching up on my favorite online forums (Reddit/programming, Hacker News, Slashdot, etc) - using my tablet.
Until my tablet died. Suddenly.
A cruel and unusual death that involved noise, a bad smell (electrolytic capacitor bidding farewell?) and then... total silence.
I could have had a look into it - but I knew it was high time I got a new one anyway.
I started looking at the various offerings, and being a nerd of a frugal nature, decided to only look at the best HW bang for the buck, completely ignoring the SW aspects.
"Software? Bah, I'll sort that out later" - Famous Last Words (TM)
I ended up with this:
Asus MemoPad 10" ME103K
In August of 2015, 175 Euros got me a tablet with a 10 inch screen, 1GB of RAM, a quad-core Qualcomm Snapdragon, and 16GB of storage - with more space available if one needed it via an SD card slot.
In my preliminary use, the tablet was quite fast in web browsing (with ad blocking of course - otherwise the ads bring down even desktop browsers to their knees). That's what I mostly expect from my tablet - adequate browsing, and reading PDFs while listening to music.
Oh, and running Debian in a chroot of course. Many benefits there; I am a Linux/Unix/BSD/embedded guy, living my life in the command line - and I need to be able to do everything that I do with my main computers, on my tablet as well (like running Privoxy, dictd, my local Flask servers that are accessible only from the tablet, tunneling via local SSH's SOCKS tunnels to tether beyond the idiocy of providers... you get the idea).
And I trust Debian. Far more than I trust the Android ecosystem.
That was when it hit me; I searched, and searched, and searched...
...there was no open [2] rooting process for this tablet.
No TWRP, no CyanogenMod, nothing.
Nowhere.
(Gulp)
...
I spent my free time over the next weeks studying Android. Not from an application developer standpoint, mind you; only from the low-level aspects that an embedded software engineer cares about.
An Android device boots from a soldered-in flash chip, that acts as a
"hard disk". The kernel sees it usually as device /dev/mmcblk0
,
and it is split in various partitions.
In my tablet, after starting an adb shell
, I can see this...
shell@K01E_2:/ $ ls -l /dev/block/platform/msm_sdcc.1/by-name lrwxrwxrwx. 1 root root 20 Oct 8 23:11 ADF -> /dev/block/mmcblk0p9 lrwxrwxrwx. 1 root root 20 Oct 8 23:11 APD -> /dev/block/mmcblk0p8 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 DDR -> /dev/block/mmcblk0p28 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 aboot -> /dev/block/mmcblk0p14 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 abootb -> /dev/block/mmcblk0p20 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 abootu -> /dev/block/mmcblk0p23 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 boot -> /dev/block/mmcblk0p16 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 cache -> /dev/block/mmcblk0p25 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 fsg -> /dev/block/mmcblk0p10 lrwxrwxrwx. 1 root root 20 Oct 8 23:11 m9kefs1 -> /dev/block/mmcblk0p5 lrwxrwxrwx. 1 root root 20 Oct 8 23:11 m9kefs2 -> /dev/block/mmcblk0p6 lrwxrwxrwx. 1 root root 20 Oct 8 23:11 m9kefs3 -> /dev/block/mmcblk0p7 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 m9kefsc -> /dev/block/mmcblk0p30 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 metadata -> /dev/block/mmcblk0p31 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 misc -> /dev/block/mmcblk0p26 lrwxrwxrwx. 1 root root 20 Oct 8 23:11 modemst1 -> /dev/block/mmcblk0p2 lrwxrwxrwx. 1 root root 20 Oct 8 23:11 modemst2 -> /dev/block/mmcblk0p3 lrwxrwxrwx. 1 root root 20 Oct 8 23:11 persist -> /dev/block/mmcblk0p4 lrwxrwxrwx. 1 root root 20 Oct 8 23:11 radio -> /dev/block/mmcblk0p1 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 recovery -> /dev/block/mmcblk0p27 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 rpm -> /dev/block/mmcblk0p15 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 rpmb -> /dev/block/mmcblk0p21 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 sbl1 -> /dev/block/mmcblk0p11 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 sbl2 -> /dev/block/mmcblk0p12 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 sbl2b -> /dev/block/mmcblk0p18 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 sbl3 -> /dev/block/mmcblk0p13 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 sbl3b -> /dev/block/mmcblk0p19 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 ssd -> /dev/block/mmcblk0p29 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 system -> /dev/block/mmcblk0p24 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 tz -> /dev/block/mmcblk0p17 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 tzb -> /dev/block/mmcblk0p22 lrwxrwxrwx. 1 root root 21 Oct 8 23:11 userdata -> /dev/block/mmcblk0p32
My ME103K starts it's booting process by verifying the aboot
partition is not tampered
with. There's some sort of signature verification going on there, so
even if you obtained root access and dd
-ed over a tweaked version
of the aboot
partition data, you'd end up with a 175 Euro brick
(unless you can desolder the flash chip, extract it from the PCB, re-program it,
and solder it again. Not my cup of tea - huge respect to those that can, though).
aboot
is what is officially called the bootloader. Once it is
verified and starts running, it will perform the same song and dance
for the boot
partition; checking that it is nice and proper,
loading its contents and executing them.
In principle, this is where Android vendors are supposed to allow
you to hijack the process, and boot your own boot
image, using...
host$ adb reboot bootloader host$ # tablet reboots into aboot, and pauses just BEFORE host$ # loading the boot partition data
host$ # Actually, please load my own bootable image host$ fastboot boot myDamnImage.img
But to be able to do that, the bootloader must be unlocked; that is, it must allow you to load your own boot images.
Some - but not all - devices, allow you to do this:
host$ fastboot oem unlock
Mine did (thank god).
I do believe though that when you do this, the tablet is supposed to be wiped automatically... since by allowing booting of a custom image, an external entity can pretty much gather every data residing inside the tablet - and completely violate your privacy.
My MemoPad didn't.
Hmm.
It was time to create my own boot image. I wish I could dd
the boot
partition over from the flash chip and start from there - but I was not root,
and therefore not allowed to dd
from partitions.
Looking for a fallback, I checked the last Over-The-Air update sent by Asus to these tablets. I was quickly able to extract the boot image and the accompanying file system from inside it (initrd, in Linux terms):
host$ ls -l total 803592 drwxr-xr-x 2 ttsiod ttsiod 4096 Oct 21 01:22 ./ drwxr-xr-x 6 ttsiod ttsiod 4096 Sep 25 23:34 ../ -rw-rw-r-- 1 ttsiod ttsiod 822861864 Aug 7 2015 UL-K01E-WW-12.16.1.12-user.zip host$ unzip -l UL-K01E-WW-12.16.1.12-user.zip | grep boot.img 7368704 2011-03-22 11:21 boot.img host$ unzip UL-K01E-WW-12.16.1.12-user.zip boot.img Archive: UL-K01E-WW-12.16.1.12-user.zip signed by SignApk inflating: boot.img host$ mkdir contents host$ cd contents host$ abootimg -x ../boot.img writing boot image config in bootimg.cfg extracting kernel in zImage extracting ramdisk in initrd.img host$ mkdir rootfs host$ cd rootfs host$ zcat ../initrd.img | cpio -ivd 4040 blocks host$ ls -l lrwxrwxrwx 1 ttsiod ttsiod 13 Oct 23 08:30 charger -> /sbin/healthd drwxrwx--x 2 ttsiod ttsiod 4096 Oct 23 08:30 data -rw-r--r-- 1 ttsiod ttsiod 350 Oct 23 08:30 default.prop drwxr-xr-x 2 ttsiod ttsiod 4096 Oct 23 08:30 dev -rw-r--r-- 1 ttsiod ttsiod 37014 Oct 23 08:30 file_contexts -rw-r----- 1 ttsiod ttsiod 1242 Oct 23 08:30 fstab.qcom -rwxr-x--- 1 ttsiod ttsiod 235904 Oct 23 08:30 init -rwxr-x--- 1 ttsiod ttsiod 2614 Oct 23 08:30 init.class_main.sh -rwxr-x--- 1 ttsiod ttsiod 1284 Oct 23 08:30 init.environ.rc -rwxr-x--- 1 ttsiod ttsiod 6758 Oct 23 08:30 init.qcom.class_core.sh -rwxr-x--- 1 ttsiod ttsiod 88 Oct 23 08:30 init.qcom.diag.rc -rwxr-x--- 1 ttsiod ttsiod 5604 Oct 23 08:30 init.qcom.early_boot.sh -rwxr-x--- 1 ttsiod ttsiod 24602 Oct 23 08:30 init.qcom.rc -rwxr-x--- 1 ttsiod ttsiod 3563 Oct 23 08:30 init.qcom.sh -rwxr-x--- 1 ttsiod ttsiod 2838 Oct 23 08:30 init.qcom.syspart_fixup.sh -rwxr-x--- 1 ttsiod ttsiod 38327 Oct 23 08:30 init.qcom.usb.rc -rwxr-x--- 1 ttsiod ttsiod 22527 Oct 23 08:30 init.rc -rwxr-x--- 1 ttsiod ttsiod 8346 Oct 23 08:30 init.target.rc -rwxr-x--- 1 ttsiod ttsiod 1927 Oct 23 08:30 init.trace.rc -rwxr-x--- 1 ttsiod ttsiod 3902 Oct 23 08:30 init.usb.rc -rwxr-x--- 1 ttsiod ttsiod 301 Oct 23 08:30 init.zygote32.rc drwxr-xr-x 2 ttsiod ttsiod 4096 Oct 23 08:30 proc -rw-r--r-- 1 ttsiod ttsiod 3138 Oct 23 08:30 property_contexts drwxr-xr-x 3 ttsiod ttsiod 4096 Oct 23 08:30 res drwxr-x--- 2 ttsiod ttsiod 4096 Oct 23 08:30 sbin -rw-r--r-- 1 ttsiod ttsiod 746 Oct 23 08:30 seapp_contexts -rw-r--r-- 1 ttsiod ttsiod 76 Oct 23 08:30 selinux_version -rw-r--r-- 1 ttsiod ttsiod 177701 Oct 23 08:30 sepolicy -rw-r--r-- 1 ttsiod ttsiod 10468 Oct 23 08:30 service_contexts drwxr-xr-x 2 ttsiod ttsiod 4096 Oct 23 08:30 sys drwxr-xr-x 2 ttsiod ttsiod 4096 Oct 23 08:30 system -rw-r--r-- 1 ttsiod ttsiod 8349 Oct 23 08:30 ueventd.qcom.rc -rw-r--r-- 1 ttsiod ttsiod 4617 Oct 23 08:30 ueventd.rc
Hello - lots of stuff here.
The Linux kernel starts /init
, which in the case of Android reads
what actions to perform from init.rc
- which includes other
init.*
to setup the various SW and HW subsystems.
init.rc
includes actions like spawning adbd
- allowing us to connect to
the tablet over adb
:
host$ less init.rc ... # adbd is controlled via property triggers in init.<platform>.usb.rc service adbd /sbin/adbd --root_seclabel=u:r:su:s0 class core socket adbd stream 660 system system disabled seclabel u:r:adbd:s0 ...
And what we usually have in /etc/fstab
in other Unixes, resides
inside fstab.deviceName
in Android (in my case, fstab.qcom
):
host$ cat fstab.qcom /dev/block/platform/msm_sdcc.1/by-name/system /system ext4 ... /dev/block/platform/msm_sdcc.1/by-name/userdata /data ext4 ... /devices/platform/msm_sdcc.3/mmc_host /storage/MicroSD vfat ... /devices/platform/msm_hsusb_host /storage/USBdisk1 vfat ... /devices/platform/msm_ehci_host.0 /storage/USBdisk2 vfat ...
This part looks interesting as well; in principle, I can edit
this file and have e.g. the /system
partition point to my external
SD card, which I CAN control (i.e. plug it in my laptop, write
anything I want in it, then plug it in the tablet and reboot).
But I can't read the current /system
partition to do this - since
I am not root, I can't take a snap, and store it in an external SD card.
Not yet :-)
Anyway - since the filesystem in the boot image is being modified, might
as well cross-compile busybox
and add it in. I copied it under /sbin
and created all the symlinks:
host$ cd sbin host$ ls -l ... lrwxrwxrwx 1 root root 9 Sep 28 2015 addgroup -> ./busybox lrwxrwxrwx 1 root root 9 Sep 28 2015 add-shell -> ./busybox lrwxrwxrwx 1 root root 9 Sep 28 2015 adduser -> ./busybox lrwxrwxrwx 1 root root 9 Sep 28 2015 adjtimex -> ./busybox lrwxrwxrwx 1 root root 9 Sep 28 2015 ar -> ./busybox lrwxrwxrwx 1 root root 9 Sep 28 2015 arp -> ./busybox lrwxrwxrwx 1 root root 9 Sep 28 2015 arping -> ./busybox lrwxrwxrwx 1 root root 9 Sep 28 2015 ash -> ./busybox lrwxrwxrwx 1 root root 9 Sep 28 2015 awk -> ./busybox ... lrwxrwxrwx 1 root root 9 Sep 28 2015 telnet -> ./busybox lrwxrwxrwx 1 root root 9 Sep 28 2015 telnetd -> ./busybox ... lrwxrwxrwx 1 root root 9 Sep 28 2015 xz -> ./busybox lrwxrwxrwx 1 root root 9 Sep 28 2015 xzcat -> ./busybox lrwxrwxrwx 1 root root 9 Sep 28 2015 yes -> ./busybox lrwxrwxrwx 1 root root 9 Sep 28 2015 zcat -> ./busybox lrwxrwxrwx 1 root root 9 Sep 28 2015 zcip -> ./busybox
Busybox is very useful - allowing running tons of stuff from inside the tablet, even without chrooting.
This modified filesystem needs to be placed back in a boot image:
host$ cd rootfs/ host$ find . | cpio --create --format='newc' | \ gzip > ../initrd_busybox.cpio.gz host$ cd .. host$ abootimg --create ../new_boot.img -f bootimg.cfg \ -k zImage -r initrd_busybox.cpio.gz
Now, since busybox was added in, this image is bigger than the
original one - so abootimg
will fail here, with...
updated is too big for the Boot Image (8441856 vs 7393280 bytes)
Editing bootimg.cfg and updating the expected length in field bootsize
is simple enough - but since I will be doing lots of experiments with
many attempted boot images, I had to automate this a bit:
host$ cat bootimg.cfg | grep bootsize bootsize = 0xDEAD host$ cat autoboot.sh #!/bin/bash cd rootfs/ find . | cpio --create --format='newc' | \ gzip > ../initrd_busybox.cpio.gz cd .. abootimg --create ../new_boot.img -f bootimgBig.cfg \ -k zImage -r initrd_busybox.cpio.gz >log 2>&1 if grep 'updated is too big' log ; then SIZE=$(grep 'updated is too big' log | awk '{print substr($10,2,7);}') HEX=$(echo -e "obase=16\n$SIZE\n" | bc -l) cat bootimg.cfg | sed "s/DEAD/$HEX/" > bootimgBig.cfg rm -f log abootimg --create ../new_boot.img -f bootimgBig.cfg \ -k zImage -r initrd_busybox.cpio.gz | tee log if grep 'updated is too big' log ; then echo Failure... exit 1 fi fi echo Rebooting... fastboot boot ../new_boot.img exit 0
And there it was, the 1st step - the tablet was now "equipped" with software that I provided myself:
host$ ./autoboot.sh ... host$ adb shell shell@K01E_2:/ $ telnet Usage: telnet [-a] [-l USER] HOST [PORT] Connect to telnet server -a Automatic login with $USER variable -l USER Automatic login as USER
Almost there! Right?
At that point, I noticed something.
I was able to boot my custom images just fine - issuing adb
reboot bootloader
afterwards, and once my tablet reached fastboot stage,
preparing and booting my next image... Always trying to become root.
But I noticed that when I tried rebooting the tablet ON ITS OWN, it ended up in the bootloader menu.
I believe the Android community calls this "stuck in fastboot".
Oh-oh - the tablet was no longer bootable on its own!
It needed to be tethered to a PC, where a fastboot boot whatever.img
would solve the issue.
Not that I take my tablet on long trips or anything, but I found it rather annoying. Why did this happen? I didn't do anything wrong, did I?
I needed a way to see what the tablet was saying during it's boot process. But... there's no way to do that in the Android world. No standard way, that is - every OEM has some method of its own.
Why Google didn't mandate logging the boot messages via some form
of the fastboot protocol (e.g. fastboot logcat
), I will never know.
What I do know, is that after Googling some more, I found out that Nexus devices have a serial port in their headphone jack.
And Asus manufactured some Nexus devices. Just as it manufactures ME103Ks (which are NOT Nexus, mind you).
Say, maybe they re-used the same hardware in my tablet...
In Nexuses, if you feed 3.3V in the microphone input of your headphone's TRRS plug, the left and right channels switch to carrying the serial port's TX and RX channels.
I used my Raspberry PI TRRS-to-TV cable to get access to the 4 places I needed: GND, MIC, L and R. The cable was not supposed to be doing anything else except exposing the 3 signals (MIC,L,R - each one paired with GND) in the three corresponding cables (red, white, yellow).
I then used my recently acquired BitScope to probe between the TX (tip of white cable in picture) and the common GND (brown probe at the bottom of picture). I also used two probes (red and blue one) to "feed" 3.3V from my USB/TTL chip (a PL2303HX plugged in my laptop) to the MIC (red) tip.
Upon rebooting the tablet, I indeed saw what was unmistakably a serial signal at 115200 (peak-to-peak of 8 to 9us), but with lots of capacitance:
There you are! Come out, little serial...
BitScope also told me that the signal was at 1.8V - so my PL2303HX (a 3.3V USB/TTL) could not possibly decode it. I needed a shifter.
The signal also looked indecipherable, to my "amateur at electronics" eyes; the edges were surely too slow for any UART to pick up. I asked the Electronics Stack Exchange guys what they thought, and they recommended probing on the jack itself - so I stripped the tablet down to its PCB, and probed all over... Nope, same capacitance-riddled signal, and I couldn't find it anywhere else on the PCB.
But if after booting, I used stty
to lower the speed to 9600 baud,
then all was crystal clear - nice square pulses:
shell@K01E_2:/ $ stty -F /dev/ttyHSL0 9600 shell@K01E_2:/ $ while true ; do echo UUUUUUU > /dev/ttyHSL0 ; sleep 0.1 ; done
I considered giving up, but Chris Stratton, in one of the few Stack Exchange comments I got, claimed that "...a good shifter can cope even with this kind of capacitance-riddled signal".
Might as well try!
As good an opportunity as ever to start rebuilding my electronics "war chest"... In the meantime, I had left Malta and gotten a job at the European Space Agency in the Netherlands, so I made my first on-line order from Dutch electronics' shops.
3 days later, I got a bunch of cables, a breadboard, a soldering iron and a level shifter: a BSS138.
After a bit of lousy soldering and jungle-cable-breadboarding, with my
heart beating unusually fast, I tried spawning minicom
...
...and to my utter amazement, I saw this:
Unbelievable - after BSS138 "lifted" the signal from 1.8 up to 3.3V, that miserable, capacitance-riddled signal was actually decoded! I could finally see why my tablet was stuck at fastboot.
Update, 2 weeks later: I ordered a TRRS breakout (to connect a male-to-male headphone cable from the tablet to the breadboard) and cleaned up the tiny design - it is, after all, a useful circuit to keep. Besides connecting the TIP and RING1 to the shifter, I also used a voltage divider to create 1.8V out of the PL2303HX's 3.3V output - and fed it in the LV input of the BSS138.
This is the complete transcript of what came through the serial port (if you are using a slow device, you may want to use your desktop/laptop to watch this):
Now after watching this ASCII cast (thanks, asciinema!), I am sure you are wondering where the heck I found those "magic" oem-specific commands (in this case, ME103K-specific):
host$ sudo fastboot fastboot oem device-info ... (bootloader) Device tampered: true ... host$ sudo fastboot oem reset-dev_info host$ sudo fastboot fastboot oem device-info ... (bootloader) Device tampered: false ...
Well...
"Normal" firmware (for various values of "normal") can be asked to actually list the oem commands it supports - but...
So I went back to that Over-The-Air update...
host$ ls -l total 803592 drwxr-xr-x 2 ttsiod ttsiod 4096 Oct 21 01:22 ./ drwxr-xr-x 6 ttsiod ttsiod 4096 Sep 25 23:34 ../ -rw-rw-r-- 1 ttsiod ttsiod 822861864 Aug 7 2015 UL-K01E-WW-12.16.1.12-user.zip host$ unzip -l UL-K01E-WW-12.16.1.12-user.zip | grep aboot 1471540 2011-03-22 11:21 bootloader.aboot.img host$ unzip UL-K01E-WW-12.16.1.12-user.zip bootloader.aboot.img Archive: UL-K01E-WW-12.16.1.12-user.zip signed by SignApk inflating: bootloader.aboot.img host$ strings bootloader.aboot.img | grep ^oem oem reboot-recovery is called! oem unlock oem device-info oem lock oem gpt-info oem fuse_blow oem check-fuse oem reset-dev_info oem grant oem off-mode-charge 1 oem off-mode-charge 0 oem uart-on oem uart-off
I wouldn't touch those 'fuse' things with a ten-foot pole; I don't want to brick my tablet.
But reset-dev_info
?
Come on, I just had to try that :-)
So, what did we learn, Palmer?
If I can only temporarily become root with a custom boot image, so be it - I will
do so, and then try to exploit something
on the /system
partition; and finally "re-brand" myself as "an ASUS-compliant, good guy".
But just in case... could one, say, flash a boot image... and then pretend it never happened?...
host$ sudo fastboot flash boot new_boot.img host$ sudo fastboot oem reset-dev_info host$ sudo fastboot reboot
Nope - same 'stuck in fastboot' behaviour.
NOTE: I already had a backup of the boot partition when I tried this - I did it AFTER I became
root and was able to dd
a pristine copy of the full boot
partition for restoring.
It was also interesting to note that the dd
copy of boot
did NOT match the boot.img
in the Over-The-Air update form - maybe there are signatures at the end of that partition
that are checked for correctness. With closed-source systems, one never knows...
So, back to becoming root via a custom boot image.
Some of my research told me that if you are in control of the boot image, all you need to do
is spawn telnetd from within init.rc
- it would be spawned with root privileges,
so anyone telnet-ing inside it is the equivalent of a Digital God.
I tried various forms of this, but to no avail...
host$ cat init.rc ... service telnetd /sbin/telnetd -l /sbin/bash oneshot
bash
) under /sbin
)
Nothing - there was no telnetd
in the ps
output after the boot.
And no log of this failure anywhere... Nothing in dmesg
, no logs under /var/log/
.
Silence.
(sigh)
I tried another form of spawning telnetd
, suggested by an awesome fireman - kudos, Kris!
But that one failed as well.
Hmm...
Maybe I can try one of the classics, and place an su
under /sbin
?
Cross-compiled it, and placed it (with the proper setuid
bit set) under /sbin
...
host$ adb shell shell@K01E_2:/ $ /sbin/su Permission denied.
Oh?
Denied by whom?
Reading some more, I realized that SELinux was standing in my way:
host$ adb shell shell@K01E_2:/ $ getenforce Enforcing shell@K01E_2:/ $ setenforce 0 setenforce: Could not set enforcing status: Permission denied
I clearly couldn't disable it - not in the default state of my tablet.
But since I control the filesystem bundled in my boot image, maybe
there was a way to tell SELinux to get off my lawn? That is, to allow
my shell
user to control its state?
In the root folder of my boot image's root filesystem, I eventually found this:
host$ ls -l sepolicy -rw-r--r-- 1 ttsiod ttsiod 177701 Oct 23 08:30 sepolicy
And - this took me ages to find - Joshua Brindle provides the source code
of sepolicy-inject;
a tool that allowed me to tell Android that the SELinux policy I want
is one where my shell
user has the power to kill the gorilla:
host$ ls -l drwxr-xr-x 2 ttsiod ttsiod 4096 Oct 22 16:05 ./ drwxr-xr-x 4 ttsiod ttsiod 4096 Oct 4 2015 ../ -rw-r--r-- 1 ttsiod ttsiod 349 Oct 22 15:42 Makefile -rw-r--r-- 1 ttsiod ttsiod 1088 Jul 17 2013 README.txt -rw-r--r-- 1 ttsiod ttsiod 5851 Jul 17 2013 sepolicy-inject.c -rwxr-xr-x 1 ttsiod ttsiod 293912 Oct 22 15:42 sepolicy-inject* host$ ./sepolicy-inject -Z shell -P /path/to/original/sepolicy \ -o /path/to/custom.rootfs/sepolicy.jean.let.me.in
I packaged this new sepolicy
in my root filesystem, packed a new boot image,
and after fastboot
-ing with it, I saw this:
host$ adb shell shell@K01E_2:/ $ /sbin/su shell@K01E_2:/ # id uid=0(root) gid=0(root) groups=1004(input),1007(log),1011(adb), 1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt), 3003(inet),3006(net_bw_stats) context=u:r:shell:s0
YES! I am (G)ROOT!
And I can chroot - right?
shell@K01E_2:/ # chroot /data/local/tmp chroot: can't change root directory to '/data/local/tmp': Operation not permitted 1|shell@K01E_2:/ # ls -l /data/local/tmp opendir failed, Permission denied
What?!?!
What kind of a root user can't list a folder?!
I asked the Gods again for inspiration - but as usual, the Gods don't respond to mortals.
FINE.
Remember this part?
host$ abootimg -x ../boot.img writing boot image config in bootimg.cfg extracting kernel in zImage extracting ramdisk in initrd.img
So far the only modifications were applied on the root filesystem contained inside
the boot image (that is, in initrd.img
) - the kernel (zImage
) was left as-is.
To debug why the chroot system call (and the listing one!) fail,
I decided to compile the kernel (ASUS thankfully provided the source code), and then
sprinkle it with printk
statements to see EXACTLY where the system calls fail.
After a LOT of work, I found out about the 'capabilities bounding set'.
To cut a long story short, the kernel allows you now to limit the kind of things ("the amount of damage") you can do from a process. The children spawned by it, can only limit things further - they can't get BACK what they (or their parents) lost.
Then again, I controlled the kernel code - so I went all in, guns blazing:
static long cap_prctl_drop(struct cred *new, unsigned long cap) { if (!capable(CAP_SETPCAP)) return -EPERM; if (!cap_valid(cap)) return -EINVAL; // ttsiodras: come in, everyone, the water's fine! //cap_lower(new->cap_bset, cap); return 0; }
(Yes, yes - I know, this is basically allowing everyone and their grandmother
to do anything; capabilities are NEVER droped.
But I don't care - remember, the plan is that I will use my temporary
root powers to mess with my /system
partition, and then revert back to
a pristine boot image).
After building this kernel and packaging it in...
host$ adb shell shell@K01E_2:/ $ /sbin/su root@K01E_2:/ # chroot /data/debian/ /bin/bash root@localhost:/# cat /etc/issue Debian GNU/Linux 8 \n \l
If you are wondering how the /data/debian
came to be, I first created
the relevant folder in my host machine (via Debian's instructions) and then used ADB's
port forwarding powers - and netcat:
In the tablet:
host$ adb shell shell@K01E_2:/ $ /sbin/su root@K01E_2:/ # cd /data root@K01E_2:/ # mkdir debian root@K01E_2:/ # cd debian root@K01E_2:/ # nc -l -p 9999 | tar xpvf -
In the host:
host$ adb forward tcp:9999 tcp:9999 host$ sudo -i host# cd /opt/chroot-armie host# tar cpf - ./* | nc localhost 9999
I enjoyed my newfound powers by setting up the chroot-ed Debian to do my
bidding - and as it always does, Debian worked perfectly. I did some
bind-mounting before chrooting to make sure it had access to everything
in the tablet (and not just under the chroot-ed folder), which allowed
me to e.g. process my recorded videos and images from within the tablet
with ImageMagick and ffmpeg
and mkvmerge
, and...
Oh wait a moment.
The camera isn't working?!?!
Googling about it... and look, I am not alone: tons of people all over the world are reporting a common thing about rooting their Android devices...
"I don't use rooting because afterwards the camera isn't working".
Now I know more than enough about this to be 100% sure: rooting has NOTHING to do with the camera! The simple thing is, the kernel sources that I just compiled obviously have no device drivers for the camera in them!
Why?
I am guessing the answer is closely related to patents and NDAs and similar (expletive).
(sigh)
BTW, this means there is no chance to compile a CyanogenMod distribution for my tablet; best-case scenario, I would succeed but lose the camera functionality in the process. And God knows what else - the GPS, etc :-(
I have to go back to my original plan - boot from the pristine boot
image burned in the tablet, and become root ONLY through modifications
to my /system
partition.
Which I can modify now - but modify what?
Accomplishing rooting with the pristine boot image, means doing so with full SELinux and capability bounding sets in place.
Which can only mean one thing - I needed to find a way to run something as root BEFORE the parent process that spawns me ends up severely limited.
Where?
This took me even more time to find... but eventually, I noticed that the tablet upgrade (the Over-The-Air update from Asus that moved the tablet from KitKat (Android 4.4) to Lollipop (Android 5.1), left behind... a gift for me:
host$ adb shell shell@K01E_2:/ $ /sbin/su root@K01E_2:/ # cd /system/bin root@K01E_2:/system/bin # cat install-recovery.sh #!/system/bin/sh if ! applypatch -c EMMC:/dev/block/platform/msm_sdcc.1/by-name/\ recovery:7847936:c8bd9025173cb3ab0f467f11291b10f517e95791; then applypatch -b /system/etc/recovery-resource.dat \ EMMC:/dev/block/platform/msm_sdcc.1/by-name/boot:7368704:\ 9c5998b970de2449b4c73b890c44f8e68b2a84da \ EMMC:/dev/block/platform/msm_sdcc.1/by-name/recovery \ c8bd9025173cb3ab0f467f11291b10f517e95791 7847936 \ 9c5998b970de2449b4c73b890c44f8e68b2a84da:\ /system/recovery-from-boot.p && \ log -t recovery "Installing new recovery image: succeeded" \ || log -t recovery "Installing new recovery image: failed" else log -t recovery "Recovery image already installed" fi
Hello - what's this?
This code clearly needs to run with god-like privileges - it is supposed to patch the recovery partition with the new recovery image contained in the update, and it obviously needs to do this rather early in the boot time.
Can I hook stuff in here?
root@K01E_2:/system/bin # vi install-recovery.sh ... root@K01E_2:/system/bin # cat install-recovery.sh ... log -t recovery "Recovery image already installed" fi /system/xbin/telnetd -b 127.0.0.1:2323 -l /system/xbin/bash
(Notice that I copied all the busybox stuff from under /sbin - that is, from the modified boot image - to under /system/xbin ; since the plan is to boot from a normal boot image, that won't have anything under /sbin except what Asus placed in there).
And there it was - I added the proper music in the background and rebooted:
host$ adb reboot bootloader ... host$ sudo fastboot oem reset-dev_info host$ sudo fastboot reboot ... host$ adb shell shell@K01E_2:/ $ telnet 127.0.0.1 2323 Entering character mode Escape character is '^]'. / #
Finally!
Finally, I streamlined the process with a fake 'su' that I placed under /system/xbin:
/ # cat /system/xbin/su #!/system/bin/sh echo "=========================================================" echo "Feel free to run 'r' to get to a full Debian." echo "" echo "Telneting into the tunnel..." echo "=========================================================" /system/xbin/telnet 127.0.0.1 2323
So my tablet, in pristine boot condition, with no tethering requirements whatsoever, was finally rooted; in an open-source way, with no mysterious dependencies on external servers [2], and with the camera still working.
And is now proudly running a Debian chroot, of course :-)
There were many points during this journey that I felt really sad. It was as if Android creators absolutely HATED people like me, and did their best to make us suffer; because they group us together with malware authors; and add multiple layers of checks that don't distinguish between the owner of the machine and the developers of applications that run in the machine.
I am NOT a bad guy!... I just want to remain in full control of my OWN hardware...
What makes this sad is that "security" and "freedom" are not mutually exclusive - and Google already knows that this dichotomy is a false one. Their Chromebooks used to have hardware switches that the user had to set by hand, in order to enter "developer mode". This meant the owners of the machines had the freedom to root them, install chroots or native Linux distros, and generally do whatever they want with the hardware they purchased - if they chose to do so and accept the risks involved.
While malware authors still had to face pains like those I described above.
(Not that it stops them, BTW...)
I shudder to think what I will face the next time the HW of my Android tablet or phone dies. The way things are going in the Android ecosystem, freedom to "tinker" will be gone; sacrificed forever in the delusionary altar of "safety-uber-alles".
And if we are going that way... Android might as well be called iOS.
Discussions in Slashdot - Reddit/programming - Reddit/Linux - Reddit/ReverseEngineering - Hacker News - Hackaday
I wish the process was as smooth as I described it in the storyline above. In reality, as you can see in my two questions to Android Stack Exchange, and the one in Electronics Stack Exchange, the process is completely chaotic - there is no plan; you just try to make sense of all the complex factors that affect what is happening. As far as mental exercises go, this one was just as hard as the toughest ones I've ever met, and stretched my debugging muscles to their breaking point (which is good; that's how they get stronger). Huge thanks go out to the many people out there that work on this kind of thing and offer the resulting open-source tools and rooting instructions to mere mortals like me - probably without ever getting a "thank you" in return. I hope this page will act as my way of saying: "I now know what you guys go through - thank you so much!"
Well, there are some closed-source "one-click roots" that seem to, er, communicate with servers in China; supposedly to obtain the "proper" kernel exploit. Much as I respect the hacking power behind this... I am sorry, but I can't trust that you guys won't install a keylogger, or steal my GMail credentials, or make my tiny tablet a part of the ARMies of Mordor that have been known to recently be used in DoS attacks.
The details of Android rooting keep changing of course, as versions of Android constantly evolve. What I described above is most likely no longer applicable to any tablet or phone running Android 6.x.
Index | CV | Updated: Wed Nov 15 20:33:06 2023 |
The comments on this website require the use of JavaScript. Perhaps your browser isn't JavaScript capable; or the script is not being run for another reason. If you're interested in reading the comments or leaving a comment behind please try again with a different browser or from a different connection.