A collection of research, discoveries, and notes from exploring the Meta Quest 2 and Horizon OS with root and ADB access.
This repository documents things found while looking through the Quest's Android userspace, boot chain, partitions, system applications, kernel, hardware interfaces, and Horizon OS components.
Most of this is experimental research rather than a guide.
The Quest 2 runs an Android-based operating system with a heavily modified userspace provided by Meta.
Current observations from a rooted Quest 2:
Architecture: aarch64
Kernel: 4.19.325-cip128-st12-g4b63ca9fd613
Shell: Toybox
The underlying Android environment exposes the usual /proc, /sys, /data, /system and /system_ext filesystems, alongside a number of Meta-specific services and components.
Root access provides considerably more visibility into the headset than the normal Android shell.
Things investigated so far include:
- Android system properties
- Kernel information
- Block devices and partitions
- Power supply interfaces
- LED interfaces
- Horizon OS applications
- VrShell
- Boot animation resources
- Boot images
- ABL
- Fastboot
- System applications
- Android framework components
- GPU interfaces
- Qualcomm hardware interfaces
The rooted shell is running Android's Toybox utilities rather than a normal GNU userspace.
The Android HOME activity resolves to Meta's VrShell application.
Running:
cmd package resolve-activity -c android.intent.category.HOME -a android.intent.action.MAIN
returns:
Activity:
com.oculus.vrshell.HomeActivity
Package:
com.oculus.vrshell
Process:
com.oculus.vrshell
The application is located at:
/system_ext/priv-app/VrShell/VrShell.apk
Its application class is:
com.oculus.vrshell.ShellApplication
The package runs as UID 10048.
This makes VrShell one of the most important parts of the Horizon OS userspace and one of the main areas worth investigating.
A large portion of the Quest's functionality is provided by Android system and privileged applications.
Applications can be found under locations such as:
/system/app/
/system/priv-app/
/system_ext/app/
/system_ext/priv-app/
The VrShell package is an example of a privileged system application:
/system_ext/priv-app/VrShell/VrShell.apk
Some system applications use Android's optimized bytecode format, meaning the APK may not contain all of the original executable bytecode directly.
This makes deodexing and reverse engineering necessary when investigating older Quest system applications.
The Quest uses a Qualcomm-based Android boot chain with multiple bootloader components and A/B partitions.
Known components include:
XBL
CDT
DDR
RPM
TZ
HYP
PMIC
MODEM
ABL
KEYMASTER
BOOT
CMNLIB
CMNLIB64
DEVCFG
Many of these exist as both _a and _b slots.
The block-device names can be inspected through:
/dev/block/by-name/
On the Quest 2, examples include:
boot_a
boot_b
abl_a
abl_b
vbmeta_vendor_a
vbmeta_vendor_b
Not every partition described by older research is necessarily present under the same name on newer firmware.
ABL is responsible for the Quest's fastboot environment and a number of Meta-specific bootloader operations.
Older Quest research identified several ABL versions, including:
213561.4150.0
256550.6810.0
333700.2680.0-396520.6170.115
ABL contains Oculus-specific modifications to the normal Android fastboot implementation.
Known OEM commands include:
oem device-info
oem reboot-edl
oem reboot-sideload
oem shutdown
oem sha1
oem partition-info
oem set-verity
oem set-verified-boot
oem get-kernel-flavor
oem update-all-slots
oem off-mode-charge
oem enable-charger-screen
oem disable-charger-screen
oem set-retail-keymaster
oem read-persist
oem write-persist
oem set-serial-number
oem set-retail-device
The exact commands and restrictions depend on the ABL version and device state.
The Quest has a Qualcomm Emergency Download mode in addition to its Android fastboot environment.
EDL can be entered through the hardware button combination on supported firmware, while fastboot can be entered using the bootloader controls or ADB when available.
The bootloader exposes additional commands for moving between these modes, including:
oem reboot-edl
oem reboot-sideload
The Quest uses multiple logical and physical partitions.
Older Quest research identified partitions including:
system_a
system_b
private
vision
userdata
xbl_a
xbl_b
cdt
ddr
rpm_a
rpm_b
tz_a
tz_b
hyp_a
hyp_b
pmic_a
pmic_b
modem_a
modem_b
bluetooth_a
bluetooth_b
abl_a
abl_b
keymaster_a
keymaster_b
boot_a
boot_b
cmnlib_a
cmnlib_b
cmnlib64_a
cmnlib64_b
devcfg_a
devcfg_b
Additional partitions include areas used for:
persist
misc
keystore
frp
devinfo
dip
apdp
msadp
splash
logfs
logdump
storsec
modemst1
modemst2
fsg
fsc
Partition layouts can change between firmware versions, so older layouts should not be assumed to exactly match newer Horizon OS releases.
Partition information can be queried from the bootloader using:
oem partition-info
The boot partitions have also been investigated directly through:
/dev/block/by-name/boot_a
/dev/block/by-name/boot_b
ABL images have been extracted for further analysis.
One ABL image investigated was:
2,097,152 bytes
Standard Android Image Kitchen tooling did not directly unpack the image, suggesting that the Quest's ABL packaging differs from a normal Android boot image.
Further reverse engineering is still needed here.
The bootloader contains controls for Android verified boot and dm-verity.
Known commands include:
oem set-verity
oem set-verified-boot
The ability to use these commands depends on the state of the device and the particular ABL version.
The Quest therefore combines standard Android verified boot mechanisms with Meta-specific bootloader restrictions.
Older Quest research found that legitimate bootloader unlocking involves an unlock_token partition.
The token contains a bootloader script and a signature.
The signature contains information identifying the target device, including its serial number.
The signature uses RSA-PSS with SHA-256.
The bootloader verifies the token before accepting the unlock operation.
The exact unlocking behaviour is firmware-dependent.
Quest firmware is distributed through OTA packages.
Factory firmware and OTA packages can be extracted using Android OTA extraction tools.
Incremental OTA packages require additional handling because of their structure.
Firmware research is useful for comparing changes between Horizon OS versions, especially changes to:
- ABL
- Kernel
- VrShell
- System applications
- Boot images
- Partition layouts
- Security mechanisms
Horizon OS contains Meta-specific boot animation resources.
On the Quest 2, these were found under:
/system_ext/etc/bootanim/
Files discovered include:
meta.bin
meta-horizonos.glb
meta-colors.png
meta-animation-config.json
The presence of:
meta-horizonos.glb
suggests that part of the boot animation uses a 3D GLB asset.
The configuration and supporting resources are also stored directly on the system partition.
The headset exposes its status LEDs through:
/sys/class/leds/
The Quest 2 exposes:
blue
green
red
These can be inspected and controlled through the Linux LED subsystem when sufficient permissions are available.
This provides a simple example of how hardware functionality is exposed to the Android userspace.
Battery information is exposed through Android's Linux power-supply subsystem:
/sys/class/power_supply/
The battery interface includes information such as:
capacity
status
For example:
/sys/class/power_supply/battery/capacity
/sys/class/power_supply/battery/status
This allows battery information to be read directly without using the Horizon OS interface.
The Quest's GPU is exposed through the Qualcomm KGSL driver.
One of the interfaces investigated is:
/sys/class/kgsl/kgsl-3d0/
This provides access to information exposed by the Qualcomm GPU driver.
The GPU information can also be discovered through Android system properties depending on the firmware.
CPU and SoC information can be obtained through a combination of:
/proc/cpuinfo
and Android properties such as:
ro.soc.model
ro.hardware
The Quest 2 uses a Qualcomm Snapdragon platform and exposes the underlying hardware through the normal Linux and Android hardware interfaces.
qfetch is a small system information utility written specifically for the Quest.
It is designed to work directly from the rooted Android shell without requiring a large dependency chain.
Current information includes:
Device
Codename
Hardware
Android
Build
Kernel
Architecture
CPU
GPU
User
Home
Home Process
Battery
Status
The HOME process is detected dynamically by resolving Android's HOME activity:
cmd package resolve-activity \
-c android.intent.category.HOME \
-a android.intent.action.MAIN
The current stock Quest environment resolves this to:
com.oculus.vrshell
The Quest has a companion system used by the Meta/Oculus mobile application.
The phone communicates with a server running on the headset.
Older Quest research identified this component as:
CompanionServer.apk
The companion system communicates over Bluetooth Low Energy.
The Quest exposes a custom BLE GATT service for companion communication.
The service identified by earlier research is:
Companion
UUID: 0000FEB8-0000-1000-8000-00805F9B34FB
Two known characteristics are:
ccs
7a442881-509c-47fa-ac02-b06a37d9eb76
status
7a442666-509c-47fa-ac02-b06a37d9eb76
The characteristics use the standard BLE Client Characteristic Configuration descriptor:
00002902-0000-1000-8000-00805F9B34FB
The companion protocol is layered.
The transport layer splits larger messages into BLE-sized chunks.
The chunks contain a two-byte header with a sequence number, with the high bit used to indicate the final chunk.
The application protocol uses Protocol Buffers.
Messages are exchanged as Request and Response structures.
The companion connection uses public-key cryptography to establish a shared secret.
During the connection handshake:
- The Quest generates a key pair.
- The client generates a key pair.
- Public keys are exchanged.
- A shared secret is derived.
- Later communication is encrypted.
Older research found the cryptographic implementation inside:
libauthentication.so
which wraps libsodium.
Research into the companion protocol identified a large number of commands, including:
ADB_MODE_SET
ADB_MODE_STATUS
APP_LAUNCH
AUTHENTICATE
HELLO
DEV_MODE_SET
DEV_MODE_STATUS
HMD_STATUS
HMD_VERSION
HMD_CAPABILITIES
CONTROLLER_PAIR
CONTROLLER_SCAN
CONTROLLER_SCAN_AND_PAIR
CONTROLLER_STATUS
CONTROLLER_UNPAIR
WIFI_CONNECT
WIFI_DISABLE
WIFI_ENABLE
WIFI_FORGET
WIFI_RECONNECT
WIFI_SCAN
WIFI_STATUS
MTP_MODE_SET
MTP_MODE_STATUS
OTA_ENABLED_SET
OTA_ENABLED_STATUS
PIN_LOCK
PIN_RESET
PIN_SET
PIN_STATUS
PIN_UNLOCK
PIN_VERIFY
SYSTEM_UNLOCK
TEXT_SEND
TIME_SET
WIPE_DATA
There are also commands for settings, accounts, controllers, autosleep, locale, managed mode, crash reporting, mirroring and other headset functionality.
System applications can be investigated by extracting their optimized Android bytecode and converting it into a form suitable for analysis.
Older Quest research used tools including:
baksmali
smali
dex2jar
Bytecode Viewer
A typical workflow is:
.odex
|
v
baksmali
|
v
.smali
|
v
smali
|
v
.dex
|
v
dex2jar
|
v
.jar
The resulting JAR can then be inspected using a Java decompiler.
The Quest uses a modified Qualcomm Linux kernel.
Older Quest firmware was affected by CVE-2018-9568.
A kernel change was eventually introduced to address the vulnerability.
The Quest kernel is particularly interesting because it provides the interface between Horizon OS and the underlying Qualcomm hardware.
Current research includes looking at:
- Kernel configuration
- Drivers
- KGSL
- Power management
- Hardware interfaces
- Android framework interaction
- Meta-specific changes
Some of the more useful locations discovered during investigation include:
/system
/system_ext
/data
/proc
/sys
/dev
/dev/block/by-name
Meta-specific components are particularly concentrated in:
/system_ext/
This includes VrShell and several other Horizon OS components.
Areas still being investigated include:
- ABL internals
- Boot image formats
- Verified boot
vbmeta- Horizon OS internals
- VrShell
- CompanionServer
- Qualcomm hardware interfaces
- KGSL
- Kernel configuration
- System services
- Root persistence
- Boot-time execution
- Boot animation
- Partition differences between firmware versions
- Meta-specific Android modifications
- Fastboot behaviour
- EDL behaviour
- OTA changes
This repository is intended to grow as more of the Quest's internals are documented.
A lot of Quest behaviour changes between firmware versions.
Something that exists on one version may be renamed, moved, removed, or protected on another.
Older research from the QuestEscape project is useful for understanding the earlier Quest boot chain and software architecture, but it should not automatically be assumed to apply to current Quest 2 firmware.
The goal here is to document what can actually be observed and verified on the device rather than making assumptions about how Horizon OS works.
Credits to the repo https://github.com/QuestEscape for extra info about the bootloader and check out his github
My simple terminal app if you want to use is Qfetch
Type this in to terminal as root to get it
cat > /data/adb/bin/qfetch <<'EOF'
#!/system/bin/sh
# QuestFetch - lightweight Quest system information
DEVICE="$(getprop ro.product.model)"
CODENAME="$(getprop ro.product.device)"
HARDWARE="$(getprop ro.boot.hardware.revision)"
ANDROID="$(getprop ro.build.version.release)"
BUILD="$(getprop ro.build.display.id)"
KERNEL="$(uname -r)"
ARCH="$(uname -m)"
USER="$(id -un)"
HOME_DIR="${HOME:-$(getent passwd "$(id -u)" 2>/dev/null | cut -d: -f6)}"
BATTERY="$(cat /sys/class/power_supply/battery/capacity 2>/dev/null)"
STATUS="$(cat /sys/class/power_supply/battery/status 2>/dev/null)"
CPU="$(getprop ro.soc.model)"
[ -z "$CPU" ] && CPU="$(getprop ro.hardware)"
[ -z "$CPU" ] && CPU="$(grep -m1 'Hardware' /proc/cpuinfo | cut -d: -f2 | sed 's/^ *//')"
[ -z "$CPU" ] && CPU="Unknown"
GPU="$(cat /sys/class/kgsl/kgsl-3d0/gpu_model 2>/dev/null)"
[ -z "$GPU" ] && GPU="$(getprop ro.hardware.egl)"
[ -z "$GPU" ] && GPU="$(getprop ro.gfx.driver.0)"
[ -z "$GPU" ] && GPU="$(getprop ro.gfx.driver.0)"
[ -z "$GPU" ] && GPU="Unknown"
HOME_PROCESS="$(
cmd package resolve-activity \
-c android.intent.category.HOME \
-a android.intent.action.MAIN 2>/dev/null |
grep 'processName=' |
head -n 1 |
sed 's/.*processName=//' |
sed 's/ .*//'
)"
[ -z "$HOME_PROCESS" ] && HOME_PROCESS="Unknown"
printf '\n'
printf 'QuestFetch\n'
printf '────────────────────────────────────\n'
printf 'Device %s\n' "$DEVICE"
printf 'Codename %s\n' "$CODENAME"
printf 'Hardware %s\n' "$HARDWARE"
printf 'Android %s\n' "$ANDROID"
printf 'Build %s\n' "$BUILD"
printf 'Kernel %s\n' "$KERNEL"
printf 'Architecture %s\n' "$ARCH"
printf 'CPU %s\n' "$CPU"
printf 'GPU %s\n' "$GPU"
printf 'User %s\n' "$USER"
printf 'Home %s\n' "$HOME_DIR"
printf 'Home Process %s\n' "$HOME_PROCESS"
printf 'Battery %s%%\n' "$BATTERY"
printf 'Status %s\n' "$STATUS"
printf '────────────────────────────────────\n'
printf '\n'
EOF
chmod 755 /data/adb/bin/qfetch
export PATH="/data/adb/bin:$PATH"
qfetchThen run command qfetch and it should run