Userspace Interaction with the Hypervisor
OS-Independent Breakpoints
Sometimes it's desirable to break into the kernel debugger from user software running in EL0. Inserting a BRK opcode usually results in it being intercepted by the operating system kernel. But if you want to end up directly in the kernel debugger instead, you can use the following opcode sequence:
EL1 (64-bit)
hvc #0x7242
iOS EL0 (64-bit)
mrs xzr, cntpct_el0
hvc #0x7242
Linux EL0 (64-bit), Android EL0 (64-bit)
mrs xzr, pmcr_el0
hvc #0x7242
Linux EL0 (32-bit), Android EL0 (32-bit)
mrc p15,#0,r0,c9,c12,#0
hvc #0x7242
After running this code, the device will enter a paused state. If a debugger is attached, this will result in the debugger registering a debug event (similar to a breakpoint).
Console Output from Anywhere in the VM
Sometimes, when writing patches to kernel or user applications, it's nice to be able to print output without having to ask the operating system to do so. It can be fairly hard to get to debug output from some places, such as EL0 software that has no standard output and disabled debugging calls.
This feature helps you get debug output from patches, shellcodes, or maybe just helper programs without having to deal with operating system constraints.
Both EL0 and EL1 codes can print directly to the device's console via a special HVC (hypervisor call). The form of the HVC itself depends on the environment:
EL1 (64-bit)
hvc #0x6C43
ciOS EL0 (64-bit)
mrs xzr, cntpct_el0
hvc #0x6C43
Linux EL0 (64-bit), Android EL0 (64-bit)
mrs xzr, pmcr_el0
hvc #0x6C43
Linux EL0 (32-bit), Android EL0 (32-bit)
mrc p15,#0,r0,c9,c12,#0
hvc #0x6C43
To use the HVC, set registers x0 .. x2 (or r0 .. r2 for 32-bit code) to the following values:
CONSLOG_REQ_STR:
prints a zero-terminated (C) string
x0 = 0xFFFF0000
x1 = pointer to string
CONSLOG_REQ_U64:
prints a number as a hex
x0 = 0xFFFF0001
x1 = number
CONSLOG_REQ_S64:
prints a number as a signed decimal
x0 = 0xFFFF0002
x1 = number
CONSLOG_REQ_HEX:
prints a buffer as a hex dump
x0 = 0xFFFF0003
x1 = pointer to buffer
x2 = size in bytes
The memory printing calls (CONSLOG_REQ_STR and CONSLOG_REQ_HEX) return status in x0. If negative, the call failed. If not negative, returns the number of bytes retrieved from the buffer (or string). The other calls simply return zero in x0.
Text printed through this output path will show up in the device console, in cyan color using ANSI control characters.
An example of the string printing function, including handling EL0 page faults, is in the Corellium GitHub repository at guest-tools - the conslog
program will echo its standard input to the VM console (in the highly visible cyan color of hypervisor messages).
Discover more insights into mobile app virtualization and userspace interactions — book a meeting with Corellium today!