Table of Contents

  1. Setup
  2. Enumeration

Setup

Purchase

The following is written with a pixel 9 bought from the google store without financing. Do not unlock the bootloader or root your personal use cellphone. Unlocking the bootloader will erase data on the pixel 9 and if flashed incorrectly can brick the device.

If you don't already have a cable to connect the pixel to a laptop I would suggest the 4-1 100W cable from xcool since it can connect to all common kinds of USB ports.

Depending on your work the mission darkness faraday bag with window and usb passthrough works well. With max screen brightness you'll be able to observe the pixel 9 screen:

The pixel 9 has a variety of RF attack surface and can be interesting to reverse engineer. While there's cheaper ways to go about this one option is to use a B205 mini from Ettus Research.

Unlock Bootloader

Unlocking the bootloader can provide the following:

1. Enable developer mode:

2. Enable OEM unlocking

3. Install fastboot on a laptop/desktop: fastboot

4. Reboot device into fast boot mode

5. Fastboot unlock

This step will erase data

Each restart will display a warning regarding the bootloader being unlocked. Fastboot can be used to lock the bootloader.

Root

Root is not strictly required but can make your life much easier when working with a pixel. You'll be able to interact with more drivers, search the filesystem (ls /dev), disable selinux and more. Without root you'll still be able to ssh with a laptop over usb and install packages in termux.

With the bootloader unlocked Magisk can be used to provide root.

1. Record the build number to download correct factory image.

Recording an incorrect build number can brick your device

2. Obtain a factory image from factory images for your device/build number.

Links to each factory image for each variant of the pixel devices:

Take note of image variants for Verizon, DTAG, TW, EMEA, T-Mobile and Telestra. You don't want to mix and match variants (don't flash a Verizon variant if you didn't buy the pixel 9 from verizon). The outcome is not tested.

3. After downloading the image check the sha256 hash matches.

If the hash doesn't match you will likely brick your device

4. Extract init_boot.img from factory image on laptop

  • unzip tokay-bp1a.250505.005-factory-5e869188.zip
  • unzip image-tokay-bp1a.250505.005.zip
  • Observe init_boot.img

5. Download Magisk

6. Connect and Enable USB File Transfer.

  • Move init_boot.img to Download folder of pixel's internal storage
  • Move Magisk apk to Download folder of pixel's internal storage

7. Install Magisk apk

  • Enable: Install unknown apps
  • Click Magisk apk in Downloads folder to install

8. Patch init_boot.img from Magisk

  • Execute Magisk: Install
  • Select and Patch a File
  • Select: init_boot.img (if init_boot.img is incorrect device will be bricked after flashing)
  • Select: LET'S GO
  • A newly created magisk_patched-29000_oMslj.img should be created next to init_boot.img on the pixel
  • Transfer magisk_patched-29000_oMslj.img to your laptop

9. Enable Developer Mode / USB Debugging

  • Enable developer mode again since it could be wiped after unlocking the bootloader
  • Enable USB Debugging for adb
  • Trust laptop for usb debugging

10. Reboot into bootloader

  • Execute on laptop: adb devices
  • A single device (your pixel phone) should be listed from previous command
  • Execute on laptop: adb reboot bootloader
  • Pixel should restart into fastboot mode

11. Flash patched init_boot.img

  • Execute on laptop: fastboot flash init_boot /path/to/patched_init_boot.img
leland@light:~/Downloads/$ fastboot flash init_boot ./magisk_patched-29000_oMsIj.img 
Sending 'init_boot_a' (8192 KB)                    OKAY [  0.364s]
Writing 'init_boot_a'                              OKAY [  0.074s]
Finished. Total time: 0.442s
        
  • Execute on laptop: fastboot reboot
Pixel is now rooted

Finsh Setup QoL

1. Install Termux. Download the latest non-beta release for arm64 from

2. Execute in Termux: su

3. Grant Termux Root

  • Execute in termux: su
  • Magisk prompt will appear: grant termux superuser priv

4. Execute in Termux: pkg install openssh clang vim

5. Execute in Termux (pick password): passwd

