Modern Node.js bindings for the Unicorn Engine CPU emulator using N-API.
Part of the HikariSystem HexCore binary analysis IDE.
- Full Unicorn Engine API support
- Modern N-API bindings (ABI stable)
- Async emulation with Promises
- TypeScript definitions included
- Hook system with ThreadSafeFunction
- Support for all architectures: x86, ARM, ARM64, MIPS, SPARC, PPC, M68K, RISC-V
- Context save/restore
- Memory mapping and protection
- Native Breakpoints (O(1) lookup)
- Shared Memory Support (GC Safe)
- State Snapshotting (Save/Restore)
npm install hexcore-unicornNote: You need to have the Unicorn library installed on your system or place the library files in the deps/unicorn/ directory.
git clone https://github.com/LXrdKnowkill/hexcore-unicorn.git
cd hexcore-unicorn
npm install
npm run buildconst { Unicorn, ARCH, MODE, PROT, X86_REG } = require('hexcore-unicorn');
// Create x86-64 emulator
const uc = new Unicorn(ARCH.X86, MODE.MODE_64);
// Map memory for code and stack
uc.memMap(0x1000n, 0x1000, PROT.ALL); // Code
uc.memMap(0x2000n, 0x1000, PROT.ALL); // Stack
// Write x86-64 code: mov rax, 0x1234; ret
const code = Buffer.from([
0x48, 0xC7, 0xC0, 0x34, 0x12, 0x00, 0x00, // mov rax, 0x1234
0xC3 // ret
]);
uc.memWrite(0x1000n, code);
// Set up stack pointer
uc.regWrite(X86_REG.RSP, 0x2800n);
// Run emulation
uc.emuStart(0x1000n, 0x1008n);
// Read result
const rax = uc.regRead(X86_REG.RAX);
console.log(`RAX = 0x${rax.toString(16)}`); // RAX = 0x1234
// Clean up
uc.close();const uc = new Unicorn(arch, mode);arch: Architecture constant (e.g.,ARCH.X86,ARCH.ARM64)mode: Mode constant (e.g.,MODE.MODE_64,MODE.UC_MODE_ARM)
// Map memory
uc.memMap(address, size, permissions);
// Read memory
const buffer = uc.memRead(address, size);
// Write memory
uc.memWrite(address, buffer);
// Unmap memory
uc.memUnmap(address, size);
// Change permissions
uc.memProtect(address, size, permissions);
// Get mapped regions
const regions = uc.memRegions();
// Returns: [{ begin: bigint, end: bigint, perms: number }, ...]// Read register
const value = uc.regRead(X86_REG.RAX);
// Write register
uc.regWrite(X86_REG.RAX, 0x1234n);
// Batch operations
const values = uc.regReadBatch([X86_REG.RAX, X86_REG.RBX]);
uc.regWriteBatch([X86_REG.RAX, X86_REG.RBX], [0x1111n, 0x2222n]);// Synchronous emulation
uc.emuStart(begin, until, timeout, count);
// Asynchronous emulation
await uc.emuStartAsync(begin, until, timeout, count);
// Stop emulation (from hook)
uc.emuStop();// Code execution hook
const handle = uc.hookAdd(HOOK.CODE, (address, size) => {
console.log(`Executing: 0x${address.toString(16)}`);
});
// Memory access hook
uc.hookAdd(HOOK.MEM_WRITE, (type, address, size, value) => {
console.log(`Memory write at 0x${address.toString(16)}`);
});
// Interrupt hook
uc.hookAdd(HOOK.INTR, (intno) => {
console.log(`Interrupt: ${intno}`);
});
// Remove hook
uc.hookDel(handle);// Save context
const ctx = uc.contextSave();
// Restore context
uc.contextRestore(ctx);
// Free context
ctx.free();// Add a native breakpoint (Zero overhead until hit)
uc.breakpointAdd(0x1000n);
// Remove breakpoint
uc.breakpointDel(0x1000n);// Create shared buffer
const sab = new SharedArrayBuffer(4096);
const buffer = Buffer.from(sab);
// Map it (Unicorn keeps a reference to prevent GC)
uc.memMapPtr(0x10000n, buffer, PROT.ALL);
// usage: write to 'buffer' in JS, read instanly in Unicorn// Save full state (Context + RAM)
const snapshot = uc.stateSave();
// ... execute more code ...
// Restore state (Rewind)
uc.stateRestore(snapshot);// Get version
const ver = version();
console.log(`Unicorn ${ver.string}`);
// Check architecture support
if (archSupported(ARCH.ARM64)) {
console.log('ARM64 is supported');
}
// Get error message
const msg = strerror(errorCode);ARCH.X86- x86/x64ARCH.ARM- ARMARCH.ARM64- ARM64 (AArch64)ARCH.MIPS- MIPSARCH.SPARC- SPARCARCH.PPC- PowerPCARCH.M68K- Motorola 68KARCH.RISCV- RISC-V
MODE.MODE_16- 16-bit modeMODE.MODE_32- 32-bit modeMODE.MODE_64- 64-bit modeMODE.LITTLE_ENDIAN- Little-endianMODE.BIG_ENDIAN- Big-endian
PROT.NONE- No permissionsPROT.READ- Read permissionPROT.WRITE- Write permissionPROT.EXEC- Execute permissionPROT.ALL- All permissions
HOOK.CODE- Code executionHOOK.BLOCK- Basic blockHOOK.INTR- InterruptsHOOK.MEM_READ- Memory readHOOK.MEM_WRITE- Memory writeHOOK.MEM_FETCH- Memory fetch
X86_REG- x86/x64 registers (RAX, RBX, RCX, etc.)ARM_REG- ARM registers (R0-R12, SP, LR, PC, etc.)ARM64_REG- ARM64 registers (X0-X30, SP, PC, etc.)MIPS_REG- MIPS registers (V0, A0-A3, T0-T9, etc.)
Full TypeScript definitions are included:
import { Unicorn, ARCH, MODE, PROT, X86_REG, version } from 'hexcore-unicorn';
const uc = new Unicorn(ARCH.X86, MODE.MODE_64);
const rax: bigint | number = uc.regRead(X86_REG.RAX);- Node.js >= 18.0.0
- Unicorn Engine library
Windows:
Download from Unicorn releases and place unicorn.dll and unicorn.lib in deps/unicorn/.
Linux:
sudo apt install libunicorn-dev
# Or build from sourcemacOS:
brew install unicornMIT License - See LICENSE for details.
Contributions are welcome! Please open an issue or submit a pull request.
- hexcore-capstone - Capstone disassembler bindings
- hexcore-keystone - Keystone assembler bindings (Older version, No Working)
- Unicorn Engine - The underlying emulation framework