Unicorn CPU Emulator Engine
Unicorn is a lightweight, multi-platform, multi-architecture CPU emulator framework based on QEMU. It provides Python bindings for emulating various CPU architectures (ARM, AArch64, M68K, MIPS, PowerPC, RISCV, SPARC, S390x, TriCore, X86), which is widely used in security research, reverse engineering, and dynamic binary analysis. The library is actively maintained, with the current version being 2.1.4, and releases occurring frequently to address bugs, enhance features, and improve stability.
Warnings
- breaking Major API changes were introduced between Unicorn 1.x and 2.x, particularly in the Python bindings which were 'strongly typed and many improvements'. While initial 2.0.0 releases claimed backward compatibility, subsequent patches indicate reverts of 'previous break changes' and removal of 'Unicorn 1 hacks'.
- breaking The behavior of memory hooks changed significantly between Unicorn 1 and Unicorn 2. If a user's `UC_HOOK_MEM_READ`/`UC_HOOK_MEM_WRITE` hook returns `true` (indicating the issue was handled) but does not correctly map memory, Unicorn 2.x will not know how to proceed, whereas Unicorn 1.x had undefined behavior.
- deprecated Previous versions (prior to 2.1.3) of the Unicorn library contained security vulnerabilities. Users are strongly urged to update to the latest version for critical security fixes.
- gotcha The Unicorn 2.1.0 release series experienced stability issues. Version 2.1.2 was released specifically to resolve these problems. Users who were on 2.1.0 or 2.1.1 are advised to upgrade.
- gotcha Unicorn 2.1.2 temporarily ceased providing binary wheels for macOS ARM64 due to GitHub Actions runner limitations. While this issue has been resolved in 2.1.4 and wheels are distributed again, users might encounter build issues on macOS ARM64 for versions 2.1.2 and 2.1.3.
- gotcha As of version 2.1.4, Unicorn guarantees a consistent Program Counter (PC) in all emulation cases. Previous versions might have had inconsistent PC behavior for performance reasons. Code relying on the former (inconsistent) PC state might behave differently.
Install
-
pip install unicorn
Imports
- Uc
from unicorn import *
- UC_ARCH_X86, UC_MODE_32, UC_X86_REG_ECX
from unicorn.x86_const import *
Quickstart
from unicorn import *
from unicorn.x86_const import *
X86_CODE32 = b'\x41\x4a' # INC ecx; DEC edx
ADDRESS = 0x1000000
print('Emulate i386 code')
try:
# Initialize emulator in X86-32bit mode
mu = Uc(UC_ARCH_X86, UC_MODE_32)
# Map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# Write machine code to be emulated to memory
mu.mem_write(ADDRESS, X86_CODE32)
# Initialize machine registers
mu.reg_write(UC_X86_REG_ECX, 0x1234)
mu.reg_write(UC_X86_REG_EDX, 0x7890)
# Emulate code in infinite time & unlimited instructions
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32))
# Print out some registers
print('Emulation done. Below is the CPU context')
r_ecx = mu.reg_read(UC_X86_REG_ECX)
r_edx = mu.reg_read(UC_X86_REG_EDX)
print(f'>>> ECX = 0x{r_ecx:x}')
print(f'>>> EDX = 0x{r_edx:x}')
except UcError as e:
print(f'ERROR: {e}')