6. SSH over usb with adb and termux

  • Execute in Termux: sshd
  • Execute on laptop: adb forward tcp:8022 tcp:8022
  • Execute on laptop: ssh -p 8022 localhost

Enumeration

Background: Drivers

On android and unix in general a special file called a character device driver exists to control physical hardware (gpu, camera, microphones, battery, gps, usb, etc). These files expose an interface for userland to interact with physical hardware/kernel module. This is done by the OS redirecting calls such as open, ioctl, read, write specified by the structure file_operations.

static const struct file_operations kbase_fops = {
	.owner = THIS_MODULE,
	.open = kbase_open,
	.release = kbase_release,
	.read = kbase_read,
	.poll = kbase_poll,
	.unlocked_ioctl = kbase_ioctl,
	.compat_ioctl = kbase_ioctl,
	.mmap = kbase_mmap,
	.check_flags = kbase_check_flags,
	.get_unmapped_area = kbase_get_unmapped_area,
};
In the above example userland calls to open of the character device is redirected to kbase_open and ioctl calls to the opened device is defined by kbase_ioctl and so on.

Direct Enumeration

To list character devices (root required):

# find / -type c 2>/dev/null | sort

SELinux Enumeration

SELinux (Security-Enhanced Linux) is a mandatory access control. SELinux specifies rules of how different context can interact. Multiple files can be in a single context. Each file contains context information in xattr.

The context of /dev/mali0 is gpu_device shown by ls -Z


:/ # ls -Z /dev/mali0
u:object_r:gpu_device:s0 /dev/mali0
        

Code snippet to get current context


#include "stdio.h"
#include "selinux/selinux.h"
#include "stdlib.h"

int main(void) {
  //check if selinux enabled
  if (is_selinux_enabled() != 1) {
    printf("selinux not enabled\n");
    return 0;
  }
  char *ctx = NULL;
  if (getcon(&ctx) == 0 && ctx) {
    printf("%s\n", ctx);
    freecon(ctx);
  }
  return 0;
}

Compile and execute:


$ clang main.c -lselinux
$ ./a.out
u:r:untrusted_app_27:s0:c32,c257,c512,c768
$ su
# run-as com.untrusted /data/data/com.untrusted/a.out
u:r:runas_app:s0:c33,c257,c512,c768

These rules can be reviewed to help understand the overall system and answer important questions such as: what can I do with an untrusted context?

SELinux rules are stored in a policy file on pixel9 is located /sys/fs/selinux/policy and can be parsed with sesearch. I have dockerized sesearch here with a parser to generate d3 javascript graphs of the contexts and permissions.

  • 16.0.0 (BP2A.250605.031.A2, Jun 2025)
  • allow and allowxperm rules: policy
  • allow and allowxperm rules (character device file): policy
  • 15.0.0 (BP1A.250505.005, May 2025)
  • allow and allowxperm rules: policy
  • allow and allowxperm rules (character device file): policy
  • allow and allowxperm rules (character device file and untrusted context): policy

Untrusted Context

Applications have untrusted_app context by default, making untrusted_app and untrusted_app_all highly valuable to review to prioritize time.

run-as in conjuction with a debuggable apk is a convenient way to iteratively execute code in an untrusted context.

1. Install apk (laptop):


leland@rising pixel9 % adb install app-debug.apk
Performing Streamed Install
Success

2. Prep (on pixel):


# cp a.out /data/data/com.untrusted
# pkg_uid=$(stat -c %u /data/data/com.untrusted)
# chown $pkg_uid:$pkg_uid /data/data/com.untrusted/a.out
# chmod 700 /data/data/com.untrusted/a.out

3. execute from untrusted context:


# run-as com.untrusted /data/data/com.untrusted/a.out

Untrusted Allow Rules: BP2A.250605.031.A2: June 2025

All allow with untrusted context source

Open rules with untrusted context source

edgetpu_device and gxp_device: BP2A.250605.031.A2: June 2025

usb_device and usbaccessory_device allow open: BP2A.250605.031.A2: June 2025

Character Device Context: BP2A.250605.031.A2: June 2025

Graph of character device context with neighbors of distance 1

Note that magisk will be included in this graph if used to root