Unicorn Backend

Unicorn is a user-space concrete emulator based on QEMU.

Unlike qemu-user, Unicorn is fully self-contained; it doesn’t depend on the host kernel or library environment. Unicorn is one of the simplest emulators to use, one of the fastest, and is the only emulator that currently supports fuzzing via AFL.

Execution Control

UnicornEmulator supports the standard execution control functions.

Note

Unicorn needs some extra configuration when dealing with platforms that include multiple ISAs. The only currently-supported example is arm32, which supports ARM and THUMB.

The function UnicornEmulator.set_thumb() allows a harness to specify that the current machine state should be interpreted using the THUMB isa. The mode will be tracked internally as code executes, and can be referenced using UnicornEmulator.get_thumb().

Both of these functions will fail if the emulator is configured for a platform other than arm32.

Note

UnicornEmulator.step() may execute more than one instruction.

This only happens on instructions with delay slots; the emulator must execute the instruction and its delay slot as a single step.

Exit Points and Bounds

UnicornEmulator supports exit points and bounds normally. It will treat execution of unmapped memory as a memory error, although it does support unmapped exit points.

Accessing Registers

UnicornEmulator supports normal register access.

Setting labels on registers has no effect on UnicornEmulator, and the labels are not preserved after execution starts.

Caution

Unicorn supports some control registers, such as the x86 segmentation and parts of the MMU control registers.

Configuring these in smallworld is difficult, since they must be configured in a specific order, or else produce an inconsistent state that will cause an exception.

The Machine State interface is not precise enough to perform this configuration, since it doesn’t guarantee the order in which registers are applied.

Mapping Memory

UnicornEmulator enforces its memory map. Accesses to unmapped memory will produce an exception, reporting either an unmapped “read”, “write”, or “fetch” (for an unmapped program counter.)

The memory map works on a page (4096 byte) resolution.

Accessing Memory

UnicornEmulator supports normal memory accesses.

Setting labels on memory has no effect on UnicornEmulator, and labels are not preserved once execution begins.

Warning

TODO: How well-supported are ISA features that change memory layout, like x86 segmentation?

Event Handlers

UnicornEmulator supports the following event types:

  • Instruction Hooks

  • Function Models

  • Memory Accesses

Warning

Unicorn also has an interface for interrupt hooking, but it is non-functional.

These have no special behaviors.

Interacting with Unicorn

Note

Understanding this section is not necessary to write a normal harness.

The features described here are completely abstracted behind the UnicornEmulator interface, and are only useful if you want to leverage Unicorn for analysis.

This section describes how to access the relevant objects, and any caveats regarding their access. Using them for analysis is an exercise left to other tutorials.

It’s possible to access the Unicorn emulator directly via the property UnicornEmulator.engine. This will be fully initialized when the UnicornEmulator object is constructed.