I was looking at the web search results of “how to manually root Android devices†and I noticed that most of those are either just advertising stuff or makes use of dedicated (particularly closed source) rooting solutions. Those which worked some time ago are now obsolete since they depended on some security vulnerability in Android which was fixed over time.
In fact it's not impossible to root phone manually but it's not worth the hassle. However as a reference, let me describe an option. But before diving into the technical details, we need to understand what root is and how Android prevents root access. Details can be found here but a few points in short:
- Mostly vendors ship their devices with locked bootloader, and a chain of trust is established during boot process, which won't let any modification be done to kernel (
boot.img
), recovery or the main OS. So the first step towards rooting is unlocked bootloader. Beware of the risks!
- A part of the chain of trust is
dm-verity
(VB
/AVB
);
a kernel based phenomenon which makes sure that partitions containing core OS (/system
, /vendor
, /odm
) are always mounted read-only and any malicious attempt to modify them must fail. Modifying boot.img
or recovery.img
is usually accompanied by disabling dm-verity
to avoid surprises like bootloops at later stages. TWRP's standard warning:
This device uses dm-verity!
This means that swiping to allow system modifications will prevent you from being able to boot if you are using the stock kernel.
- Similarly some vendors particularly Samsung go beyond normal and build extra security features in their devices like Knox, RKP, Defex, proca, TIMA, FIPS, bla bla bla. So you might also need to circumvent those e.g. by building kernel with
SECURITY_SELINUX_DEVELOP=y
, patching kernel binary etc.
- Android apps don't have capabilities to elevate their privileges by executing binaries which have
setuid
or file capabilities set (which is the standard way to get root access). So the only option is to run a background persistent process (daemon) with root privileges outside the apps (e.g. during boot process) and request it to do privileged tasks on behalf of unprivileged apps when needed.
- Root (i.e. UID 0) is the old Discretionary Access Control (DAC), but Android also makes use of Mandatory Access Control (MAC) i.e. SELinux. A root process running with restricted SELinux context is quite helpless, so we have to break this barrier too. It requires modifying SELinux policy.
I'll address the last two points here, but first two considerations:
- Android rooting solutions deploy a special
su
binary which - when executed by an app - makes a connection to privileged daemon and gets the app a root shell. So this su
binary and daemon are specially developed for this purpose. A less sophisticated approach would be to run already available daemons like adbd
, sshd
or one of the old inetutils flavors (telnetd
, rlogind
, rshd
, rexecd
) with root privileges. When running in background, these servers can provide a root shell or execute commands with root privileges when connected by a client. For the sake of simplicity I'm using only a minimal network utility named netcat
(nc
) which is a busybox applet. But please note that apps can't get root access with this approach, it can only be used from commandline.
- We are going to patch monolithic
/sepolicy
file and /init.rc
file which are part of ramdisk
in boot.img
. But starting with Treble Android uses split policy which is loaded/compiled from /system/etc/selinux/
and /vendor/etc/selinux/
. Starting with SAR, there's no ramdisk at all in boot.img
and /init.rc
is part of system.img
. So in both cases you need to necessarily modify system
partition. I'm not giving details on that here.
STEPS:
- Extract
boot.img
e.g. using AIK or magiskboot
on Android or PC.
Create a new SELinux context, say pseudo_su
. Set it permissive so that to allow any possible interactions with other processes / files etc. Use Magisk's supolicy
tool or sepolicy-inject
(1, 2) on Android or PC:
~# supolicy --load sepolicy --save sepolicy 'create pseudo_su' 'permissive pseudo_su' 'dontaudit pseudo_su * * *' 'allow pseudo_su * * *' 'allow * pseudo_su * *'
Or to build split-policy
using Android's compiler:
~# /system/bin/secilc -m -M true -G -N -c $(cat /sys/fs/selinux/policyvers) -o sepolicy /system/etc/selinux/plat_sepolicy.cil /system/etc/selinux/mapping/$(cat /vendor/etc/selinux/plat_sepolicy_vers.txt).cil $([ -f /vendor/etc/selinux/vendor_sepolicy.cil ] && echo /vendor/etc/selinux/vendor_sepolicy.cil /vendor/etc/selinux/plat_pub_versioned.cil || echo /vendor/etc/selinux/nonplat_sepolicy.cil)
* Get the value of POLICYDB_VERSION_MAX
from your kernel source.
To use vendor's precompiled_sepolicy
or build from split-policy
and patch:
~# supolicy --load-split --save sepolicy 'create pseudo_su' 'permissive pseudo_su' 'dontaudit pseudo_su * * *' 'allow pseudo_su * * *' 'allow * pseudo_su * *'
Define an init
service which starts a simple TCP server on boot, only listening to on-device connections:
# /init.rc
...
service pseudo_su /sbin/busybox nc -lk -s 127.0.0.1 -p 23 -e /sbin/busybox sh
seclabel u:r:pseudo_su:s0
disabled
on property:sys.boot_completed=1
start pseudo_su
* Make sure you get the right busybox
binary, there are multiple implementations of netcat
.
* Use port 23
or any other unused port.
- Copy updated
sepolicy
and init.rc
to root of extracted ramdisk, busybox
binary to [ramdisk/]sbin/
and set permissions.
- Repack
boot.img
and flash or test with fastboot boot boot.img
.
HOW TO RUN ROOT COMMANDS?
Once booted, we can pass commands from a netcat
client on terminal emulator app (like Termux) or adb shell
:
~$ echo id | /sbin/busybox nc localhost 23
uid=0(root) gid=0(root) groups=0(root) context=u:r:pseudo_su:s0
* Default SELinux policy doesn't allow apps traverse /sbin
. So either inject allow rules or put busybox
in /system/*bin/
. Or use some other nc
e.g. from Termux's netcat
package.
For ease of use, create functions (put them in .bashrc
so that you don't have to define every time):
# ~/.bashrc
...
function psu() {
echo "$@ 2>&1" | /sbin/busybox nc localhost 23;
}
function psush() {
/sbin/busybox nc localhost 23;
}
~$ ls -ld /data/adb
ls: cannot access '/data/adb': Permission denied
~$ psu ls -ld /data/adb
drwx------ 7 root root 3488 2019-07-19 00:44 /data/adb
To get root shell:
~$ psush
whoami
root
^C
But it's just a dumb shell, not connected to terminal. To have a more feature-rich experience, other tools like socat
can be used which support line editing, pseudo-terminals etc.
Also environment variables aren't evaluated unless explicitly passed, because commands are executed remotely:
~$ /data/data/com.termux/files/usr/bin/ps -p $$,1 -o pid=,comm=
23599 bash
~$ psu /data/data/com.termux/files/usr/bin/ps -p $$,1 -o pid=,comm=
CANNOT LINK EXECUTABLE "/data/data/com.termux/files/usr/bin/ps": library "libprocps.so" not found
~$ psu LD_LIBRARY_PATH=/data/data/com.termux/files/usr/lib /data/data/com.termux/files/usr/bin/ps -p $$,1 -o pid=,comm=
1 init
23599 bash
So that's how we can get a minimal root functionality without using any specialized rooting tools.
RELATED: