Table of Contents

Corellium virtual machines offer user programs running inside the CHARM hypervisor a way to access either kernel or physical views of VM RAM. This allows users to write research tools that do not rely on other, more complex, paths to gain that privilege, which is normally reserved to the kernel.


The Access Interface

The access interface has two parts. The first part allows for obtaining information on the kernel location in memory, as well as current values of relevant Arm system registers. The second part operates like a privileged equivalent to the memcpy function, adding the ability to copy data across typically privileged address space boundaries.

Note: kernel memory page faults are not supported; they will result in partial copy (and return value set appropriately). User page faults are supported.

Only 64-bit EL0 code can use this interface, and is accessed via the use of HVC instructions. The exact syntax form differs slightly between iOS and Android VMs. Both are shown below, and are also available in the Corellium GitHub as guest-tools, along with examples.


EL0 Code

iOS EL0 (64-bit)

.align 4
.global _get_kern_info
_get_kern_info:
mrs xzr, cntpct_el0
hvc #0x9402
ret

.align 4
.global _unicopy
_unicopy:
uxtb x0, w0
orr x5, x0, #0x100
mov x4, #0
mov x6, #16
1:
cbz x3, 2f
tst x5, #12
bne 4f
ldrb w0, [x2]
4:
tst x5, #3
bne 5f
strb w0, [x1]
5:
mov x0, x5
mrs xzr, cntpct_el0
hvc #0x9402
cbnz x0, 3f
sub x6, x6, #1
cbz x6, 2f
b 1b
3:
mov x6, #16
add x1, x1, x0
add x2, x2, x0
sub x3, x3, x0
add x4, x4, x0
b 1b
2:
mov x0, x4
ret

Android and Linux EL0 (64-bit)

.align 4
.global get_kern_info
get_kern_info:
mrs xzr, pmcr_el0
hvc #0x9402
ret

.align 4
.global unicopy
unicopy:
uxtb x0, w0
orr x5, x0, #0x100
mov x4, #0
mov x6, #16
1:
cbz x3, 2f
tst x5, #12
bne 4f
ldrb w0, [x2]
4:
tst x5, #3
bne 5f
strb w0, [x1]
5:
mov x0, x5
mrs xzr, pmcr_el0
hvc #0x9402
cbnz x0, 3f
sub x6, x6, #1
cbz x6, 2f
b 1b
3:
mov x6, #16
add x1, x1, x0
add x2, x2, x0
sub x3, x3, x0
add x4, x4, x0
b 1b
2:
mov x0, x4
ret


Header File

The following header file declares the interface for both:

#define KERN_INFO_VA 0x00
#define KERN_INFO_PA 0x01

#define KERN_INFO_TPIDR_EL1 0x40
#define KERN_INFO_TTBR0_EL1 0x41
#define KERN_INFO_TTBR1_EL1 0x42
#define KERN_INFO_TCR_EL1 0x43
#define KERN_INFO_VBAR_EL1 0x44
#define KERN_INFO_CONTEXTIDR_EL1 0x45

// access kernel-specific information based on the specified KERN_INFO_* parameter
uintptr_t get_kern_info(unsigned int key);

#define UNICOPY_DST_USER 0 // Copy to user virtual address space
#define UNICOPY_DST_KERN 1 // Copy to kernel virtual address space
#define UNICOPY_DST_PHYS 2 // Copy to physical address
#define UNICOPY_SRC_USER 0 // Copy from user virtual address space
#define UNICOPY_SRC_KERN 4 // Copy from kernel address space
#define UNICOPY_SRC_PHYS 8 // Copy from physical address

// returns amount of data copied successfully
// mode: the bitwise OR of a UNICOPY_DST_* and UNICOPY_SRC* parameter, specifying the
// address space to copy to and from.
// dst: the address to copy to in the UNICODE_DST_* address space
// src: the address to copy from in the UNICODE_SRC_* address space
// size: the number of bytes to copy from one address space to the other.
size_t unicopy(unsigned int mode, uintptr_t dst, uintptr_t src, size_t size);



Did this answer your question?