IntelLabs / IntelLabs/kAFL

Add support for VMware port IO backdoor as secondary hypercall interface

Open
#208 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Makefile
Stars
814
Forks
109
PR merge metrics
No merged PRs in 30d

Description

This ticket explores what's the status of implementing the Nyx hypercall API via the VMware port IO backdoor.

# kAFL agent
On the guest side, in [`libnyx_agent.c`](https://github.com/IntelLabs/kafl.targets/blob/master/linux-user/libnyx_agent/src/nyx_agent.c#L117), there is a dispatcher already implemented

~~~c
/**
* Execute hypercall depending on Nyx CPU type
*/
unsigned long hypercall(unsigned id, uintptr_t arg)
{
switch (nyx_cpu_type) {
case nyx_cpu_v1:
debug_printf("\t# vmcall(0x%x,0x%lx) ..\n", id, arg);
return kAFL_hypercall(id, arg);
case nyx_cpu_v2:
case nyx_cpu_none:
debug_printf("\t# vmcall(0x%x,0x%lx) skipped..\n", id, arg);
return 0;
case nyx_cpu_invalid:
default:
fprintf(stderr, "get_nyx_cpu_type() must be called first\n");
habort_msg("get_nyx_cpu_type() must be called first\n");
assert(false);
}
}
~~~

depending on the CPU type returned by CPUID.
~~~c
/**
* Get Nyx VMM type from CPUID
*/
static nyx_cpu_type_t _get_nyx_cpu_type(void)
{
uint32_t regs[4];
char str[17];

cpuid(KAFL_CPUID_IDENTIFIER, regs[0], regs[1], regs[2], regs[3]);

memcpy(str, regs, sizeof(regs));
str[16] = '\0';

//debug_printf("CPUID string: >>%s<<\n", str);

if (0 == strncmp(str, "NYX vCPU (PT)", sizeof(str))) {
return nyx_cpu_v1;
} else if (0 == strncmp(str, "NYX vCPU (NO-PT)", sizeof(str))) {
return nyx_cpu_v2;
} else {
return nyx_cpu_none;
}
}
~~~

# QEMU-Nyx

On the QEMU side, in [`kvm-all.c:kvm_cpu_exec()`](https://github.com/IntelLabs/kafl.qemu/blob/kafl_stable/accel/kvm/kvm-all.c#L2589), the handler for the VMware port forwarding the Nyx hypercalls seems to already be here

~~~c
switch (run->exit_reason) {
case KVM_EXIT_IO:
DPRINTF("handle_io\n");
#ifdef QEMU_NYX
// clang-format on
if (run->io.port == 0x5658 && run->io.size == 4 &&
*((uint32_t *)((uint8_t *)run + run->io.data_offset)) == 0x8080801f)
{
assert(kvm_state->nyx_no_pt_mode);
ret = handle_vmware_hypercall(run, cpu);
break;
}
// clang-format off
#endif
~~~

~~~c
static int handle_vmware_hypercall(struct kvm_run *run, CPUState *cpu)
{
kvm_arch_get_registers_fast(cpu);

X86CPU *x86_cpu = X86_CPU(cpu);
CPUX86State *env = &x86_cpu->env;

return handle_kafl_hypercall(run, cpu, env->regs[R_EBX] + 100, env->regs[R_ECX]);
}
~~~

# Related

- on QEMU, the [`vmport`](https://www.qemu.org/docs/master/system/invocation.html?highlight=vmport) parameter toggles the emulation of the VMware backdoor. ([patch](https://listman.redhat.com/archives/libvir-list/2015-April/msg00000.html))
![image](https://github.com/IntelLabs/kAFL/assets/964610/15c9314d-4723-41b5-96c2-64f5f14b6908)
- on KVM, the vmware backdoor can be toggled with [`enable_vmware_backdoor=y`](https://patchwork.kernel.org/project/kvm/patch/1514134612-25857-2-git-send-email-liran.alon@oracle.com/) on the `kvm` module

@schumilo
- do you happen to have an existing implementation of the glude code necessary in the guest to issue hypercalls with the vmware backdoor ? Since the implementation exists in QEMU, I guest it was already working before ?
- can you detail a bit the existing code in QEMU ? I'm not clear on this line: `*((uint32_t *)((uint8_t *)run + run->io.data_offset)) == 0x8080801f)`. Also `handle_kafl_hypercall(run, cpu, env->regs[R_EBX] + 100, env->regs[R_ECX]);` why +100 ? 🤔

Thanks !

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.