Merge pull request #3 from mkravchik/vld

Vld
This commit is contained in:
mkravchik 2016-05-03 22:26:00 +03:00
commit 1afbee3a12
187 changed files with 11672 additions and 2100 deletions

16
.appveyor.yml Normal file
View file

@ -0,0 +1,16 @@
version: 1.0-{build}
platform:
- x64
environment:
global:
MSYS2_ARCH: x86_64
matrix:
- HOST_ARCH_ARG: --host=x86_64-w64-mingw32
ADD_PATH: /mingw64/bin
- HOST_ARCH_ARG: --host=i686-w64-mingw32
ADD_PATH: /mingw32/bin
build_script:
- make.sh cross-win64

22
.gitignore vendored
View file

@ -70,7 +70,13 @@ mem_unmap.static
mem_apis mem_apis
mem_apis.exe mem_apis.exe
mem_apis.static mem_apis.static
sample_batch_reg
sample_batch_reg.exe
sample_batch_reg.static
sample_x86_32_gdt_and_seg_regs
sample_x86_32_gdt_and_seg_regs.exe
sample_x86_32_gdt_and_seg_regs.static
mem_64_c
libunicorn*.dll libunicorn*.dll
libunicorn*.so libunicorn*.so
@ -128,6 +134,12 @@ threaded_emu_start
emu_stop_in_hook_overrun emu_stop_in_hook_overrun
mips_branch_likely_issue mips_branch_likely_issue
emu_clear_errors emu_clear_errors
001-bad_condition_code_0xe
002-qemu__fatal__unimplemented_control_register_write_0xffb___0x0
003-qemu__fatal__wdebug_not_implemented
004-segmentation_fault_1
005-qemu__fatal__illegal_instruction__0000___00000404
006-qemu__fatal__illegal_instruction__0421___00040026
test_mem_map_ptr test_mem_map_ptr
test_mem_high test_mem_high
@ -139,6 +151,14 @@ test_multihook
test_pc_change test_pc_change
mem_fuzz mem_fuzz
test_x86_soft_paging test_x86_soft_paging
test_hookcounts
memleak_x86
memleak_arm
memleak_arm64
memleak_mips
memleak_m68k
memleak_sparc
################# #################

15
.travis.yml Normal file
View file

@ -0,0 +1,15 @@
language: c
sudo: false
before_install:
- export LD_LIBRARY_PATH=`pwd`/samples/:$LD_LIBRARY_PATH
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install glib; fi
script:
- ./make.sh
compiler:
- clang
- gcc
os:
- linux
- osx

View file

@ -1,304 +0,0 @@
This documentation explains how to compile, install & run Unicorn on MacOSX,
Linux, *BSD & Solaris. We also show steps to cross-compile for Microsoft Windows.
*-*-*-*-*-*
[0] Dependencies
Unicorn requires few dependent packages as follows.
- For Mac OS X, "pkg-config" and "glib" are needed.
Brew users can install "pkg-config" and "glib" with:
$ brew install pkg-config glib
- For Linux, "glib2-dev" is needed.
Ubuntu/Debian users can install this with:
$ sudo apt-get install libglib2.0-dev
- For Windows, cross-compile requires Mingw. Mingw-glib2 is needed.
At the moment, it is confirmed that Unicorn can be compiled either on Ubuntu
or Windows.
- On Ubuntu 14.04 64-bit, do:
1. Download DEB packages for Mingw64 from:
https://launchpad.net/~greg-hellings/+archive/ubuntu/mingw-libs/+build/2924251
2. To cross-compile for Windows 32-bit, install Mingw with (ignore all the warnings):
$ sudo dpkg -i --force-depends mingw64-x86-glib2_2.31.0_all.deb
To cross-compile for Windows 64-bit, install Mingw with:
$ sudo dpkg -i --force-depends mingw64-x64-glib2_2.31.0_all.deb
- On Windows, install MinGW via package MSYS2 at https://msys2.github.io/
Follow the install instructions and don't forget to update the system packages with:
$ pacman --needed -Sy bash pacman pacman-mirrors msys2-runtime
Then close MSYS2, run it again from Start menu and update the rest with:
$ pacman -Su
Finally, install required toolchain to build C projects.
- To compile for Windows 32-bit, run:
$ pacman -S python2
$ pacman -S make
$ pacman -S pkg-config
$ pacman -S mingw-w64-i686-glib2
$ pacman -S mingw-w64-i686-toolchain
- To compile for Windows 64-bit, run:
$ pacman -S python2
$ pacman -S make
$ pacman -S pkg-config
$ pacman -S mingw-w64-x86_64-glib2
$ pacman -S mingw-w64-x86_64-toolchain
- For Cygwin, "make", "gcc-core", "pkg-config", "libpcre-devel", "zlib-devel"
and "libglib2.0-devel" are needed.
If apt-cyg is available, you can install these with:
$ apt-cyg install make gcc-core pkg-config libpcre-devel zlib-devel libglib2.0-devel
[1] Tailor Unicorn to your need.
Out of 6 archtitectures supported by Unicorn (Arm, Arm64, M68K, Mips, Sparc,
& X86), if you just need several selected archs, choose which ones you want
to compile in by editing "config.mk" before going to next steps.
By default, all 6 architectures are compiled.
The other way of customize Unicorn without having to edit config.mk is to
pass the desired options on the commandline to ./make.sh. Currently,
Unicorn supports 4 options, as follows.
- UNICORN_ARCHS: specify list of architectures to compiled in.
- UNICORN_STATIC: build static library.
- UNICORN_SHARED: build dynamic (shared) library.
- UNICORN_QEMU_FLAGS: specify extra flags for qemu's configure script
To avoid editing config.mk for these customization, we can pass their values to
make.sh, as follows.
$ UNICORN_ARCHS="arm aarch64 x86" ./make.sh
NOTE: on commandline, put these values in front of ./make.sh, not after it.
For each option, refer to docs/README for more details.
[2] Compile and install from source on *nix
To build Unicorn on *nix (such as MacOSX, Linux, *BSD, Solaris):
- To compile for current platform, run:
$ ./make.sh
- Unicorn requires Python 2.x to compile. If Python 2.x is not the default
Python interpreter, ensure that the appropriate option is set:
$ UNICORN_QEMU_FLAGS="--python=/path/to/python2" ./make.sh
- To cross-compile Unicorn on 64-bit OS to target 32-bit binary, run:
$ ./make.sh nix32
After compiling, install Unicorn with:
$ sudo ./make.sh install
For FreeBSD/OpenBSD, where sudo is unavailable, run:
$ su; ./make.sh install
Users are then required to enter root password to copy Unicorn into machine
system directories.
Afterwards, run ./samples/sample_all.sh to test the sample emulations.
NOTE: The core framework installed by "./make.sh install" consist of
following files:
/usr/include/unicorn/unicorn.h
/usr/include/unicorn/x86.h
/usr/include/unicorn/arm.h
/usr/include/unicorn/arm64.h
/usr/include/unicorn/mips.h
/usr/include/unicorn/ppc.h
/usr/include/unicorn/sparc.h
/usr/include/unicorn/m68k.h
/usr/include/unicorn/platform.h
/usr/lib/libunicorn.so (for Linux/*nix), or /usr/lib/libunicorn.dylib (OSX)
/usr/lib/libunicorn.a
[3] Compile from source on Windows - with MinGW (MSYS2)
To compile with MinGW, install MSYS2 as instructed in the first section.
Then, build Unicorn with the next steps:
- To compile Windows 32-bit binary with MinGW, run:
$ ./make.sh cross-win32
- To compile Windows 64-bit binary with MinGW, run:
$ ./make.sh cross-win64
Resulted files unicorn.dll, unicorn.lib & samples/sample*.exe can then
be used on Windows machine.
To run sample_x86.exe on Windows 32-bit, you need the following files:
- unicorn.dll
- %MSYS2%\mingw32\bin\libiconv-2.dll
- %MSYS2%\mingw32\bin\libintl-8.dll
- %MSYS2%\mingw32\bin\libglib-2.0-0.dll
- %MSYS2%\mingw32\bin\libgcc_s_dw2-1.dll
- %MSYS2%\mingw32\bin\libwinpthread-1.dll
To run sample_x86.exe on Windows 64-bit, you need the following files:
- unicorn.dll
- %MSYS2%\mingw64\bin\libiconv-2.dll
- %MSYS2%\mingw64\bin\libintl-8.dll
- %MSYS2%\mingw64\bin\libglib-2.0-0.dll
- %MSYS2%\mingw64\bin\libgcc_s_seh-1.dll
- %MSYS2%\mingw64\bin\libwinpthread-1.dll
[4] Compile and install from source on Cygwin
To build Unicorn on Cygwin, run:
$ ./make.sh
After compiling, install Unicorn with:
$ ./make.sh install
Resulted files cygunicorn.dll, libunicorn.dll.a and libunicorn.a can be
used on Cygwin but not native Windows.
NOTE: The core framework installed by "./make.sh install" consist of
following files:
/usr/include/unicorn/*.h
/usr/bin/cygunicorn.dll
/usr/lib/libunicorn.dll.a
/usr/lib/libunicorn.a
[5] Cross-compile for Windows from *nix
To cross-compile for Windows, Linux & gcc-mingw-w64-i686 (and also gcc-mingw-w64-x86-64
for 64-bit binaries) are required.
- To cross-compile Windows 32-bit binary, simply run:
$ ./make.sh cross-win32
- To cross-compile Windows 64-bit binary, run:
$ ./make.sh cross-win64
Resulted files unicorn.dll, unicorn.lib & samples/sample*.exe can then
be used on Windows machine.
To run sample_x86.exe on Windows 32-bit, you need the following files:
- unicorn.dll
- /usr/i686-w64-mingw32/sys-root/mingw/bin/libglib-2.0-0.dll
- /usr/lib/gcc/i686-w64-mingw32/4.8/libgcc_s_sjlj-1.dll
- /usr/i686-w64-mingw32/lib/libwinpthread-1.dll
To run sample_x86.exe on Windows 64-bit, you need the following files:
- unicorn.dll
- /usr/x86_64-w64-mingw32/sys-root/mingw/bin/libglib-2.0-0.dll
- /usr/lib/gcc/x86_64-w64-mingw32/4.8/libgcc_s_sjlj-1.dll
- /usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll
Then run either "sample_x86.exe -32" or "sample_x86.exe -64" to test emulators for X86 32-bit or X86 64-bit.
For other architectures, run "sample_xxx.exe" found in the same directory.
[6] Cross-compile for iOS from Mac OSX.
To cross-compile for iOS (iPhone/iPad/iPod), Mac OSX with XCode installed is required.
- To cross-compile for ArmV7 (iPod 4, iPad 1/2/3, iPhone4, iPhone4S), run:
$ ./make.sh ios_armv7
- To cross-compile for ArmV7s (iPad 4, iPhone 5C, iPad mini), run:
$ ./make.sh ios_armv7s
- To cross-compile for Arm64 (iPhone 5S, iPad mini Retina, iPad Air), run:
$ ./make.sh ios_arm64
- To cross-compile for all iDevices (armv7 + armv7s + arm64), run:
$ ./make.sh ios
Resulted files libunicorn.dylib, libunicorn.a & tests/test* can then
be used on iOS devices.
[7] Cross-compile for Android
To cross-compile for Android (smartphone/tablet), Android NDK is required.
NOTE: Only ARM and ARM64 are currently supported.
$ NDK=/android/android-ndk-r10e ./make.sh cross-android arm
or
$ NDK=/android/android-ndk-r10e ./make.sh cross-android arm64
Resulted files libunicorn.so, libunicorn.a & tests/test* can then
be used on Android devices.
[8] By default, "cc" (default C compiler on the system) is used as compiler.
- To use "clang" compiler instead, run the command below:
$ ./make.sh clang
- To use "gcc" compiler instead, run:
$ ./make.sh gcc
[9] To uninstall Unicorn, run the command below:
$ sudo ./make.sh uninstall
[10] Language bindings
Look for the bindings under directory bindings/, and refer to README file
of corresponding languages.
[11] Unit tests
Automated unit tests use the cmocka unit testing framework (https://cmocka.org/).
It can be installed in most Linux distros using the package manager, e.g.
`sudo yum install libcmocka libcmocka-devel`, or you can easily build and install it from source.
You can run the tests by running `make test` in the project directory.

View file

@ -59,3 +59,7 @@ Chris Eagle: Java binding
Ryan Hileman: Go binding Ryan Hileman: Go binding
Antonio Parata: .NET binding Antonio Parata: .NET binding
Jonathon Reinhart: C unit test Jonathon Reinhart: C unit test
Sascha Schirra: Ruby binding
Adrian Herrera: Haskell binding
practicalswift: Various cool bugs found by fuzzing
farmdve: Memory leaking fix

View file

@ -64,6 +64,13 @@ else
CFLAGS += -O3 CFLAGS += -O3
endif endif
ifeq ($(UNICORN_ASAN),yes)
CC = clang -fsanitize=address -fno-omit-frame-pointer
CXX = clang++ -fsanitize=address -fno-omit-frame-pointer
AR = llvm-ar
LDFLAGS := -fsanitize=address ${LDFLAGS}
endif
ifeq ($(CROSS),) ifeq ($(CROSS),)
CC ?= cc CC ?= cc
AR ?= ar AR ?= ar
@ -213,18 +220,17 @@ config:
qemu/config-host.h-timestamp: qemu/config-host.h-timestamp:
ifeq ($(UNICORN_DEBUG),yes) ifeq ($(UNICORN_DEBUG),yes)
cd qemu && \ cd qemu && \
./configure --extra-cflags="$(UNICORN_CFLAGS)" --target-list="$(UNICORN_TARGETS)" ${UNICORN_QEMU_FLAGS} ./configure --cc="${CC}" --extra-cflags="$(UNICORN_CFLAGS)" --target-list="$(UNICORN_TARGETS)" ${UNICORN_QEMU_FLAGS}
printf "$(UNICORN_ARCHS)" > config.log printf "$(UNICORN_ARCHS)" > config.log
else else
cd qemu && \ cd qemu && \
./configure --disable-debug-info --extra-cflags="$(UNICORN_CFLAGS)" --target-list="$(UNICORN_TARGETS)" ${UNICORN_QEMU_FLAGS} ./configure --cc="${CC}" --disable-debug-info --extra-cflags="$(UNICORN_CFLAGS)" --target-list="$(UNICORN_TARGETS)" ${UNICORN_QEMU_FLAGS}
printf "$(UNICORN_ARCHS)" > config.log printf "$(UNICORN_ARCHS)" > config.log
endif endif
compile_lib: config qemu/config-host.h-timestamp compile_lib: config qemu/config-host.h-timestamp
rm -rf lib$(LIBNAME)* $(LIBNAME)*.lib $(LIBNAME)*.dll cyg$(LIBNAME)*.dll && cd qemu && $(MAKE) -j 4 rm -rf lib$(LIBNAME)* $(LIBNAME)*.lib $(LIBNAME)*.dll cyg$(LIBNAME)*.dll && cd qemu && $(MAKE) -j 4
$(MAKE) unicorn $(MAKE) unicorn
cd samples && $(MAKE) clean
unicorn: $(LIBRARY) $(ARCHIVE) unicorn: $(LIBRARY) $(ARCHIVE)
@ -265,8 +271,7 @@ endif
test: all test: all
$(MAKE) -C tests/unit test $(MAKE) -C tests/unit test
install: compile_lib $(PKGCFGF)
install: all $(PKGCFGF)
mkdir -p $(DESTDIR)$(LIBDIR) mkdir -p $(DESTDIR)$(LIBDIR)
ifeq ($(UNICORN_SHARED),yes) ifeq ($(UNICORN_SHARED),yes)
ifeq ($(IS_CYGWIN),1) ifeq ($(IS_CYGWIN),1)

View file

@ -1,6 +1,11 @@
Unicorn Engine Unicorn Engine
============== ==============
[![Join the chat at https://gitter.im/unicorn-engine/chat](https://badges.gitter.im/unicorn-engine/unicorn.svg)](https://gitter.im/unicorn-engine/chat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Build Status](https://travis-ci.org/unicorn-engine/unicorn.svg?branch=master)](https://travis-ci.org/unicorn-engine/unicorn)
[![Build status](https://ci.appveyor.com/api/projects/status/kojr7bald748ba2x/branch/master?svg=true)](https://ci.appveyor.com/project/aquynh/unicorn/branch/master)
Unicorn is a lightweight, multi-platform, multi-architecture CPU emulator framework Unicorn is a lightweight, multi-platform, multi-architecture CPU emulator framework
based on [QEMU](http://qemu.org). based on [QEMU](http://qemu.org).
@ -8,7 +13,7 @@ Unicorn offers some unparalleled features:
- Multi-architecture: ARM, ARM64 (ARMv8), M68K, MIPS, SPARC, and X86 (16, 32, 64-bit) - Multi-architecture: ARM, ARM64 (ARMv8), M68K, MIPS, SPARC, and X86 (16, 32, 64-bit)
- Clean/simple/lightweight/intuitive architecture-neutral API - Clean/simple/lightweight/intuitive architecture-neutral API
- Implemented in pure C language, with bindings for Python, Java, and Go - Implemented in pure C language, with bindings for Rust, Ruby, Python, Java, MSVC, .NET, Go, Delphi/Free Pascal and Haskell.
- Native support for Windows & *nix (with Mac OSX, Linux, *BSD & Solaris confirmed) - Native support for Windows & *nix (with Mac OSX, Linux, *BSD & Solaris confirmed)
- High performance via Just-In-Time compilation - High performance via Just-In-Time compilation
- Support for fine-grained instrumentation at various levels - Support for fine-grained instrumentation at various levels
@ -27,7 +32,7 @@ This project is released under the [GPL license](COPYING).
Compilation & Docs Compilation & Docs
------------------ ------------------
See [COMPILE.TXT](COMPILE.TXT) file for how to compile and install Unicorn. See [docs/COMPILE.md](docs/COMPILE.md) file for how to compile and install Unicorn.
More documentation is available in [docs/README.md](docs/README.md). More documentation is available in [docs/README.md](docs/README.md).

View file

@ -1,31 +1,35 @@
# Unicorn Engine # Unicorn Engine
# By Nguyen Anh Quynh & Dang Hoang Vu, 2015 # By Nguyen Anh Quynh & Dang Hoang Vu, 2015
TMPDIR = /tmp/unicorn_sample TMP_DIR = /tmp/unicorn_sample
DIFF = diff -u -w DIFF = diff -u -w
SAMPLE_ARM = $(TMPDIR)/sample_arm SAMPLE_ARM = $(TMP_DIR)/sample_arm
SAMPLE_ARM64 = $(TMPDIR)/sample_arm64 SAMPLE_ARM64 = $(TMP_DIR)/sample_arm64
SAMPLE_MIPS = $(TMPDIR)/sample_mips SAMPLE_MIPS = $(TMP_DIR)/sample_mips
SAMPLE_M68K = $(TMPDIR)/sample_m68k SAMPLE_M68K = $(TMP_DIR)/sample_m68k
SAMPLE_SPARC = $(TMPDIR)/sample_sparc SAMPLE_SPARC = $(TMP_DIR)/sample_sparc
SAMPLE_X86 = $(TMPDIR)/sample_x86 SAMPLE_X86 = $(TMP_DIR)/sample_x86
.PHONY: all expected python .PHONY: build install samples sample_python expected python sample_diff clean check
all: build:
cd python && $(MAKE) gen_const cd python && $(MAKE) gen_const
cd go && $(MAKE) gen_const cd go && $(MAKE) gen_const
cd java && $(MAKE) gen_const cd java && $(MAKE) gen_const
python const_generator.py dotnet python const_generator.py dotnet
install: build
cd python && $(MAKE) install
cd java && $(MAKE) install
samples: expected python samples: expected python
sample_python: expected python sample_python: expected python
expected: expected:
cd ../samples && $(MAKE) cd ../samples && $(MAKE)
mkdir -p $(TMPDIR) mkdir -p $(TMP_DIR)
../samples/sample_arm > $(SAMPLE_ARM)_e ../samples/sample_arm > $(SAMPLE_ARM)_e
../samples/sample_arm64 > $(SAMPLE_ARM64)_e ../samples/sample_arm64 > $(SAMPLE_ARM64)_e
../samples/sample_mips > $(SAMPLE_MIPS)_e ../samples/sample_mips > $(SAMPLE_MIPS)_e
@ -52,8 +56,9 @@ sample_diff: FORCE
$(DIFF) $(SAMPLE_X86)_e $(SAMPLE_X86)_o $(DIFF) $(SAMPLE_X86)_e $(SAMPLE_X86)_o
clean: clean:
rm -rf $(TMPDIR) rm -rf $(TMP_DIR)
cd python && $(MAKE) clean cd python && $(MAKE) clean
cd java && $(MAKE) clean
check: check:
make -C python check make -C python check

View file

@ -7,8 +7,13 @@ The following bindings are contributed by community.
- Go binding: by Ryan Hileman. - Go binding: by Ryan Hileman.
- .NET binding: by Antonio Parata. - .NET binding: by Antonio Parata.
- MSVC binding: by Zak Escano - MSVC binding: by Zak Escano
- Ruby binding: by Sascha Schirra
- Haskell binding: by Adrian Herrera.
More bindings created & maintained externally by community are available as follows. More bindings created & maintained externally by community are available as follows.
- UnicornPascal: Delphi/Free Pascal binding (by Stievie). - UnicornPascal: Delphi/Free Pascal binding (by Stievie).
https://github.com/stievie/UnicornPascal https://github.com/stievie/UnicornPascal
- Unicorn-Rs: Rust binding (by Sébastien Duquette)
https://github.com/ekse/unicorn-rs

View file

@ -24,6 +24,22 @@ template = {
'comment_open': '#', 'comment_open': '#',
'comment_close': '', 'comment_close': '',
}, },
'ruby': {
'header': "# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [%s_const.rb]\n\nmodule Unicorn\n",
'footer': "end",
'line_format': '\tUC_%s = %s\n',
'out_file': './ruby/unicorn_gem/lib/unicorn/%s_const.rb',
# prefixes for constant filenames of all archs - case sensitive
'arm.h': 'arm',
'arm64.h': 'arm64',
'mips.h': 'mips',
'x86.h': 'x86',
'sparc.h': 'sparc',
'm68k.h': 'm68k',
'unicorn.h': 'unicorn',
'comment_open': '#',
'comment_close': '',
},
'go': { 'go': {
'header': "package unicorn\n// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [%s_const.go]\nconst (\n", 'header': "package unicorn\n// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [%s_const.go]\nconst (\n",
'footer': ")", 'footer': ")",

View file

@ -93,6 +93,7 @@ module Common =
let UC_HOOK_MEM_INVALID = 1008 let UC_HOOK_MEM_INVALID = 1008
let UC_HOOK_MEM_VALID = 7168 let UC_HOOK_MEM_VALID = 7168
let UC_QUERY_MODE = 1 let UC_QUERY_MODE = 1
let UC_QUERY_PAGE_SIZE = 2
let UC_PROT_NONE = 0 let UC_PROT_NONE = 0
let UC_PROT_READ = 1 let UC_PROT_READ = 1

View file

@ -255,7 +255,9 @@ module X86 =
let UC_X86_REG_GDTR = 243 let UC_X86_REG_GDTR = 243
let UC_X86_REG_LDTR = 244 let UC_X86_REG_LDTR = 244
let UC_X86_REG_TR = 245 let UC_X86_REG_TR = 245
let UC_X86_REG_ENDING = 246 let UC_X86_REG_FPCW = 246
let UC_X86_REG_FPTAG = 247
let UC_X86_REG_ENDING = 248
// X86 instructions // X86 instructions

View file

@ -19,10 +19,10 @@ var asm = strings.Join([]string{
func addHooks(mu uc.Unicorn) { func addHooks(mu uc.Unicorn) {
mu.HookAdd(uc.HOOK_BLOCK, func(mu uc.Unicorn, addr uint64, size uint32) { mu.HookAdd(uc.HOOK_BLOCK, func(mu uc.Unicorn, addr uint64, size uint32) {
fmt.Printf("Block: 0x%x, 0x%x\n", addr, size) fmt.Printf("Block: 0x%x, 0x%x\n", addr, size)
}) }, 1, 0)
mu.HookAdd(uc.HOOK_CODE, func(mu uc.Unicorn, addr uint64, size uint32) { mu.HookAdd(uc.HOOK_CODE, func(mu uc.Unicorn, addr uint64, size uint32) {
fmt.Printf("Code: 0x%x, 0x%x\n", addr, size) fmt.Printf("Code: 0x%x, 0x%x\n", addr, size)
}) }, 1, 0)
mu.HookAdd(uc.HOOK_MEM_READ|uc.HOOK_MEM_WRITE, func(mu uc.Unicorn, access int, addr uint64, size int, value int64) { mu.HookAdd(uc.HOOK_MEM_READ|uc.HOOK_MEM_WRITE, func(mu uc.Unicorn, access int, addr uint64, size int, value int64) {
if access == uc.MEM_WRITE { if access == uc.MEM_WRITE {
fmt.Printf("Mem write") fmt.Printf("Mem write")
@ -30,7 +30,7 @@ func addHooks(mu uc.Unicorn) {
fmt.Printf("Mem read") fmt.Printf("Mem read")
} }
fmt.Printf(": @0x%x, 0x%x = 0x%x\n", addr, size, value) fmt.Printf(": @0x%x, 0x%x = 0x%x\n", addr, size, value)
}) }, 1, 0)
invalid := uc.HOOK_MEM_READ_INVALID | uc.HOOK_MEM_WRITE_INVALID | uc.HOOK_MEM_FETCH_INVALID invalid := uc.HOOK_MEM_READ_INVALID | uc.HOOK_MEM_WRITE_INVALID | uc.HOOK_MEM_FETCH_INVALID
mu.HookAdd(invalid, func(mu uc.Unicorn, access int, addr uint64, size int, value int64) bool { mu.HookAdd(invalid, func(mu uc.Unicorn, access int, addr uint64, size int, value int64) bool {
switch access { switch access {
@ -45,11 +45,11 @@ func addHooks(mu uc.Unicorn) {
} }
fmt.Printf(": @0x%x, 0x%x = 0x%x\n", addr, size, value) fmt.Printf(": @0x%x, 0x%x = 0x%x\n", addr, size, value)
return false return false
}) }, 1, 0)
mu.HookAdd(uc.HOOK_INSN, func(mu uc.Unicorn) { mu.HookAdd(uc.HOOK_INSN, func(mu uc.Unicorn) {
rax, _ := mu.RegRead(uc.X86_REG_RAX) rax, _ := mu.RegRead(uc.X86_REG_RAX)
fmt.Printf("Syscall: %d\n", rax) fmt.Printf("Syscall: %d\n", rax)
}, uc.X86_INS_SYSCALL) }, 1, 0, uc.X86_INS_SYSCALL)
} }
func run() error { func run() error {

25
bindings/go/unicorn/uc.c Normal file
View file

@ -0,0 +1,25 @@
#include <stdlib.h>
#include <unicorn/unicorn.h>
#include "_cgo_export.h"
uc_err uc_reg_read_batch_helper(uc_engine *handle, int *regs, uint64_t *val_out, int count) {
void **val_ref = malloc(sizeof(void *) * count);
int i;
for (i = 0; i < count; i++) {
val_ref[i] = (void *)&val_out[i];
}
uc_err ret = uc_reg_read_batch(handle, regs, val_ref, count);
free(val_ref);
return ret;
}
uc_err uc_reg_write_batch_helper(uc_engine *handle, int *regs, uint64_t *val_in, int count) {
void **val_ref = malloc(sizeof(void *) * count);
int i;
for (i = 0; i < count; i++) {
val_ref[i] = (void *)&val_in[i];
}
uc_err ret = uc_reg_write_batch(handle, regs, (void *const *)val_ref, count);
free(val_ref);
return ret;
}

2
bindings/go/unicorn/uc.h Normal file
View file

@ -0,0 +1,2 @@
uc_err uc_reg_read_batch_helper(uc_engine *handle, int *regs, uint64_t *val_out, int count);
uc_err uc_reg_write_batch_helper(uc_engine *handle, int *regs, uint64_t *val_in, int count);

View file

@ -7,8 +7,10 @@ import (
) )
/* /*
#cgo CFLAGS: -O3
#cgo LDFLAGS: -lunicorn #cgo LDFLAGS: -lunicorn
#include <unicorn/unicorn.h> #include <unicorn/unicorn.h>
#include "uc.h"
*/ */
import "C" import "C"
@ -41,7 +43,9 @@ type Unicorn interface {
MemReadInto(dst []byte, addr uint64) error MemReadInto(dst []byte, addr uint64) error
MemWrite(addr uint64, data []byte) error MemWrite(addr uint64, data []byte) error
RegRead(reg int) (uint64, error) RegRead(reg int) (uint64, error)
RegReadBatch(regs []int) ([]uint64, error)
RegWrite(reg int, value uint64) error RegWrite(reg int, value uint64) error
RegWriteBatch(regs []int, vals []uint64) error
RegReadMmr(reg int) (*X86Mmr, error) RegReadMmr(reg int) (*X86Mmr, error)
RegWriteMmr(reg int, value *X86Mmr) error RegWriteMmr(reg int, value *X86Mmr) error
Start(begin, until uint64) error Start(begin, until uint64) error
@ -62,9 +66,14 @@ type UcOptions struct {
Timeout, Count uint64 Timeout, Count uint64
} }
func NewUnicorn(arch, mode int) (Unicorn, error) { func Version() (int, int) {
var major, minor C.uint var major, minor C.uint
C.uc_version(&major, &minor) C.uc_version(&major, &minor)
return int(major), int(minor)
}
func NewUnicorn(arch, mode int) (Unicorn, error) {
major, minor := Version()
if major != C.UC_API_MAJOR || minor != C.UC_API_MINOR { if major != C.UC_API_MAJOR || minor != C.UC_API_MINOR {
return nil, UcError(ERR_VERSION) return nil, UcError(ERR_VERSION)
} }
@ -112,6 +121,38 @@ func (u *uc) RegRead(reg int) (uint64, error) {
return uint64(val), errReturn(ucerr) return uint64(val), errReturn(ucerr)
} }
func (u *uc) RegWriteBatch(regs []int, vals []uint64) error {
if len(regs) == 0 {
return nil
}
if len(vals) < len(regs) {
regs = regs[:len(vals)]
}
cregs := make([]C.int, len(regs))
for i, v := range regs {
cregs[i] = C.int(v)
}
cregs2 := (*C.int)(unsafe.Pointer(&cregs[0]))
cvals := (*C.uint64_t)(unsafe.Pointer(&vals[0]))
ucerr := C.uc_reg_write_batch_helper(u.handle, cregs2, cvals, C.int(len(regs)))
return errReturn(ucerr)
}
func (u *uc) RegReadBatch(regs []int) ([]uint64, error) {
if len(regs) == 0 {
return nil, nil
}
cregs := make([]C.int, len(regs))
for i, v := range regs {
cregs[i] = C.int(v)
}
cregs2 := (*C.int)(unsafe.Pointer(&cregs[0]))
vals := make([]uint64, len(regs))
cvals := (*C.uint64_t)(unsafe.Pointer(&vals[0]))
ucerr := C.uc_reg_read_batch_helper(u.handle, cregs2, cvals, C.int(len(regs)))
return vals, errReturn(ucerr)
}
func (u *uc) MemRegions() ([]*MemRegion, error) { func (u *uc) MemRegions() ([]*MemRegion, error) {
var regions *C.struct_uc_mem_region var regions *C.struct_uc_mem_region
var count C.uint32_t var count C.uint32_t
@ -120,7 +161,7 @@ func (u *uc) MemRegions() ([]*MemRegion, error) {
return nil, errReturn(ucerr) return nil, errReturn(ucerr)
} }
ret := make([]*MemRegion, count) ret := make([]*MemRegion, count)
tmp := (*[1 << 30]C.struct_uc_mem_region)(unsafe.Pointer(regions))[:count] tmp := (*[1 << 24]C.struct_uc_mem_region)(unsafe.Pointer(regions))[:count]
for i, v := range tmp { for i, v := range tmp {
ret[i] = &MemRegion{ ret[i] = &MemRegion{
Begin: uint64(v.begin), Begin: uint64(v.begin),

View file

@ -88,6 +88,7 @@ const (
HOOK_MEM_INVALID = 1008 HOOK_MEM_INVALID = 1008
HOOK_MEM_VALID = 7168 HOOK_MEM_VALID = 7168
QUERY_MODE = 1 QUERY_MODE = 1
QUERY_PAGE_SIZE = 2
PROT_NONE = 0 PROT_NONE = 0
PROT_READ = 1 PROT_READ = 1

View file

@ -250,7 +250,9 @@ const (
X86_REG_GDTR = 243 X86_REG_GDTR = 243
X86_REG_LDTR = 244 X86_REG_LDTR = 244
X86_REG_TR = 245 X86_REG_TR = 245
X86_REG_ENDING = 246 X86_REG_FPCW = 246
X86_REG_FPTAG = 247
X86_REG_ENDING = 248
// X86 instructions // X86 instructions

23
bindings/haskell/.gitignore vendored Normal file
View file

@ -0,0 +1,23 @@
dist
cabal-dev
*.o
*.hi
*.chi
*.chs.h
*.dyn_o
*.dyn_hi
.virtualenv
.hpc
.hsenv
.cabal-sandbox/
cabal.sandbox.config
*.prof
*.aux
*.hp
SampleArm
SampleArm64
SampleM68k
SampleMips
SampleSparc
SampleX86
Shellcode

View file

@ -0,0 +1,31 @@
This documentation explains how to install Haskell binding for Unicorn
from source.
0. Install the core engine as dependency
Follow README in the root directory to compile & install the core.
On *nix, this can simply be done by (project root directory):
$ sudo ./make.sh install
1. Change directories into the Haskell bindings, build and install
$ cd bindings/haskell
$ cabal install
If you are installing into a sandbox, run `cabal sandbox init` before
installing Unicorn's dependencies.
If the build fails, install c2hs manually `cabal install c2hs` (note that this
will probably also require you to run `cabal install alex` and `cabal install
happy` as well). If you are NOT using a sandbox, ensure that `$HOME/.cabal/bin`
is on your PATH.
To build a sample (after having built and installed the Haskell bindings)
$ cd bindings/haskell
$ ghc --make samples/SampleArm.hs

View file

@ -0,0 +1,2 @@
import Distribution.Simple
main = defaultMain

View file

@ -0,0 +1,133 @@
-- Sample code to demonstrate how to emulate ARM code
import Unicorn
import Unicorn.Hook
import qualified Unicorn.CPU.Arm as Arm
import qualified Data.ByteString as BS
import Data.Word
import qualified Numeric as N (showHex)
-- Code to be emulated
--
-- mov r0, #0x37; sub r1, r2, r3
armCode :: BS.ByteString
armCode = BS.pack [0x37, 0x00, 0xa0, 0xe3, 0x03, 0x10, 0x42, 0xe0]
-- sub sp, #0xc
thumbCode :: BS.ByteString
thumbCode = BS.pack [0x83, 0xb0]
-- Memory address where emulation starts
address :: Word64
address = 0x10000
-- Pretty-print integral as hex
showHex :: (Integral a, Show a) => a -> String
showHex =
flip N.showHex ""
-- Calculate code length
codeLength :: Num a => BS.ByteString -> a
codeLength =
fromIntegral . BS.length
hookBlock :: BlockHook ()
hookBlock _ addr size _ =
putStrLn $ ">>> Tracing basic block at 0x" ++ showHex addr ++
", block size = 0x" ++ (maybe "0" showHex size)
hookCode :: CodeHook ()
hookCode _ addr size _ =
putStrLn $ ">>> Tracing instruction at 0x" ++ showHex addr ++
", instruction size = 0x" ++ (maybe "0" showHex size)
testArm :: IO ()
testArm = do
putStrLn "Emulate ARM code"
result <- runEmulator $ do
-- Initialize emulator in ARM mode
uc <- open ArchArm [ModeArm]
-- Map 2MB memory for this emulation
memMap uc address (2 * 1024 * 1024) [ProtAll]
-- Write machine code to be emulated to memory
memWrite uc address armCode
-- Initialize machine registers
regWrite uc Arm.R0 0x1234
regWrite uc Arm.R2 0x6789
regWrite uc Arm.R3 0x3333
-- Tracing all basic blocks with customized callback
blockHookAdd uc hookBlock () 1 0
-- Tracing one instruction at address with customized callback
codeHookAdd uc hookCode () address address
-- Emulate machine code in infinite time (last param = Nothing), or
-- when finishing all the code
let codeLen = codeLength armCode
start uc address (address + codeLen) Nothing Nothing
-- Return the results
r0 <- regRead uc Arm.R0
r1 <- regRead uc Arm.R1
return (r0, r1)
case result of
Right (r0, r1) -> do
-- Now print out some registers
putStrLn ">>> Emulation done. Below is the CPU context"
putStrLn $ ">>> R0 = 0x" ++ showHex r0
putStrLn $ ">>> R1 = 0x" ++ showHex r1
Left err -> putStrLn $ "Failed with error: " ++ show err ++ " (" ++
strerror err ++ ")"
testThumb :: IO ()
testThumb = do
putStrLn "Emulate THUMB code"
result <- runEmulator $ do
-- Initialize emulator in ARM mode
uc <- open ArchArm [ModeThumb]
-- Map 2MB memory for this emulation
memMap uc address (2 * 1024 * 1024) [ProtAll]
-- Write machine code to be emulated to memory
memWrite uc address thumbCode
-- Initialize machine registers
regWrite uc Arm.Sp 0x1234
-- Tracing all basic blocks with customized callback
blockHookAdd uc hookBlock () 1 0
-- Tracing one instruction at address with customized callback
codeHookAdd uc hookCode () address address
-- Emulate machine code in infinite time (last param = Nothing), or
-- when finishing all the code
let codeLen = codeLength thumbCode
start uc address (address + codeLen) Nothing Nothing
-- Return the results
sp <- regRead uc Arm.Sp
return sp
case result of
Right sp -> do
-- Now print out some registers
putStrLn ">>> Emulation done. Below is the CPU context"
putStrLn $ ">>> SP = 0x" ++ showHex sp
Left err -> putStrLn $ "Failed with error: " ++ show err ++ " (" ++
strerror err ++ ")"
main :: IO ()
main = do
testArm
putStrLn "=========================="
testThumb

View file

@ -0,0 +1,85 @@
-- Sample code to demonstrate how to emulate ARM64 code
import Unicorn
import Unicorn.Hook
import qualified Unicorn.CPU.Arm64 as Arm64
import qualified Data.ByteString as BS
import Data.Word
import qualified Numeric as N (showHex)
-- Code to be emulated
--
-- add x11, x13, x15
armCode :: BS.ByteString
armCode = BS.pack [0xab, 0x01, 0x0f, 0x8b]
-- Memory address where emulation starts
address :: Word64
address = 0x10000
-- Pretty-print integral as hex
showHex :: (Integral a, Show a) => a -> String
showHex =
flip N.showHex ""
-- Calculate code length
codeLength :: Num a => BS.ByteString -> a
codeLength =
fromIntegral . BS.length
hookBlock :: BlockHook ()
hookBlock _ addr size _ =
putStrLn $ ">>> Tracing basic block at 0x" ++ showHex addr ++
", block size = 0x" ++ (maybe "0" showHex size)
hookCode :: CodeHook ()
hookCode _ addr size _ =
putStrLn $ ">>> Tracing instruction at 0x" ++ showHex addr ++
", instruction size = 0x" ++ (maybe "0" showHex size)
testArm64 :: IO ()
testArm64 = do
putStrLn "Emulate ARM64 code"
result <- runEmulator $ do
-- Initialize emulator in ARM mode
uc <- open ArchArm64 [ModeArm]
-- Map 2MB memory for this emulation
memMap uc address (2 * 1024 * 1024) [ProtAll]
-- Write machine code to be emulated to memory
memWrite uc address armCode
-- Initialize machine registers
regWrite uc Arm64.X11 0x1234
regWrite uc Arm64.X13 0x6789
regWrite uc Arm64.X15 0x3333
-- Tracing all basic blocks with customized callback
blockHookAdd uc hookBlock () 1 0
-- Tracing one instruction at address with customized callback
codeHookAdd uc hookCode () address address
-- Emulate machine code in infinite time (last param = Nothing), or
-- when finishing all the code
let codeLen = codeLength armCode
start uc address (address + codeLen) Nothing Nothing
-- Return the results
x11 <- regRead uc Arm64.X11
return x11
case result of
Right x11 -> do
-- Now print out some registers
putStrLn $ ">>> Emulation done. Below is the CPU context"
putStrLn $ ">>> X11 = 0x" ++ showHex x11
Left err -> putStrLn $ "Failed with error: " ++ show err ++ " (" ++
strerror err ++ ")"
main :: IO ()
main =
testArm64

View file

@ -0,0 +1,142 @@
-- Sample code to demonstrate how to emulate m68k code
import Unicorn
import Unicorn.Hook
import qualified Unicorn.CPU.M68k as M68k
import qualified Data.ByteString as BS
import Data.Word
import qualified Numeric as N (showHex)
-- Code to be emulated
--
-- movq #-19, %d3
m68kCode :: BS.ByteString
m68kCode = BS.pack [0x76, 0xed]
-- Memory address where emulation starts
address :: Word64
address = 0x10000
-- Pretty-print integral as hex
showHex :: (Integral a, Show a) => a -> String
showHex =
flip N.showHex ""
-- Calculate code length
codeLength :: Num a => BS.ByteString -> a
codeLength =
fromIntegral . BS.length
hookBlock :: BlockHook ()
hookBlock _ addr size _ =
putStrLn $ ">>> Tracing basic block at 0x" ++ showHex addr ++
", block size = 0x" ++ (maybe "0" showHex size)
hookCode :: CodeHook ()
hookCode _ addr size _ =
putStrLn $ ">>> Tracing instruction at 0x" ++ showHex addr ++
", instruction size = 0x" ++ (maybe "0" showHex size)
testM68k :: IO ()
testM68k = do
putStrLn "Emulate M68K code"
result <- runEmulator $ do
-- Initialize emulator in M68K mode
uc <- open ArchM68k [ModeBigEndian]
-- Map 2MB memory for this emulation
memMap uc address (2 * 1024 * 1024) [ProtAll]
-- Write machine code to be emulated to memory
memWrite uc address m68kCode
-- Initialize machine registers
regWrite uc M68k.D0 0x0000
regWrite uc M68k.D1 0x0000
regWrite uc M68k.D2 0x0000
regWrite uc M68k.D3 0x0000
regWrite uc M68k.D4 0x0000
regWrite uc M68k.D5 0x0000
regWrite uc M68k.D6 0x0000
regWrite uc M68k.D7 0x0000
regWrite uc M68k.A0 0x0000
regWrite uc M68k.A1 0x0000
regWrite uc M68k.A2 0x0000
regWrite uc M68k.A3 0x0000
regWrite uc M68k.A4 0x0000
regWrite uc M68k.A5 0x0000
regWrite uc M68k.A6 0x0000
regWrite uc M68k.A7 0x0000
regWrite uc M68k.Pc 0x0000
regWrite uc M68k.Sr 0x0000
-- Tracing all basic blocks with customized callback
blockHookAdd uc hookBlock () 1 0
-- Tracing all instruction
codeHookAdd uc hookCode () 1 0
-- Emulate machine code in infinite time (last param = Nothing), or
-- when finishing all the code
let codeLen = codeLength m68kCode
start uc address (address + codeLen) Nothing Nothing
-- Return the results
d0 <- regRead uc M68k.D0
d1 <- regRead uc M68k.D1
d2 <- regRead uc M68k.D2
d3 <- regRead uc M68k.D3
d4 <- regRead uc M68k.D4
d5 <- regRead uc M68k.D5
d6 <- regRead uc M68k.D6
d7 <- regRead uc M68k.D7
a0 <- regRead uc M68k.A0
a1 <- regRead uc M68k.A1
a2 <- regRead uc M68k.A2
a3 <- regRead uc M68k.A3
a4 <- regRead uc M68k.A4
a5 <- regRead uc M68k.A5
a6 <- regRead uc M68k.A6
a7 <- regRead uc M68k.A7
pc <- regRead uc M68k.Pc
sr <- regRead uc M68k.Sr
return (d0, d1, d2, d3, d4, d5, d6, d7,
a0, a1, a2, a3, a4, a5, a6, a7,
pc, sr)
case result of
Right (d0, d1, d2, d3, d4, d5, d6, d7,
a0, a1, a2, a3, a4, a5, a6, a7,
pc, sr) -> do
-- Now print out some registers
putStrLn ">>> Emulation done. Below is the CPU context"
putStrLn $ ">>> A0 = 0x" ++ showHex a0 ++
"\t\t>>> D0 = 0x" ++ showHex d0
putStrLn $ ">>> A1 = 0x" ++ showHex a1 ++
"\t\t>>> D1 = 0x" ++ showHex d1
putStrLn $ ">>> A2 = 0x" ++ showHex a2 ++
"\t\t>>> D2 = 0x" ++ showHex d2
putStrLn $ ">>> A3 = 0x" ++ showHex a3 ++
"\t\t>>> D3 = 0x" ++ showHex d3
putStrLn $ ">>> A4 = 0x" ++ showHex a4 ++
"\t\t>>> D4 = 0x" ++ showHex d4
putStrLn $ ">>> A5 = 0x" ++ showHex a5 ++
"\t\t>>> D5 = 0x" ++ showHex d5
putStrLn $ ">>> A6 = 0x" ++ showHex a6 ++
"\t\t>>> D6 = 0x" ++ showHex d6
putStrLn $ ">>> A7 = 0x" ++ showHex a7 ++
"\t\t>>> D7 = 0x" ++ showHex d7
putStrLn $ ">>> PC = 0x" ++ showHex pc
putStrLn $ ">>> SR = 0x" ++ showHex sr
Left err -> putStrLn $ "Failed with error: " ++ show err ++ " (" ++
strerror err ++ ")"
main :: IO ()
main =
testM68k

View file

@ -0,0 +1,129 @@
-- Sample code to demonstrate how to emulate Mips code (big endian)
import Unicorn
import Unicorn.Hook
import qualified Unicorn.CPU.Mips as Mips
import qualified Data.ByteString as BS
import Data.Word
import qualified Numeric as N (showHex)
-- Code to be emulated
--
-- ori $at, $at, 0x3456
mipsCodeEb :: BS.ByteString
mipsCodeEb = BS.pack [0x34, 0x21, 0x34, 0x56]
-- ori $at, $at, 0x3456
mipsCodeEl :: BS.ByteString
mipsCodeEl = BS.pack [0x56, 0x34, 0x21, 0x34]
-- Memory address where emulation starts
address :: Word64
address = 0x10000
-- Pretty-print integral as hex
showHex :: (Integral a, Show a) => a -> String
showHex =
flip N.showHex ""
-- Calculate code length
codeLength :: Num a => BS.ByteString -> a
codeLength =
fromIntegral . BS.length
hookBlock :: BlockHook ()
hookBlock _ addr size _ =
putStrLn $ ">>> Tracing basic block at 0x" ++ showHex addr ++
", block size = 0x" ++ (maybe "0" showHex size)
hookCode :: CodeHook ()
hookCode _ addr size _ =
putStrLn $ ">>> Tracing instruction at 0x" ++ showHex addr ++
", instruction size = 0x" ++ (maybe "0" showHex size)
testMipsEb :: IO ()
testMipsEb = do
putStrLn "Emulate MIPS code (big-endian)"
result <- runEmulator $ do
-- Initialize emulator in MIPS mode
uc <- open ArchMips [ModeMips32, ModeBigEndian]
-- Map 2MB memory for this emulation
memMap uc address (2 * 1024 * 1024) [ProtAll]
-- Write machine code to be emulated to memory
memWrite uc address mipsCodeEb
-- Initialise machine registers
regWrite uc Mips.Reg1 0x6789
-- Tracing all basic blocks with customized callback
blockHookAdd uc hookBlock () 1 0
-- Tracing one instruction at address with customized callback
codeHookAdd uc hookCode () address address
-- Emulate machine code in infinite time (last param = Nothing), or
-- when finishing all the code
let codeLen = codeLength mipsCodeEb
start uc address (address + codeLen) Nothing Nothing
-- Return the results
r1 <- regRead uc Mips.Reg1
return r1
case result of
Right r1 -> do
-- Now print out some registers
putStrLn ">>> Emulation done. Below is the CPU context"
putStrLn $ ">>> R1 = 0x" ++ showHex r1
Left err -> putStrLn $ "Failed with error: " ++ show err ++ " (" ++
strerror err ++ ")"
testMipsEl :: IO ()
testMipsEl = do
putStrLn "==========================="
putStrLn "Emulate MIPS code (little-endian)"
result <- runEmulator $ do
-- Initialize emulator in MIPS mode
uc <- open ArchMips [ModeMips32, ModeLittleEndian]
-- Map 2MB memory for this emulation
memMap uc address (2 * 1024 * 1024) [ProtAll]
-- Write machine code to be emulated to memory
memWrite uc address mipsCodeEl
-- Initialize machine registers
regWrite uc Mips.Reg1 0x6789
-- Tracing all basic blocks with customized callback
blockHookAdd uc hookBlock () 1 0
-- Tracing one instruction at address with customized callback
codeHookAdd uc hookCode () address address
-- Emulate machine code in infinite time (last param = Nothing), or
-- when finishing all the code
let codeLen = codeLength mipsCodeEl
start uc address (address + codeLen) Nothing Nothing
-- Return the results
r1 <- regRead uc Mips.Reg1
return r1
case result of
Right r1 -> do
-- Now print out some registers
putStrLn ">>> Emulation done. Below is the CPU context"
putStrLn $ ">>> R1 = 0x" ++ showHex r1
Left err -> putStrLn $ "Failed with error: " ++ show err ++ " (" ++
strerror err ++ ")"
main :: IO ()
main = do
testMipsEb
testMipsEl

View file

@ -0,0 +1,85 @@
-- Sample code to demonstrate how to emulate Sparc code
import Unicorn
import Unicorn.Hook
import qualified Unicorn.CPU.Sparc as Sparc
import qualified Data.ByteString as BS
import Data.Word
import qualified Numeric as N (showHex)
-- Code to be emulated
--
-- add %g1, %g2, %g3
sparcCode :: BS.ByteString
sparcCode = BS.pack [0x86, 0x00, 0x40, 0x02]
-- Memory address where emulation starts
address :: Word64
address = 0x10000
-- Pretty-print integral as hex
showHex :: (Integral a, Show a) => a -> String
showHex =
flip N.showHex ""
-- Calculate code length
codeLength :: Num a => BS.ByteString -> a
codeLength =
fromIntegral . BS.length
hookBlock :: BlockHook ()
hookBlock _ addr size _ =
putStrLn $ ">>> Tracing basic block at 0x" ++ showHex addr ++
", block size = 0x" ++ (maybe "0" showHex size)
hookCode :: CodeHook ()
hookCode _ addr size _ =
putStrLn $ ">>> Tracing instruction at 0x" ++ showHex addr ++
", instruction size = 0x" ++ (maybe "0" showHex size)
testSparc :: IO ()
testSparc = do
putStrLn "Emulate SPARC code"
result <- runEmulator $ do
-- Initialize emulator in Sparc mode
uc <- open ArchSparc [ModeSparc32, ModeBigEndian]
-- Map 2MB memory for this emulation
memMap uc address (2 * 1024 * 1024) [ProtAll]
-- Write machine code to be emulated to memory
memWrite uc address sparcCode
-- Initialize machine registers
regWrite uc Sparc.G1 0x1230
regWrite uc Sparc.G2 0x6789
regWrite uc Sparc.G3 0x5555
-- Tracing all basic blocks with customized callback
blockHookAdd uc hookBlock () 1 0
-- Tracing all instructions with customized callback
codeHookAdd uc hookCode () 1 0
-- Emulate machine code in infinite time (last param = Nothing), or
-- when finishing all the code
let codeLen = codeLength sparcCode
start uc address (address + codeLen) Nothing Nothing
-- Return results
g3 <- regRead uc Sparc.G3
return g3
case result of
Right g3 -> do
-- Now print out some registers
putStrLn ">>> Emulation done. Below is the CPU context"
putStrLn $ ">>> G3 = 0x" ++ showHex g3
Left err -> putStrLn $ "Failed with error: " ++ show err ++ " (" ++
strerror err ++ ")"
main :: IO ()
main =
testSparc

View file

@ -0,0 +1,675 @@
-- Sample code to demonstrate how to emulate X86 code
import Unicorn
import Unicorn.Hook
import qualified Unicorn.CPU.X86 as X86
import Control.Monad.Trans.Class (lift)
import qualified Data.ByteString as BS
import Data.Word
import qualified Numeric as N (showHex)
import System.Environment
-- Code to be emulated
--
-- inc ecx; dec edx
x86Code32 :: BS.ByteString
x86Code32 = BS.pack [0x41, 0x4a]
-- jmp 4; nop; nop; nop; nop; nop; nop
x86Code32Jump :: BS.ByteString
x86Code32Jump = BS.pack [0xeb, 0x02, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90]
-- inc ecx; dec edx; jmp self-loop
x86Code32Loop :: BS.ByteString
x86Code32Loop = BS.pack [0x41, 0x4a, 0xeb, 0xfe]
-- mov [0xaaaaaaaa], ecx; inc ecx; dec edx
x86Code32MemWrite :: BS.ByteString
x86Code32MemWrite = BS.pack [0x89, 0x0d, 0xaa, 0xaa, 0xaa, 0xaa, 0x41, 0x4a]
-- mov ecx, [0xaaaaaaaa]; inc ecx; dec edx
x86Code32MemRead :: BS.ByteString
x86Code32MemRead = BS.pack [0x8b, 0x0d, 0xaa, 0xaa, 0xaa, 0xaa, 0x41, 0x4a]
-- jmp ouside; inc ecx; dec edx
x86Code32JmpInvalid :: BS.ByteString
x86Code32JmpInvalid = BS.pack [0xe9, 0xe9, 0xee, 0xee, 0xee, 0x41, 0x4a]
-- inc ecx; in al, 0x3f; dec edx; out 0x46, al; inc ebx
x86Code32InOut :: BS.ByteString
x86Code32InOut = BS.pack [0x41, 0xe4, 0x3f, 0x4a, 0xe6, 0x46, 0x43]
x86Code64 :: BS.ByteString
x86Code64 = BS.pack [0x41, 0xbc, 0x3b, 0xb0, 0x28, 0x2a, 0x49, 0x0f, 0xc9,
0x90, 0x4d, 0x0f, 0xad, 0xcf, 0x49, 0x87, 0xfd, 0x90,
0x48, 0x81, 0xd2, 0x8a, 0xce, 0x77, 0x35, 0x48, 0xf7,
0xd9, 0x4d, 0x29, 0xf4, 0x49, 0x81, 0xc9, 0xf6, 0x8a,
0xc6, 0x53, 0x4d, 0x87, 0xed, 0x48, 0x0f, 0xad, 0xd2,
0x49, 0xf7, 0xd4, 0x48, 0xf7, 0xe1, 0x4d, 0x19, 0xc5,
0x4d, 0x89, 0xc5, 0x48, 0xf7, 0xd6, 0x41, 0xb8, 0x4f,
0x8d, 0x6b, 0x59, 0x4d, 0x87, 0xd0, 0x68, 0x6a, 0x1e,
0x09, 0x3c, 0x59]
-- add byte ptr [bx + si], al
x86Code16 :: BS.ByteString
x86Code16 = BS.pack [0x00, 0x00]
-- SYSCALL
x86Code64Syscall :: BS.ByteString
x86Code64Syscall = BS.pack [0x0f, 0x05]
-- Memory address where emulation starts
address :: Word64
address = 0x1000000
-- Pretty-print integral as hex
showHex :: (Integral a, Show a) => a -> String
showHex i =
N.showHex (fromIntegral i :: Word64) ""
-- Pretty-print byte string as hex
showHexBS :: BS.ByteString -> String
showHexBS =
concatMap (flip N.showHex "") . reverse . BS.unpack
-- Write a string (with a newline character) to standard output in the emulator
emuPutStrLn :: String -> Emulator ()
emuPutStrLn =
lift . putStrLn
-- Calculate code length
codeLength :: Num a => BS.ByteString -> a
codeLength =
fromIntegral . BS.length
-- Callback for tracing basic blocks
hookBlock :: BlockHook ()
hookBlock _ addr size _ =
putStrLn $ ">>> Tracing basic block at 0x" ++ showHex addr ++
", block size = 0x" ++ (maybe "0" showHex size)
-- Callback for tracing instruction
hookCode :: CodeHook ()
hookCode uc addr size _ = do
runEmulator $ do
emuPutStrLn $ ">>> Tracing instruction at 0x" ++ showHex addr ++
", instruction size = 0x" ++ (maybe "0" showHex size)
eflags <- regRead uc X86.Eflags
emuPutStrLn $ ">>> --- EFLAGS is 0x" ++ showHex eflags
return ()
-- Callback for tracing instruction
hookCode64 :: CodeHook ()
hookCode64 uc addr size _ = do
runEmulator $ do
rip <- regRead uc X86.Rip
emuPutStrLn $ ">>> Tracing instruction at 0x" ++ showHex addr ++
", instruction size = 0x" ++ (maybe "0" showHex size)
emuPutStrLn $ ">>> RIP is 0x" ++ showHex rip
return ()
-- Callback for tracing memory access (READ or WRITE)
hookMemInvalid :: MemoryEventHook ()
hookMemInvalid uc MemWriteUnmapped addr size (Just value) _ = do
runEmulator $ do
emuPutStrLn $ ">>> Missing memory is being WRITE at 0x" ++
showHex addr ++ ", data size = " ++ show size ++
", data value = 0x" ++ showHex value
memMap uc 0xaaaa0000 (2 * 1024 * 1024) [ProtAll]
return True
hookMemInvalid _ _ _ _ _ _ =
return False
hookMem64 :: MemoryHook ()
hookMem64 _ MemRead addr size _ _ =
putStrLn $ ">>> Memory is being READ at 0x" ++ showHex addr ++
", data size = " ++ show size
hookMem64 _ MemWrite addr size (Just value) _ =
putStrLn $ ">>> Memory is being WRITE at 0x" ++ showHex addr ++
", data size = " ++ show size ++ ", data value = 0x" ++
showHex value
-- Callback for IN instruction (X86)
-- This returns the data read from the port
hookIn :: InHook ()
hookIn uc port size _ = do
result <- runEmulator $ do
eip <- regRead uc X86.Eip
emuPutStrLn $ "--- reading from port 0x" ++ showHex port ++
", size: " ++ show size ++ ", address: 0x" ++ showHex eip
case size of
-- Read 1 byte to AL
1 -> return 0xf1
-- Read 2 byte to AX
2 -> return 0xf2
-- Read 4 byte to EAX
4 -> return 0xf4
-- Should never reach this
_ -> return 0
case result of
Right r -> return r
Left _ -> return 0
-- Callback for OUT instruction (X86)
hookOut :: OutHook ()
hookOut uc port size value _ = do
runEmulator $ do
eip <- regRead uc X86.Eip
emuPutStrLn $ "--- writing to port 0x" ++ showHex port ++ ", size: " ++
show size ++ ", value: 0x" ++ showHex value ++
", address: 0x" ++ showHex eip
-- Confirm that value is indeed the value of AL/AX/EAX
case size of
1 -> do
tmp <- regRead uc X86.Al
emuPutStrLn $ "--- register value = 0x" ++ showHex tmp
2 -> do
tmp <- regRead uc X86.Ax
emuPutStrLn $ "--- register value = 0x" ++ showHex tmp
4 -> do
tmp <- regRead uc X86.Eax
emuPutStrLn $ "--- register value = 0x" ++ showHex tmp
-- Should never reach this
_ -> return ()
return ()
-- Callback for SYSCALL instruction (X86)
hookSyscall :: SyscallHook ()
hookSyscall uc _ = do
runEmulator $ do
rax <- regRead uc X86.Rax
if rax == 0x100 then
regWrite uc X86.Rax 0x200
else
emuPutStrLn $ "ERROR: was not expecting rax=0x" ++ showHex rax ++
" in syscall"
return ()
testI386 :: IO ()
testI386 = do
putStrLn "Emulate i386 code"
result <- runEmulator $ do
-- Initialize emulator in X86-32bit mode
uc <- open ArchX86 [Mode32]
-- Map 2MB memory for this emulation
memMap uc address (2 * 1024 * 1024) [ProtAll]
-- Write machine code to be emulated to memory
memWrite uc address x86Code32
-- Initialize machine registers
regWrite uc X86.Ecx 0x1234
regWrite uc X86.Edx 0x7890
-- Tracing all basic blocks with customized callback
blockHookAdd uc hookBlock () 1 0
-- Tracing all instruction by having @begin > @end
codeHookAdd uc hookCode () 1 0
-- Emulate machine code in infinite time
let codeLen = codeLength x86Code32
start uc address (address + codeLen) Nothing Nothing
-- Now print out some registers
emuPutStrLn ">>> Emulation done. Below is the CPU context"
ecx <- regRead uc X86.Ecx
edx <- regRead uc X86.Edx
emuPutStrLn $ ">>> ECX = 0x" ++ showHex ecx
emuPutStrLn $ ">>> EDX = 0x" ++ showHex edx
-- Read from memory
tmp <- memRead uc address 4
emuPutStrLn $ ">>> Read 4 bytes from [0x" ++ showHex address ++
"] = 0x" ++ showHexBS tmp
case result of
Right _ -> return ()
Left err -> putStrLn $ "Failed with error " ++ show err ++ ": " ++
strerror err
testI386Jump :: IO ()
testI386Jump = do
putStrLn "==================================="
putStrLn "Emulate i386 code with jump"
result <- runEmulator $ do
-- Initialize emulator in X86-32bit mode
uc <- open ArchX86 [Mode32]
-- Map 2MB memory for this emulation
memMap uc address (2 * 1024 * 1024) [ProtAll]
-- Write machine code to be emulated to memory
memWrite uc address x86Code32Jump
-- Tracing 1 basic block with customized callback
blockHookAdd uc hookBlock () address address
-- Tracing 1 instruction at address
codeHookAdd uc hookCode () address address
-- Emulate machine code ininfinite time
let codeLen = codeLength x86Code32Jump
start uc address (address + codeLen) Nothing Nothing
emuPutStrLn ">>> Emulation done. Below is the CPU context"
case result of
Right _ -> return ()
Left err -> putStrLn $ "Failed with error " ++ show err ++ ": " ++
strerror err
-- Emulate code that loop forever
testI386Loop :: IO ()
testI386Loop = do
putStrLn "==================================="
putStrLn "Emulate i386 code that loop forever"
result <- runEmulator $ do
-- Initialize emulator in X86-32bit mode
uc <- open ArchX86 [Mode32]
-- Map 2MB memory for this emulation
memMap uc address (2 * 1024 * 1024) [ProtAll]
-- Write machine code to be emulated in memory
memWrite uc address x86Code32Loop
-- Initialize machine registers
regWrite uc X86.Ecx 0x1234
regWrite uc X86.Edx 0x7890
-- Emulate machine code in 2 seconds, so we can quit even if the code
-- loops
let codeLen = codeLength x86Code32Loop
start uc address (address + codeLen) (Just $ 2 * 1000000) Nothing
-- Now print out some registers
emuPutStrLn ">>> Emulation done. Below is the CPU context"
ecx <- regRead uc X86.Ecx
edx <- regRead uc X86.Edx
emuPutStrLn $ ">>> ECX = 0x" ++ showHex ecx
emuPutStrLn $ ">>> EDX = 0x" ++ showHex edx
case result of
Right _ -> return ()
Left err -> putStrLn $ "Failed with error " ++ show err ++ ": " ++
strerror err
-- Emulate code that read invalid memory
testI386InvalidMemRead :: IO ()
testI386InvalidMemRead = do
putStrLn "==================================="
putStrLn "Emulate i386 code that read from invalid memory"
result <- runEmulator $ do
-- Initialize emulator in X86-32bit mode
uc <- open ArchX86 [Mode32]
-- Map 2MB memory for this emulation
memMap uc address (2 * 1024 * 1024) [ProtAll]
-- Write machine code to be emulated to memory
memWrite uc address x86Code32MemRead
-- Initialize machine registers
regWrite uc X86.Ecx 0x1234
regWrite uc X86.Edx 0x7890
-- Tracing all basic block with customized callback
blockHookAdd uc hookBlock () 1 0
-- Tracing all instructions by having @beegin > @end
codeHookAdd uc hookCode () 1 0
-- Emulate machine code in infinite time
let codeLen = codeLength x86Code32MemRead
start uc address (address + codeLen) Nothing Nothing
-- Now print out some registers
emuPutStrLn ">>> Emulation done. Below is the CPU context"
ecx <- regRead uc X86.Ecx
edx <- regRead uc X86.Edx
emuPutStrLn $ ">>> ECX = 0x" ++ showHex ecx
emuPutStrLn $ ">>> EDX = 0x" ++ showHex edx
case result of
Right _ -> return ()
Left err -> putStrLn $ "Failed with error " ++ show err ++ ": " ++
strerror err
-- Emulate code that write invalid memory
testI386InvalidMemWrite :: IO ()
testI386InvalidMemWrite = do
putStrLn "==================================="
putStrLn "Emulate i386 code that write to invalid memory"
result <- runEmulator $ do
-- Initialize emulator in X86-32bit mode
uc <- open ArchX86 [Mode32]
-- Map 2MB memory for this emulation
memMap uc address (2 * 1024 * 1024) [ProtAll]
-- Write machine code to be emulated to memory
memWrite uc address x86Code32MemWrite
-- Initialize machine registers
regWrite uc X86.Ecx 0x1234
regWrite uc X86.Edx 0x7890
-- Tracing all basic blocks with customized callback
blockHookAdd uc hookBlock () 1 0
-- Tracing all instruction by having @begin > @end
codeHookAdd uc hookCode () 1 0
-- Intercept invalid memory events
memoryEventHookAdd uc HookMemReadUnmapped hookMemInvalid () 1 0
memoryEventHookAdd uc HookMemWriteUnmapped hookMemInvalid () 1 0
-- Emulate machine code in infinite time
let codeLen = codeLength x86Code32MemWrite
start uc address (address + codeLen) Nothing Nothing
-- Now print out some registers
emuPutStrLn ">>> Emulation done. Below is the CPU context"
ecx <- regRead uc X86.Ecx
edx <- regRead uc X86.Edx
emuPutStrLn $ ">>> ECX = 0x" ++ showHex ecx
emuPutStrLn $ ">>> EDX = 0x" ++ showHex edx
-- Read from memory
tmp <- memRead uc 0xaaaaaaaa 4
emuPutStrLn $ ">>> Read 4 bytes from [0x" ++ showHex 0xaaaaaaaa ++
"] = 0x" ++ showHexBS tmp
tmp <- memRead uc 0xffffffaa 4
emuPutStrLn $ ">>> Read 4 bytes from [0x" ++ showHex 0xffffffaa ++
"] = 0x" ++ showHexBS tmp
case result of
Right _ -> return ()
Left err -> putStrLn $ "Failed with error " ++ show err ++ ": " ++
strerror err
-- Emulate code that jump to invalid memory
testI386JumpInvalid :: IO ()
testI386JumpInvalid = do
putStrLn "==================================="
putStrLn "Emulate i386 code that jumps to invalid memory"
result <- runEmulator $ do
-- Initialize emulator in X86-32bit mode
uc <- open ArchX86 [Mode32]
-- Map 2MB memory for this emulation
memMap uc address (2 * 1024 * 1024) [ProtAll]
-- Write machine code to be emulated to memory
memWrite uc address x86Code32JmpInvalid
-- Initialize machine registers
regWrite uc X86.Ecx 0x1234
regWrite uc X86.Edx 0x7890
-- Tracing all basic blocks with customized callback
blockHookAdd uc hookBlock () 1 0
-- Tracing all instructions by having @begin > @end
codeHookAdd uc hookCode () 1 0
-- Emulate machine code in infinite time
let codeLen = codeLength x86Code32JmpInvalid
start uc address (address + codeLen) Nothing Nothing
-- Now print out some registers
emuPutStrLn ">>> Emulation done. Below is the CPU context"
ecx <- regRead uc X86.Ecx
edx <- regRead uc X86.Edx
emuPutStrLn $ ">>> ECX = 0x" ++ showHex ecx
emuPutStrLn $ ">>> EDX = 0x" ++ showHex edx
case result of
Right _ -> return ()
Left err -> putStrLn $ "Failed with error " ++ show err ++ ": " ++
strerror err
testI386InOut :: IO ()
testI386InOut = do
putStrLn "==================================="
putStrLn "Emulate i386 code with IN/OUT instructions"
result <- runEmulator $ do
-- Initialize emulator in X86-32bit mode
uc <- open ArchX86 [Mode32]
-- Map 2MB memory for this emulation
memMap uc address (2 * 1024 * 1024) [ProtAll]
-- Write machine code to be emulated to memory
memWrite uc address x86Code32InOut
-- Initialize machine registers
regWrite uc X86.Eax 0x1234
regWrite uc X86.Ecx 0x6789
-- Tracing all basic blocks with customized callback
blockHookAdd uc hookBlock () 1 0
-- Tracing all instructions
codeHookAdd uc hookCode () 1 0
-- uc IN instruction
inHookAdd uc hookIn () 1 0
-- uc OUT instruction
outHookAdd uc hookOut () 1 0
-- Emulate machine code in infinite time
let codeLen = codeLength x86Code32InOut
start uc address (address + codeLen) Nothing Nothing
-- Now print out some registers
emuPutStrLn ">>> Emulation done. Below is the CPU context"
eax <- regRead uc X86.Eax
ecx <- regRead uc X86.Ecx
emuPutStrLn $ ">>> EAX = 0x" ++ showHex eax
emuPutStrLn $ ">>> ECX = 0x" ++ showHex ecx
case result of
Right _ -> return ()
Left err -> putStrLn $ "Failed with error " ++ show err ++ ": " ++
strerror err
testX8664 :: IO ()
testX8664 = do
putStrLn "Emulate x86_64 code"
result <- runEmulator $ do
-- Initialize emualator in X86-64bit mode
uc <- open ArchX86 [Mode64]
-- Map 2MB memory for this emulation
memMap uc address (2 * 1024 * 1024) [ProtAll]
-- Write machine code to be emulated to memory
memWrite uc address x86Code64
-- Initialize machine registers
regWrite uc X86.Rsp (fromIntegral address + 0x200000)
regWrite uc X86.Rax 0x71f3029efd49d41d
regWrite uc X86.Rbx 0xd87b45277f133ddb
regWrite uc X86.Rcx 0xab40d1ffd8afc461
regWrite uc X86.Rdx 0x919317b4a733f01
regWrite uc X86.Rsi 0x4c24e753a17ea358
regWrite uc X86.Rdi 0xe509a57d2571ce96
regWrite uc X86.R8 0xea5b108cc2b9ab1f
regWrite uc X86.R9 0x19ec097c8eb618c1
regWrite uc X86.R10 0xec45774f00c5f682
regWrite uc X86.R11 0xe17e9dbec8c074aa
regWrite uc X86.R12 0x80f86a8dc0f6d457
regWrite uc X86.R13 0x48288ca5671c5492
regWrite uc X86.R14 0x595f72f6e4017f6e
regWrite uc X86.R15 0x1efd97aea331cccc
-- Tracing all basic blocks with customized callback
blockHookAdd uc hookBlock () 1 0
-- Tracing all instructions in the range [address, address+20]
codeHookAdd uc hookCode64 () address (address + 20)
-- Tracing all memory WRITE access (with @begin > @end)
memoryHookAdd uc HookMemWrite hookMem64 () 1 0
-- Tracing all memory READ access (with @begin > @end)
memoryHookAdd uc HookMemRead hookMem64 () 1 0
-- Emulate machine code in infinite time (last param = Nothing), or
-- when finishing all the code
let codeLen = codeLength x86Code64
start uc address (address + codeLen) Nothing Nothing
-- Now print out some registers
emuPutStrLn ">>> Emulation done. Below is the CPU context"
rax <- regRead uc X86.Rax
rbx <- regRead uc X86.Rbx
rcx <- regRead uc X86.Rcx
rdx <- regRead uc X86.Rdx
rsi <- regRead uc X86.Rsi
rdi <- regRead uc X86.Rdi
r8 <- regRead uc X86.R8
r9 <- regRead uc X86.R9
r10 <- regRead uc X86.R10
r11 <- regRead uc X86.R11
r12 <- regRead uc X86.R12
r13 <- regRead uc X86.R13
r14 <- regRead uc X86.R14
r15 <- regRead uc X86.R15
emuPutStrLn $ ">>> RAX = 0x" ++ showHex rax
emuPutStrLn $ ">>> RBX = 0x" ++ showHex rbx
emuPutStrLn $ ">>> RCX = 0x" ++ showHex rcx
emuPutStrLn $ ">>> RDX = 0x" ++ showHex rdx
emuPutStrLn $ ">>> RSI = 0x" ++ showHex rsi
emuPutStrLn $ ">>> RDI = 0x" ++ showHex rdi
emuPutStrLn $ ">>> R8 = 0x" ++ showHex r8
emuPutStrLn $ ">>> R9 = 0x" ++ showHex r9
emuPutStrLn $ ">>> R10 = 0x" ++ showHex r10
emuPutStrLn $ ">>> R11 = 0x" ++ showHex r11
emuPutStrLn $ ">>> R12 = 0x" ++ showHex r12
emuPutStrLn $ ">>> R13 = 0x" ++ showHex r13
emuPutStrLn $ ">>> R14 = 0x" ++ showHex r14
emuPutStrLn $ ">>> R15 = 0x" ++ showHex r15
case result of
Right _ -> return ()
Left err -> putStrLn $ "Failed with error " ++ show err ++ ": " ++
strerror err
testX8664Syscall :: IO ()
testX8664Syscall = do
putStrLn "==================================="
putStrLn "Emulate x86_64 code with 'syscall' instruction"
result <- runEmulator $ do
-- Initialize emulator in X86-64bit mode
uc <- open ArchX86 [Mode64]
-- Map 2MB memory for this emulation
memMap uc address (2 * 1024 * 1024) [ProtAll]
-- Write machine code to be emulated to memory
memWrite uc address x86Code64Syscall
-- Hook interrupts for syscall
syscallHookAdd uc hookSyscall () 1 0
-- Initialize machine registers
regWrite uc X86.Rax 0x100
-- Emulate machine code in infinite time (last param = Nothing), or
-- when finishing all code
let codeLen = codeLength x86Code64Syscall
start uc address (address + codeLen) Nothing Nothing
-- Now print out some registers
emuPutStrLn ">>> Emulation done. Below is the CPU context"
rax <- regRead uc X86.Rax
emuPutStrLn $ ">>> RAX = 0x" ++ showHex rax
case result of
Right _ -> return ()
Left err -> putStrLn $ "Failed with error " ++ show err ++ ": " ++
strerror err
testX8616 :: IO ()
testX8616 = do
putStrLn "Emulate x86 16-bit code"
result <- runEmulator $ do
-- Initialize emulator in X86-16bit mode
uc <- open ArchX86 [Mode16]
-- Map 8KB memory for this emulation
memMap uc 0 (8 * 1024) [ProtAll]
-- Write machine code to be emulated in memory
memWrite uc 0 x86Code16
-- Initialize machine registers
regWrite uc X86.Eax 7
regWrite uc X86.Ebx 5
regWrite uc X86.Esi 6
-- Emulate machine code in infinite time (last param = Nothing), or
-- when finishing all the code
let codeLen = codeLength x86Code16
start uc 0 codeLen Nothing Nothing
-- Now print out some registers
emuPutStrLn ">>> Emulation done. Below is the CPU context"
-- Read from memory
tmp <- memRead uc 11 1
emuPutStrLn $ ">>> Read 1 bytes from [0x" ++ showHex 11 ++
"] = 0x" ++ showHexBS tmp
case result of
Right _ -> return ()
Left err -> putStrLn $ "Failed with error " ++ show err ++ ": " ++
strerror err
main :: IO ()
main = do
progName <- getProgName
args <- getArgs
case args of
["-32"] -> do
testI386
testI386InOut
testI386Jump
testI386Loop
testI386InvalidMemRead
testI386InvalidMemWrite
testI386JumpInvalid
["-64"] -> do
testX8664
testX8664Syscall
["-16"] -> testX8616
-- Test memleak
["-0"] -> testI386
_ -> putStrLn $ "Syntax: " ++ progName ++ " <-16|-32|-64>"

View file

@ -0,0 +1,153 @@
-- Sample code to trace code with Linux code with syscall
import Unicorn
import Unicorn.Hook
import qualified Unicorn.CPU.X86 as X86
import Control.Monad.Trans.Class (lift)
import qualified Data.ByteString as BS
import Data.Word
import qualified Numeric as N (showHex)
import System.Environment
-- Code to be emulated
x86Code32 :: BS.ByteString
x86Code32 = BS.pack [0xeb, 0x19, 0x31, 0xc0, 0x31, 0xdb, 0x31, 0xd2, 0x31,
0xc9, 0xb0, 0x04, 0xb3, 0x01, 0x59, 0xb2, 0x05, 0xcd,
0x80, 0x31, 0xc0, 0xb0, 0x01, 0x31, 0xdb, 0xcd, 0x80,
0xe8, 0xe2, 0xff, 0xff, 0xff, 0x68, 0x65, 0x6c, 0x6c,
0x6f]
x86Code32Self :: BS.ByteString
x86Code32Self = BS.pack [0xeb, 0x1c, 0x5a, 0x89, 0xd6, 0x8b, 0x02, 0x66, 0x3d,
0xca, 0x7d, 0x75, 0x06, 0x66, 0x05, 0x03, 0x03, 0x89,
0x02, 0xfe, 0xc2, 0x3d, 0x41, 0x41, 0x41, 0x41, 0x75,
0xe9, 0xff, 0xe6, 0xe8, 0xdf, 0xff, 0xff, 0xff, 0x31,
0xd2, 0x6a, 0x0b, 0x58, 0x99, 0x52, 0x68, 0x2f, 0x2f,
0x73, 0x68, 0x68, 0x2f, 0x62, 0x69, 0x6e, 0x89, 0xe3,
0x52, 0x53, 0x89, 0xe1, 0xca, 0x7d, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41]
-- Memory address where emulation starts
address :: Word64
address = 0x1000000
-- Pretty-print integral as hex
showHex :: (Integral a, Show a) => a -> String
showHex =
flip N.showHex ""
-- Pretty-print byte string as hex
showHexBS :: BS.ByteString -> String
showHexBS =
concatMap (flip N.showHex " ") . BS.unpack
-- Write a string (with a newline character) to standard output in the emulator
emuPutStrLn :: String -> Emulator ()
emuPutStrLn =
lift . putStrLn
-- Calculate code length
codeLength :: Num a => BS.ByteString -> a
codeLength =
fromIntegral . BS.length
-- Callback for tracing instructions
hookCode :: CodeHook ()
hookCode uc addr size _ = do
runEmulator $ do
emuPutStrLn $ "Tracing instruction at 0x" ++ showHex addr ++
", instruction size = 0x" ++ (maybe "0" showHex size)
eip <- regRead uc X86.Eip
tmp <- memRead uc addr (maybe 0 id size)
emuPutStrLn $ "*** EIP = " ++ showHex eip ++ " ***: " ++ showHexBS tmp
return ()
-- Callback for handling interrupts
-- ref: http://syscalls.kernelgrok.com
hookIntr :: InterruptHook ()
hookIntr uc intno _
| intno == 0x80 = do
runEmulator $ do
eax <- regRead uc X86.Eax
eip <- regRead uc X86.Eip
case eax of
-- sys_exit
1 -> do
emuPutStrLn $ ">>> 0x" ++ showHex eip ++
": interrupt 0x" ++ showHex intno ++
", SYS_EXIT. quit!\n"
stop uc
-- sys_write
4 -> do
-- ECX = buffer address
ecx <- regRead uc X86.Ecx
-- EDX = buffer size
edx <- regRead uc X86.Edx
-- Read the buffer in
buffer <- memRead uc (fromIntegral ecx) (fromIntegral edx)
err <- errno uc
if err == ErrOk then
emuPutStrLn $ ">>> 0x" ++ showHex eip ++
": interrupt 0x" ++ showHex intno ++
", SYS_WRITE. buffer = 0x" ++
showHex ecx ++ ", size = " ++
show edx ++ ", content = " ++
showHexBS buffer
else
emuPutStrLn $ ">>> 0x" ++ showHex eip ++
": interrupt 0x" ++ showHex intno ++
", SYS_WRITE. buffer = 0x" ++
showHex ecx ++ ", size = " ++ show edx ++
" (cannot get content)"
_ -> emuPutStrLn $ ">>> 0x" ++ showHex eip ++
": interrupt 0x" ++ showHex intno ++
", EAX = 0x" ++ showHex eax
return ()
| otherwise = return ()
testI386 :: IO ()
testI386 = do
result <- runEmulator $ do
emuPutStrLn "Emulate i386 code"
-- Initialize emulator in X86-32bit mode
uc <- open ArchX86 [Mode32]
-- Map 2MB memory for this emulation
memMap uc address (2 * 1024 * 1024) [ProtAll]
-- Write machine code to be emulated to memory
memWrite uc address x86Code32Self
-- Initialize machine registers
regWrite uc X86.Esp (fromIntegral address + 0x200000)
-- Tracing all instructions by having @begin > @end
codeHookAdd uc hookCode () 1 0
-- Handle interrupt ourself
interruptHookAdd uc hookIntr () 1 0
emuPutStrLn "\n>>> Start tracing this Linux code"
-- Emulate machine code in infinite time
let codeLen = codeLength x86Code32Self
start uc address (address + codeLen) Nothing Nothing
case result of
Right _ -> putStrLn "\n>>> Emulation done."
Left err -> putStrLn $ "Failed with error " ++ show err ++ ": " ++
strerror err
main :: IO ()
main = do
progName <- getProgName
args <- getArgs
case args of
["-32"] -> testI386
_ -> putStrLn $ "Syntax: " ++ progName ++ " <-32|-64>"

View file

@ -0,0 +1,284 @@
{-|
Module : Unicorn
Description : The Unicorn CPU emulator.
Copyright : (c) Adrian Herrera, 2016
License : GPL-2
Unicorn is a lightweight, multi-platform, multi-architecture CPU emulator
framework based on QEMU.
Further information is available at <http://www.unicorn-engine.org>.
-}
module Unicorn (
-- * Emulator control
Emulator,
Engine,
Architecture(..),
Mode(..),
QueryType(..),
runEmulator,
open,
query,
start,
stop,
-- * Register operations
regWrite,
regRead,
-- * Memory operations
MemoryPermission(..),
MemoryRegion(..),
memWrite,
memRead,
memMap,
memUnmap,
memProtect,
memRegions,
-- * Error handling
Error(..),
errno,
strerror,
-- * Misc.
version,
) where
import Control.Monad (liftM)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Either (hoistEither, left, right, runEitherT)
import Foreign
import Prelude hiding (until)
import Data.ByteString (ByteString, pack)
import Unicorn.Internal.Core
import Unicorn.Internal.Unicorn
-------------------------------------------------------------------------------
-- Emulator control
-------------------------------------------------------------------------------
-- | Run the Unicorn emulator and return a result on success, or an 'Error' on
-- failure.
runEmulator :: Emulator a -- ^ The emulation code to execute
-> IO (Either Error a) -- ^ A result on success, or an 'Error' on
-- failure
runEmulator =
runEitherT
-- | Create a new instance of the Unicorn engine.
open :: Architecture -- ^ CPU architecture
-> [Mode] -- ^ CPU hardware mode
-> Emulator Engine -- ^ A 'Unicorn' engine on success, or an 'Error' on
-- failure
open arch mode = do
(err, ucPtr) <- lift $ ucOpen arch mode
if err == ErrOk then
-- Return a pointer to the unicorn engine if ucOpen completed
-- successfully
lift $ mkEngine ucPtr
else
-- Otherwise return the error
left err
-- | Query internal status of the Unicorn engine.
query :: Engine -- ^ 'Unicorn' engine handle
-> QueryType -- ^ Query type
-> Emulator Int -- ^ The result of the query
query uc queryType = do
(err, result) <- lift $ ucQuery uc queryType
if err == ErrOk then
right result
else
left err
-- | Emulate machine code for a specific duration of time.
start :: Engine -- ^ 'Unicorn' engine handle
-> Word64 -- ^ Address where emulation starts
-> Word64 -- ^ Address where emulation stops (i.e. when this
-- address is hit)
-> Maybe Int -- ^ Optional duration to emulate code (in
-- microseconds).
-- If 'Nothing' is provided, continue to emulate
-- until the code is finished
-> Maybe Int -- ^ Optional number of instructions to emulate. If
-- 'Nothing' is provided, emulate all the code
-- available, until the code is finished
-> Emulator () -- ^ An 'Error' on failure
start uc begin until timeout count = do
err <- lift $ ucEmuStart uc begin until (maybeZ timeout) (maybeZ count)
if err == ErrOk then
right ()
else
left err
where maybeZ = maybe 0 id
-- | Stop emulation (which was started by 'start').
-- This is typically called from callback functions registered by tracing APIs.
--
-- NOTE: For now, this will stop execution only after the current block.
stop :: Engine -- ^ 'Unicorn' engine handle
-> Emulator () -- ^ An 'Error' on failure
stop uc = do
err <- lift $ ucEmuStop uc
if err == ErrOk then
right ()
else
left err
-------------------------------------------------------------------------------
-- Register operations
-------------------------------------------------------------------------------
-- | Write to register.
regWrite :: Reg r =>
Engine -- ^ 'Unicorn' engine handle
-> r -- ^ Register ID to write to
-> Int64 -- ^ Value to write to register
-> Emulator () -- ^ An 'Error' on failure
regWrite uc regId value = do
err <- lift . alloca $ \ptr -> do
poke ptr value
ucRegWrite uc regId ptr
if err == ErrOk then
right ()
else
left err
-- | Read register value.
regRead :: Reg r =>
Engine -- ^ 'Unicorn' engine handle
-> r -- ^ Register ID to read from
-> Emulator Int64 -- ^ The value read from the register on success,
-- or an 'Error' on failure
regRead uc regId = do
(err, val) <- lift $ ucRegRead uc regId
if err == ErrOk then
right val
else
left err
-------------------------------------------------------------------------------
-- Memory operations
-------------------------------------------------------------------------------
-- | Write to memory.
memWrite :: Engine -- ^ 'Unicorn' engine handle
-> Word64 -- ^ Starting memory address of bytes to write
-> ByteString -- ^ The data to write
-> Emulator () -- ^ An 'Error' on failure
memWrite uc address bytes = do
err <- lift $ ucMemWrite uc address bytes
if err == ErrOk then
right ()
else
left err
-- | Read memory contents.
memRead :: Engine -- ^ 'Unicorn' engine handle
-> Word64 -- ^ Starting memory address to read
-- from
-> Int -- ^ Size of memory to read (in bytes)
-> Emulator ByteString -- ^ The memory contents on success, or
-- an 'Error' on failure
memRead uc address size = do
-- Allocate an array of the given size
result <- lift . allocaArray size $ \ptr -> do
err <- ucMemRead uc address ptr size
if err == ErrOk then
-- If ucMemRead completed successfully, pack the contents of the
-- array into a ByteString and return it
liftM (Right . pack) (peekArray size ptr)
else
-- Otherwise return the error
return $ Left err
hoistEither result
-- | Map a range of memory.
memMap :: Engine -- ^ 'Unicorn' engine handle
-> Word64 -- ^ Start address of the new memory region to
-- be mapped in. This address must be
-- aligned to 4KB, or this will return with
-- 'ErrArg' error
-> Int -- ^ Size of the new memory region to be mapped
-- in. This size must be a multiple of 4KB, or
-- this will return with an 'ErrArg' error
-> [MemoryPermission] -- ^ Permissions for the newly mapped region
-> Emulator () -- ^ An 'Error' on failure
memMap uc address size perms = do
err <- lift $ ucMemMap uc address size perms
if err == ErrOk then
right ()
else
left err
-- | Unmap a range of memory.
memUnmap :: Engine -- ^ 'Unicorn' engine handle
-> Word64 -- ^ Start addres of the memory region to be unmapped.
-- This address must be aligned to 4KB or this will
-- return with an 'ErrArg' error
-> Int -- ^ Size of the memory region to be modified. This
-- must be a multiple of 4KB, or this will return with
-- an 'ErrArg' error
-> Emulator () -- ^ An 'Error' on failure
memUnmap uc address size = do
err <- lift $ ucMemUnmap uc address size
if err == ErrOk then
right ()
else
left err
-- | Change permissions on a range of memory.
memProtect :: Engine -- ^ 'Unicorn' engine handle
-> Word64 -- ^ Start address of the memory region to
-- modify. This address must be aligned to
-- 4KB, or this will return with an
-- 'ErrArg' error
-> Int -- ^ Size of the memory region to be
-- modified. This size must be a multiple
-- of 4KB, or this will return with an
-- 'ErrArg' error
-> [MemoryPermission] -- ^ New permissions for the mapped region
-> Emulator () -- ^ An 'Error' on failure
memProtect uc address size perms = do
err <- lift $ ucMemProtect uc address size perms
if err == ErrOk then
right ()
else
left err
-- | Retrieve all memory regions mapped by 'memMap'.
memRegions :: Engine -- ^ 'Unicorn' engine handle
-> Emulator [MemoryRegion] -- ^ A list of memory regions
memRegions uc = do
(err, regionPtr, count) <- lift $ ucMemRegions uc
if err == ErrOk then do
regions <- lift $ peekArray count regionPtr
right regions
else
left err
-------------------------------------------------------------------------------
-- Misc.
-------------------------------------------------------------------------------
-- | Combined API version & major and minor version numbers. Returns a
-- hexadecimal number as (major << 8 | minor), which encodes both major and
-- minor versions.
version :: Int
version =
ucVersion nullPtr nullPtr
-- | Report the 'Error' of the last failed API call.
errno :: Engine -- ^ 'Unicorn' engine handle
-> Emulator Error -- ^ The last 'Error' code
errno =
lift . ucErrno
-- | Return a string describing the given 'Error'.
strerror :: Error -- ^ The 'Error' code
-> String -- ^ Description of the error code
strerror =
ucStrerror

View file

@ -0,0 +1,29 @@
{-# LANGUAGE ForeignFunctionInterface #-}
{-|
Module : Unicorn.CPU.Arm
Description : Definitions for the ARM architecture.
Copyright : (c) Adrian Herrera, 2016
License : GPL-2
Definitions for the ARM architecture.
-}
module Unicorn.CPU.Arm (
Register(..),
) where
import Unicorn.Internal.Core (Reg)
{# context lib="unicorn" #}
#include <unicorn/arm.h>
-- | ARM registers.
{# enum uc_arm_reg as Register
{underscoreToCase}
omit (UC_ARM_REG_INVALID,
UC_ARM_REG_ENDING)
with prefix="UC_ARM_REG_"
deriving (Show, Eq, Bounded) #}
instance Reg Register

View file

@ -0,0 +1,29 @@
{-# LANGUAGE ForeignFunctionInterface #-}
{-|
Module : Unicorn.CPU.Arm64
Description : Definitions for the ARM64 (ARMv8) architecture.
Copyright : (c) Adrian Herrera, 2016
License : GPL-2
Definitions for the ARM64 (ARMv8) architecture.
-}
module Unicorn.CPU.Arm64 (
Register(..),
) where
import Unicorn.Internal.Core (Reg)
{# context lib="unicorn" #}
#include <unicorn/arm64.h>
-- | ARM64 registers.
{# enum uc_arm64_reg as Register
{underscoreToCase}
omit (UC_ARM64_REG_INVALID,
UC_ARM64_REG_ENDING)
with prefix="UC_ARM64_REG_"
deriving (Show, Eq, Bounded) #}
instance Reg Register

View file

@ -0,0 +1,29 @@
{-# LANGUAGE ForeignFunctionInterface #-}
{-|
Module : Unicorn.CPU.Mk68k
Description : Definitions for the MK68K architecture.
Copyright : (c) Adrian Herrera, 2016
License : GPL-2
Definitions for the MK68K architecture.
-}
module Unicorn.CPU.M68k (
Register(..),
) where
import Unicorn.Internal.Core (Reg)
{# context lib="unicorn" #}
#include <unicorn/m68k.h>
-- | M68K registers.
{# enum uc_m68k_reg as Register
{underscoreToCase}
omit (UC_M68K_REG_INVALID,
UC_M68K_REG_ENDING)
with prefix="UC_M68K_REG_"
deriving (Show, Eq, Bounded) #}
instance Reg Register

View file

@ -0,0 +1,61 @@
{-# LANGUAGE ForeignFunctionInterface #-}
{-|
Module : Unicorn.CPU.Mips
Description : Definitions for the MIPS architecture.
Copyright : (c) Adrian Herrera, 2016
License : GPL-2
Definitions for the MIPS architecture.
-}
module Unicorn.CPU.Mips (
Register(..),
) where
import Unicorn.Internal.Core (Reg)
{# context lib="unicorn" #}
#include <unicorn/mips.h>
-- | MIPS registers.
{# enum UC_MIPS_REG as Register
{underscoreToCase,
UC_MIPS_REG_0 as Reg0,
UC_MIPS_REG_1 as Reg1,
UC_MIPS_REG_2 as Reg2,
UC_MIPS_REG_3 as Reg3,
UC_MIPS_REG_4 as Reg4,
UC_MIPS_REG_5 as Reg5,
UC_MIPS_REG_6 as Reg6,
UC_MIPS_REG_7 as Reg7,
UC_MIPS_REG_8 as Reg8,
UC_MIPS_REG_9 as Reg9,
UC_MIPS_REG_10 as Reg10,
UC_MIPS_REG_11 as Reg11,
UC_MIPS_REG_12 as Reg12,
UC_MIPS_REG_13 as Reg13,
UC_MIPS_REG_14 as Reg14,
UC_MIPS_REG_15 as Reg15,
UC_MIPS_REG_16 as Reg16,
UC_MIPS_REG_17 as Reg17,
UC_MIPS_REG_18 as Reg18,
UC_MIPS_REG_19 as Reg19,
UC_MIPS_REG_20 as Reg20,
UC_MIPS_REG_21 as Reg21,
UC_MIPS_REG_22 as Reg22,
UC_MIPS_REG_23 as Reg23,
UC_MIPS_REG_24 as Reg24,
UC_MIPS_REG_25 as Reg25,
UC_MIPS_REG_26 as Reg26,
UC_MIPS_REG_27 as Reg27,
UC_MIPS_REG_28 as Reg28,
UC_MIPS_REG_29 as Reg29,
UC_MIPS_REG_30 as Reg30,
UC_MIPS_REG_31 as Reg31}
omit (UC_MIPS_REG_INVALID,
UC_MIPS_REG_ENDING)
with prefix="UC_MIPS_REG_"
deriving (Show, Eq, Bounded) #}
instance Reg Register

View file

@ -0,0 +1,29 @@
{-# LANGUAGE ForeignFunctionInterface #-}
{-|
Module : Unicorn.CPU.Sparc
Description : Definitions for the SPARC architecture.
Copyright : (c) Adrian Herrera, 2016
License : GPL-2
Definitions for the SPARC architecture.
-}
module Unicorn.CPU.Sparc (
Register(..),
) where
import Unicorn.Internal.Core (Reg)
{# context lib="unicorn" #}
#include <unicorn/sparc.h>
-- | SPARC registers.
{# enum uc_sparc_reg as Register
{underscoreToCase}
omit (UC_SPARC_REG_INVALID,
UC_SPARC_REG_ENDING)
with prefix="UC_SPARC_REG_"
deriving (Show, Eq, Bounded) #}
instance Reg Register

View file

@ -0,0 +1,65 @@
{-# LANGUAGE ForeignFunctionInterface #-}
{-|
Module : Unicorn.CPU.X86
Description : Definitions for the X86 architecture.
Copyright : (c) Adrian Herrera, 2016
License : GPL-2
Definitions for the X86 architecture.
-}
module Unicorn.CPU.X86 (
Mmr(..),
Register(..),
Instruction(..),
) where
import Control.Applicative
import Data.Word
import Foreign
import Unicorn.Internal.Core (Reg)
{# context lib="unicorn" #}
#include <unicorn/x86.h>
-- | Memory-managemen Register for instructions IDTR, GDTR, LDTR, TR.
-- Borrow from SegmentCache in qemu/target-i386/cpu.h
data Mmr = Mmr {
mmrSelector :: Word16, -- ^ Not used by GDTR and IDTR
mmrBase :: Word64, -- ^ Handle 32 or 64 bit CPUs
mmrLimit :: Word32,
mmrFlags :: Word32 -- ^ Not used by GDTR and IDTR
}
instance Storable Mmr where
sizeOf _ = {# sizeof uc_x86_mmr #}
alignment _ = {# alignof uc_x86_mmr #}
peek p = Mmr <$> liftA fromIntegral ({# get uc_x86_mmr->selector #} p)
<*> liftA fromIntegral ({# get uc_x86_mmr->base #} p)
<*> liftA fromIntegral ({# get uc_x86_mmr->limit #} p)
<*> liftA fromIntegral ({# get uc_x86_mmr->flags #} p)
poke p mmr = do
{# set uc_x86_mmr.selector #} p (fromIntegral $ mmrSelector mmr)
{# set uc_x86_mmr.base #} p (fromIntegral $ mmrBase mmr)
{# set uc_x86_mmr.limit #} p (fromIntegral $ mmrLimit mmr)
{# set uc_x86_mmr.flags #} p (fromIntegral $ mmrFlags mmr)
-- | X86 registers.
{# enum uc_x86_reg as Register
{underscoreToCase}
omit (UC_X86_REG_INVALID,
UC_X86_REG_ENDING)
with prefix="UC_X86_REG_"
deriving (Show, Eq, Bounded) #}
instance Reg Register
-- | X86 instructions.
{# enum uc_x86_insn as Instruction
{underscoreToCase}
omit (UC_X86_INS_INVALID,
UC_X86_INS_ENDING)
with prefix="UC_X86_INS_"
deriving (Show, Eq, Bounded) #}

View file

@ -0,0 +1,223 @@
{-|
Module : Unicorn.Hook
Description : Unicorn hooks.
Copyright : (c) Adrian Herrera, 2016
License : GPL-2
Insert hook points into the Unicorn emulator engine.
-}
module Unicorn.Hook (
-- * Hook types
Hook,
MemoryHookType(..),
MemoryEventHookType(..),
MemoryAccess(..),
-- * Hook callbacks
CodeHook,
InterruptHook,
BlockHook,
InHook,
OutHook,
SyscallHook,
MemoryHook,
MemoryReadHook,
MemoryWriteHook,
MemoryEventHook,
-- * Hook callback management
codeHookAdd,
interruptHookAdd,
blockHookAdd,
inHookAdd,
outHookAdd,
syscallHookAdd,
memoryHookAdd,
memoryEventHookAdd,
hookDel,
) where
import Control.Monad
import Control.Monad.Trans.Class
import Control.Monad.Trans.Either (hoistEither, left, right)
import Foreign
import Unicorn.Internal.Core
import Unicorn.Internal.Hook
import qualified Unicorn.CPU.X86 as X86
-------------------------------------------------------------------------------
-- Hook callback management (registration and deletion)
-------------------------------------------------------------------------------
-- | Register a callback for a code hook event.
codeHookAdd :: Storable a
=> Engine -- ^ 'Unicorn' engine handle
-> CodeHook a -- ^ Code hook callback
-> a -- ^ User-defined data. This will be passed to
-- the callback function
-> Word64 -- ^ Start address
-> Word64 -- ^ End address
-> Emulator Hook -- ^ The hook handle on success, or an 'Error'
-- on failure
codeHookAdd uc callback userData begin end = do
result <- lift . alloca $ \userDataPtr -> do
poke userDataPtr userData
funPtr <- marshalCodeHook callback
getResult $ ucHookAdd uc HookCode funPtr userDataPtr begin end
hoistEither result
-- | Register a callback for an interrupt hook event.
interruptHookAdd :: Storable a
=> Engine -- ^ 'Unicorn' engine handle
-> InterruptHook a -- ^ Interrupt callback
-> a -- ^ User-defined data. This will be passed
-- to the callback function
-> Word64 -- ^ Start address
-> Word64 -- ^ End address
-> Emulator Hook -- ^ The hook handle on success, or 'Error'
-- on failure
interruptHookAdd uc callback userData begin end = do
result <- lift . alloca $ \userDataPtr -> do
poke userDataPtr userData
funPtr <- marshalInterruptHook callback
getResult $ ucHookAdd uc HookIntr funPtr userDataPtr begin end
hoistEither result
-- | Register a callback for a block hook event.
blockHookAdd :: Storable a
=> Engine -- ^ 'Unicorn' engine handle
-> BlockHook a -- ^ Block callback
-> a -- ^ User-defined data. This will be passed to
-- the callback function
-> Word64 -- ^ Start address
-> Word64 -- ^ End address
-> Emulator Hook -- ^ The hook handle on success, or an 'Error'
-- on failure
blockHookAdd uc callback userData begin end = do
result <- lift . alloca $ \userDataPtr -> do
poke userDataPtr userData
funPtr <- marshalBlockHook callback
getResult $ ucHookAdd uc HookBlock funPtr userDataPtr begin end
hoistEither result
-- | Register a callback for an IN instruction hook event (X86).
inHookAdd :: Storable a
=> Engine -- ^ 'Unicorn' engine handle
-> InHook a -- ^ IN instruction callback
-> a -- ^ User-defined data. This will be passed to the
-- callback function
-> Word64 -- ^ Start address
-> Word64 -- ^ End address
-> Emulator Hook -- ^ The hook handle on success, or an 'Error' on
-- failure
inHookAdd uc callback userData begin end = do
result <- lift . alloca $ \userDataPtr -> do
poke userDataPtr userData
funPtr <- marshalInHook callback
getResult $ ucInsnHookAdd uc HookInsn funPtr userDataPtr begin end
X86.In
hoistEither result
-- | Register a callback for an OUT instruction hook event (X86).
outHookAdd :: Storable a
=> Engine -- ^ 'Unicorn' engine handle
-> OutHook a -- ^ OUT instruction callback
-> a -- ^ User-defined data. This will be passed to the
-- callback function
-> Word64 -- ^ Start address
-> Word64 -- ^ End address
-> Emulator Hook -- ^ The hook handle on success, or an 'Error' on
-- failure
outHookAdd uc callback userData begin end = do
result <- lift . alloca $ \userDataPtr -> do
poke userDataPtr userData
funPtr <- marshalOutHook callback
getResult $ ucInsnHookAdd uc HookInsn funPtr userDataPtr begin end
X86.Out
hoistEither result
-- | Register a callback for a SYSCALL instruction hook event (X86).
syscallHookAdd :: Storable a
=> Engine -- ^ 'Unicorn' engine handle
-> SyscallHook a -- ^ SYSCALL instruction callback
-> a -- ^ User-defined data. This will be passed to
-- the callback function
-> Word64 -- ^ Start address
-> Word64 -- ^ End address
-> Emulator Hook -- ^ The hook handle on success, or an 'Error'
-- on failure
syscallHookAdd uc callback userData begin end = do
result <- lift . alloca $ \userDataPtr -> do
poke userDataPtr userData
funPtr <- marshalSyscallHook callback
getResult $ ucInsnHookAdd uc HookInsn funPtr userDataPtr begin end
X86.Syscall
hoistEither result
-- | Register a callback for a valid memory access event.
memoryHookAdd :: Storable a
=> Engine -- ^ 'Unicorn' engine handle
-> MemoryHookType -- ^ A valid memory access (e.g. read, write,
-- etc.) to trigger the callback on
-> MemoryHook a -- ^ Memory access callback
-> a -- ^ User-defined data. This will be passed to
-- the callback function
-> Word64 -- ^ Start address
-> Word64 -- ^ End address
-> Emulator Hook -- ^ The hook handle on success, or an 'Error'
-- on failure
memoryHookAdd uc memHookType callback userData begin end = do
result <- lift . alloca $ \userDataPtr -> do
poke userDataPtr userData
funPtr <- marshalMemoryHook callback
getResult $ ucHookAdd uc memHookType funPtr userDataPtr begin end
hoistEither result
-- | Register a callback for an invalid memory access event.
memoryEventHookAdd :: Storable a
=> Engine -- ^ 'Unicorn' engine handle
-> MemoryEventHookType -- ^ An invalid memory access (e.g.
-- read, write, etc.) to trigger
-- the callback on
-> MemoryEventHook a -- ^ Invalid memory access callback
-> a -- ^ User-defined data. This will
-- be passed to the callback
-- function
-> Word64 -- ^ Start address
-> Word64 -- ^ End address
-> Emulator Hook -- ^ The hook handle on success, or
-- an 'Error' on failure
memoryEventHookAdd uc memEventHookType callback userData begin end = do
result <- lift . alloca $ \userDataPtr -> do
poke userDataPtr userData
funPtr <- marshalMemoryEventHook callback
getResult $ ucHookAdd uc memEventHookType funPtr userDataPtr begin end
hoistEither result
-- | Unregister (remove) a hook callback.
hookDel :: Engine -- ^ 'Unicorn' engine handle
-> Hook -- ^ 'Hook' handle
-> Emulator () -- ^ 'ErrOk' on success, or other value on failure
hookDel uc hook = do
err <- lift $ ucHookDel uc hook
if err == ErrOk then
right ()
else
left err
-------------------------------------------------------------------------------
-- Helper functions
-------------------------------------------------------------------------------
-- Takes the tuple returned by `ucHookAdd`, an IO (Error, Hook), and
-- returns either a `Right Hook` if no error occurred or a `Left Error` if an
-- error occurred
getResult :: IO (Error, Hook) -> IO (Either Error Hook)
getResult =
liftM (uncurry checkResult)
where checkResult err hook =
if err == ErrOk then
Right hook
else
Left err

View file

@ -0,0 +1,52 @@
{-# LANGUAGE ForeignFunctionInterface #-}
{-|
Module : Unicorn.Internal.Core
Description : Core Unicorn components.
Copyright : (c) Adrian Herrera, 2016
License : GPL-2
Defines core Unicorn components.
This module should not be directly imported; it is only exposed because of the
way cabal handles ordering of chs files.
-}
module Unicorn.Internal.Core where
import Control.Monad
import Control.Monad.Trans.Either (EitherT)
import Foreign
{# context lib="unicorn" #}
#include <unicorn/unicorn.h>
#include "unicorn_wrapper.h"
-- | The Unicorn engine.
{# pointer *uc_engine as Engine
foreign finalizer uc_close_wrapper as close
newtype #}
-- | A pointer to a Unicorn engine.
{# pointer *uc_engine as EnginePtr -> Engine #}
-- | Make a new Unicorn engine out of an engine pointer. The returned Unicorn
-- engine will automatically close 'uc_close_wrapper' when it goes out of
-- scope.
mkEngine :: EnginePtr -> IO Engine
mkEngine ptr =
liftM Engine (newForeignPtr close ptr)
-- | Errors encountered by the Unicorn API. These values are returned by
-- 'errno'.
{# enum uc_err as Error
{underscoreToCase}
with prefix = "UC_"
deriving (Show, Eq, Bounded) #}
-- | The emulator runs in the IO monad and allows for the handling of errors
-- "under the hood".
type Emulator a = EitherT Error IO a
-- | An architecture-dependent register.
class Enum a => Reg a

View file

@ -0,0 +1,415 @@
{-# LANGUAGE ForeignFunctionInterface #-}
{-|
Module : Unicorn.Internal.Hook
Description : Unicorn hooks.
Copyright : (c) Adrian Herrera, 2016
License : GPL-2
Low-level bindings for inserting hook points into the Unicorn emulator engine.
This module should not be directly imported; it is only exposed because of the
way cabal handles ordering of chs files.
-}
module Unicorn.Internal.Hook (
-- * Types
Hook,
HookType(..),
MemoryHookType(..),
MemoryEventHookType(..),
MemoryAccess(..),
-- * Hook callback bindings
CodeHook,
InterruptHook,
BlockHook,
InHook,
OutHook,
SyscallHook,
MemoryHook,
MemoryReadHook,
MemoryWriteHook,
MemoryEventHook,
-- * Hook marshalling
marshalCodeHook,
marshalInterruptHook,
marshalBlockHook,
marshalInHook,
marshalOutHook,
marshalSyscallHook,
marshalMemoryHook,
marshalMemoryReadHook,
marshalMemoryWriteHook,
marshalMemoryEventHook,
-- * Hook registration and deletion bindings
ucHookAdd,
ucInsnHookAdd,
ucHookDel,
) where
import Control.Monad
import Foreign
import Unicorn.Internal.Util
{# context lib="unicorn" #}
{# import Unicorn.Internal.Core #}
{# import Unicorn.CPU.X86 #}
#include <unicorn/unicorn.h>
#include "unicorn_wrapper.h"
-------------------------------------------------------------------------------
-- Types
-------------------------------------------------------------------------------
-- When we pass a Unicorn engine to a hook callback, we do not want this engine
-- object to be freed automatically when the callback returns (which is what
-- would typically occur when using a ForeignPtr), because we want to continue
-- using the Unicorn engine outside the callback. To avoid this,
-- unicorn_wrapper.h provides a dummy "close" function that does nothing. When
-- we go to create a Unicorn engine to pass to a callback, we use a pointer to
-- this dummy close function as the finalizer pointer. When the callback
-- returns, the Unicorn engine remains untouched!
--
-- XX Is there a better way to do this?
foreign import ccall "&uc_close_dummy"
closeDummy :: FunPtr (EnginePtr -> IO ())
mkEngineNC :: EnginePtr -> IO Engine
mkEngineNC ptr =
liftM Engine (newForeignPtr closeDummy ptr)
-- | A Unicorn hook.
type Hook = {# type uc_hook #}
-- Hook types. These are used internally within this module by the callback
-- registration functions and are not exposed to the user.
--
-- Note that the both valid and invalid memory access hooks are omitted from
-- this enum (and are exposed to the user).
{# enum uc_hook_type as HookType
{underscoreToCase}
omit (UC_HOOK_MEM_READ_UNMAPPED,
UC_HOOK_MEM_WRITE_UNMAPPED,
UC_HOOK_MEM_FETCH_UNMAPPED,
UC_HOOK_MEM_READ_PROT,
UC_HOOK_MEM_WRITE_PROT,
UC_HOOK_MEM_FETCH_PROT,
UC_HOOK_MEM_READ,
UC_HOOK_MEM_WRITE,
UC_HOOK_MEM_FETCH)
with prefix="UC_"
deriving (Show, Eq, Bounded) #}
-- | Memory hook types (for valid memory accesses).
{# enum uc_hook_type as MemoryHookType
{underscoreToCase}
omit (UC_HOOK_INTR,
UC_HOOK_INSN,
UC_HOOK_CODE,
UC_HOOK_BLOCK,
UC_HOOK_MEM_READ_UNMAPPED,
UC_HOOK_MEM_WRITE_UNMAPPED,
UC_HOOK_MEM_FETCH_UNMAPPED,
UC_HOOK_MEM_READ_PROT,
UC_HOOK_MEM_WRITE_PROT,
UC_HOOK_MEM_FETCH_PROT)
with prefix="UC_"
deriving (Show, Eq, Bounded) #}
-- | Memory event hook types (for invalid memory accesses).
{# enum uc_hook_type as MemoryEventHookType
{underscoreToCase}
omit (UC_HOOK_INTR,
UC_HOOK_INSN,
UC_HOOK_CODE,
UC_HOOK_BLOCK,
UC_HOOK_MEM_READ,
UC_HOOK_MEM_WRITE,
UC_HOOK_MEM_FETCH)
with prefix="UC_"
deriving (Show, Eq, Bounded) #}
-- | Unify the hook types with a type class
class Enum a => HookTypeC a
instance HookTypeC HookType
instance HookTypeC MemoryHookType
instance HookTypeC MemoryEventHookType
-- | Memory access.
{# enum uc_mem_type as MemoryAccess
{underscoreToCase}
with prefix="UC_"
deriving (Show, Eq, Bounded) #}
-------------------------------------------------------------------------------
-- Hook callbacks
-------------------------------------------------------------------------------
-- | Callback function for tracing code.
type CodeHook a = Engine -- ^ 'Unicorn' engine handle
-> Word64 -- ^ Addres where the code is being executed
-> Maybe Int -- ^ Size of machine instruction(s) being
-- executed, or 'Nothing' when size is unknown
-> a -- ^ User data passed to tracing APIs
-> IO ()
type CCodeHook = EnginePtr -> Word64 -> Word32 -> Ptr () -> IO ()
foreign import ccall "wrapper"
mkCodeHook :: CCodeHook -> IO {# type uc_cb_hookcode_t #}
marshalCodeHook :: Storable a
=> CodeHook a -> IO {# type uc_cb_hookcode_t #}
marshalCodeHook codeHook =
mkCodeHook $ \ucPtr address size userDataPtr -> do
uc <- mkEngineNC ucPtr
userData <- castPtrAndPeek userDataPtr
let maybeSize = if size == 0 then Nothing
else Just $ fromIntegral size
codeHook uc address maybeSize userData
-- | Callback function for tracing interrupts.
type InterruptHook a = Engine -- ^ 'Unicorn' engine handle
-> Int -- ^ Interrupt number
-> a -- ^ User data passed to tracing APIs
-> IO ()
type CInterruptHook = EnginePtr -> Word32 -> Ptr () -> IO ()
foreign import ccall "wrapper"
mkInterruptHook :: CInterruptHook -> IO {# type uc_cb_hookintr_t #}
marshalInterruptHook :: Storable a
=> InterruptHook a -> IO {# type uc_cb_hookintr_t #}
marshalInterruptHook interruptHook =
mkInterruptHook $ \ucPtr intNo userDataPtr -> do
uc <- mkEngineNC ucPtr
userData <- castPtrAndPeek userDataPtr
interruptHook uc (fromIntegral intNo) userData
-- | Callback function for tracing blocks.
type BlockHook a = CodeHook a
marshalBlockHook :: Storable a
=> BlockHook a -> IO {# type uc_cb_hookcode_t #}
marshalBlockHook =
marshalCodeHook
-- | Callback function for tracing IN instructions (X86).
type InHook a = Engine -- ^ 'Unicorn' engine handle
-> Int -- ^ Port number
-> Int -- ^ Data size (1/2/4) to be read from this port
-> a -- ^ User data passed to tracing APIs
-> IO Word32 -- ^ The data read from the port
type CInHook = EnginePtr -> Word32 -> Int32 -> Ptr () -> IO Word32
foreign import ccall "wrapper"
mkInHook :: CInHook -> IO {# type uc_cb_insn_in_t #}
marshalInHook :: Storable a
=> InHook a -> IO {# type uc_cb_insn_in_t #}
marshalInHook inHook =
mkInHook $ \ucPtr port size userDataPtr -> do
uc <- mkEngineNC ucPtr
userData <- castPtrAndPeek userDataPtr
inHook uc (fromIntegral port) (fromIntegral size) userData
-- | Callback function for tracing OUT instructions (X86).
type OutHook a = Engine -- ^ 'Unicorn' engine handle
-> Int -- ^ Port number
-> Int -- ^ Data size (1/2/4) to be written to this port
-> Int -- ^ Data value to be written to this port
-> a -- ^ User data passed to tracing APIs
-> IO ()
type COutHook = EnginePtr -> Word32 -> Int32 -> Word32 -> Ptr () -> IO ()
foreign import ccall "wrapper"
mkOutHook :: COutHook -> IO {# type uc_cb_insn_out_t #}
marshalOutHook :: Storable a
=> OutHook a -> IO {# type uc_cb_insn_out_t #}
marshalOutHook outHook =
mkOutHook $ \ucPtr port size value userDataPtr -> do
uc <- mkEngineNC ucPtr
userData <- castPtrAndPeek userDataPtr
outHook uc (fromIntegral port) (fromIntegral size) (fromIntegral value)
userData
-- | Callback function for tracing SYSCALL instructions (X86).
type SyscallHook a = Engine -- ^ 'Unicorn' engine handle
-> a -- ^ User data passed to tracing APIs
-> IO ()
type CSyscallHook = Ptr () -> Ptr () -> IO ()
foreign import ccall "wrapper"
mkSyscallHook :: CSyscallHook -> IO {# type uc_cb_insn_syscall_t #}
marshalSyscallHook :: Storable a
=> SyscallHook a -> IO {# type uc_cb_insn_syscall_t #}
marshalSyscallHook syscallHook =
mkSyscallHook $ \ucPtr userDataPtr -> do
uc <- mkEngineNC $ castPtr ucPtr
userData <- castPtrAndPeek userDataPtr
syscallHook uc userData
-- | Callback function for hooking memory operations.
type MemoryHook a = Engine -- ^ 'Unicorn' engine handle
-> MemoryAccess -- ^ Memory access; read or write
-> Word64 -- ^ Address where the code is being
-- executed
-> Int -- ^ Size of data being read or written
-> Maybe Int -- ^ Value of data being wrriten, or
-- 'Nothing' if read
-> a -- ^ User data passed to tracing APIs
-> IO ()
type CMemoryHook = EnginePtr
-> Int32
-> Word64
-> Int32
-> Int64
-> Ptr ()
-> IO ()
foreign import ccall "wrapper"
mkMemoryHook :: CMemoryHook -> IO {# type uc_cb_hookmem_t #}
marshalMemoryHook :: Storable a
=> MemoryHook a -> IO {# type uc_cb_hookmem_t #}
marshalMemoryHook memoryHook =
mkMemoryHook $ \ucPtr memAccessI address size value userDataPtr -> do
uc <- mkEngineNC ucPtr
userData <- castPtrAndPeek userDataPtr
let memAccess = toMemAccess memAccessI
maybeValue = case memAccess of
MemRead -> Nothing
MemWrite -> Just $ fromIntegral value
_ -> undefined -- XX Handle this?
memoryHook uc memAccess address (fromIntegral size) maybeValue userData
-- | Callback function for hooking memory reads.
type MemoryReadHook a = Engine -- ^ 'Unicorn' engine handle
-> Word64 -- ^ Address where the code is being executed
-> Int -- ^ Size of data being read
-> a -- ^ User data passed to tracing APIs
-> IO ()
marshalMemoryReadHook :: Storable a
=> MemoryReadHook a -> IO {# type uc_cb_hookmem_t #}
marshalMemoryReadHook memoryReadHook =
mkMemoryHook $ \ucPtr _ address size _ userDataPtr -> do
uc <- mkEngineNC ucPtr
userData <- castPtrAndPeek userDataPtr
memoryReadHook uc address (fromIntegral size) userData
-- | Callback function for hooking memory writes.
type MemoryWriteHook a = Engine -- ^ 'Unicorn' engine handle
-> Word64 -- ^ Address where the code is being
-- executed
-> Int -- ^ Size of data being written
-> Int -- ^ Value of data being written
-> a -- ^ User data passed to tracing APIs
-> IO ()
marshalMemoryWriteHook :: Storable a
=> MemoryWriteHook a -> IO {# type uc_cb_hookmem_t #}
marshalMemoryWriteHook memoryWriteHook =
mkMemoryHook $ \ucPtr _ address size value userDataPtr -> do
uc <- mkEngineNC ucPtr
userData <- castPtrAndPeek userDataPtr
memoryWriteHook uc address (fromIntegral size) (fromIntegral value)
userData
-- | Callback function for handling invalid memory access events.
type MemoryEventHook a = Engine -- ^ 'Unicorn' engine handle
-> MemoryAccess -- ^ Memory access; read or write
-> Word64 -- ^ Address where the code is being
-- executed
-> Int -- ^ Size of data being read or written
-> Maybe Int -- ^ Value of data being written, or
-- 'Nothing' if read
-> a -- ^ User data passed to tracing APIs
-> IO Bool -- ^ Return 'True' to continue, or
-- 'False' to stop the program (due to
-- invalid memory)
type CMemoryEventHook = EnginePtr
-> Int32
-> Word64
-> Int32
-> Int64
-> Ptr ()
-> IO Int32
foreign import ccall "wrapper"
mkMemoryEventHook :: CMemoryEventHook -> IO {# type uc_cb_eventmem_t #}
marshalMemoryEventHook :: Storable a
=> MemoryEventHook a -> IO {# type uc_cb_eventmem_t #}
marshalMemoryEventHook eventMemoryHook =
mkMemoryEventHook $ \ucPtr memAccessI address size value userDataPtr -> do
uc <- mkEngineNC ucPtr
userData <- castPtrAndPeek userDataPtr
let memAccess = toMemAccess memAccessI
maybeValue = case memAccess of
MemReadUnmapped -> Nothing
MemReadProt -> Nothing
MemWriteUnmapped -> Just $ fromIntegral value
MemWriteProt -> Just $ fromIntegral value
_ -> undefined -- XX Handle this?
res <- eventMemoryHook uc memAccess address (fromIntegral size)
maybeValue userData
return $ boolToInt res
where boolToInt True = 1
boolToInt False = 0
-------------------------------------------------------------------------------
-- Hook callback registration (and deletion)
-------------------------------------------------------------------------------
{# fun variadic uc_hook_add as ucHookAdd
`(Storable a, HookTypeC h)' =>
{`Engine',
alloca- `Hook' peek*,
enumToNum `h',
castFunPtrToPtr `FunPtr b',
castPtr `Ptr a',
`Word64',
`Word64'}
-> `Error' #}
{# fun variadic uc_hook_add[int] as ucInsnHookAdd
`(Storable a, HookTypeC h)' =>
{`Engine',
alloca- `Hook' peek*,
enumToNum `h',
castFunPtrToPtr `FunPtr b',
castPtr `Ptr a',
`Word64',
`Word64',
enumToNum `Instruction'}
-> `Error' #}
-- | Unregister (remove) a hook callback.
{# fun uc_hook_del as ^
{`Engine',
fromIntegral `Hook'}
-> `Error' #}
-------------------------------------------------------------------------------
-- Helper functions
-------------------------------------------------------------------------------
toMemAccess :: Integral a => a -> MemoryAccess
toMemAccess =
toEnum . fromIntegral

View file

@ -0,0 +1,242 @@
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-|
Module : Unicorn.Internal.Unicorn
Description : The Unicorn CPU emulator.
Copyright : (c) Adrian Herrera, 2016
License : GPL-2
Low-level bindings for the Unicorn CPU emulator framework.
This module should not be directly imported; it is only exposed because of the
way cabal handles ordering of chs files.
-}
module Unicorn.Internal.Unicorn (
-- * Types
Architecture(..),
Mode(..),
MemoryPermission(..),
MemoryRegion(..),
QueryType(..),
-- * Function bindings
ucOpen,
ucQuery,
ucEmuStart,
ucEmuStop,
ucRegWrite,
ucRegRead,
ucMemWrite,
ucMemRead,
ucMemMap,
ucMemUnmap,
ucMemProtect,
ucMemRegions,
ucVersion,
ucErrno,
ucStrerror,
) where
import Foreign
import Foreign.C
import Control.Applicative
import Data.ByteString (ByteString, useAsCStringLen)
import Prelude hiding (until)
import Unicorn.Internal.Util
{# context lib="unicorn" #}
{# import Unicorn.Internal.Core #}
#include <unicorn/unicorn.h>
-------------------------------------------------------------------------------
-- Types
-------------------------------------------------------------------------------
-- | CPU architecture.
{# enum uc_arch as Architecture
{underscoreToCase}
with prefix = "UC_"
deriving (Show, Eq, Bounded) #}
-- | CPU hardware mode.
{# enum uc_mode as Mode
{underscoreToCase}
with prefix="UC_"
deriving (Show, Eq, Bounded) #}
-- | Memory permissions.
{# enum uc_prot as MemoryPermission
{underscoreToCase}
with prefix="UC_"
deriving (Show, Eq, Bounded) #}
-- | Memory region mapped by 'memMap'. Retrieve the list of memory regions with
-- 'memRegions'.
data MemoryRegion = MemoryRegion {
mrBegin :: Word64, -- ^ Begin address of the region (inclusive)
mrEnd :: Word64, -- ^ End address of the region (inclusive)
mrPerms :: [MemoryPermission] -- ^ Memory permissions of the region
}
instance Storable MemoryRegion where
sizeOf _ = {# sizeof uc_mem_region #}
alignment _ = {# alignof uc_mem_region #}
peek p = MemoryRegion
<$> liftA fromIntegral ({# get uc_mem_region->begin #} p)
<*> liftA fromIntegral ({# get uc_mem_region->end #} p)
<*> liftA expandMemPerms ({# get uc_mem_region->perms #} p)
poke p mr = do
{# set uc_mem_region.begin #} p (fromIntegral $ mrBegin mr)
{# set uc_mem_region.end #} p (fromIntegral $ mrEnd mr)
{# set uc_mem_region.perms #} p (combineEnums $ mrPerms mr)
-- | A pointer to a memory region.
{# pointer *uc_mem_region as MemoryRegionPtr -> MemoryRegion #}
-- | Query types for the 'query' API.
{# enum uc_query_type as QueryType
{underscoreToCase}
with prefix="UC_"
deriving (Show, Eq, Bounded) #}
-------------------------------------------------------------------------------
-- Emulator control
-------------------------------------------------------------------------------
{# fun uc_open as ^
{`Architecture',
combineEnums `[Mode]',
alloca- `EnginePtr' peek*}
-> `Error' #}
{# fun uc_query as ^
{`Engine',
`QueryType',
alloca- `Int' castPtrAndPeek*}
-> `Error' #}
{# fun uc_emu_start as ^
{`Engine',
`Word64',
`Word64',
`Int',
`Int'}
-> `Error' #}
{# fun uc_emu_stop as ^
{`Engine'}
-> `Error' #}
-------------------------------------------------------------------------------
-- Register operations
-------------------------------------------------------------------------------
{# fun uc_reg_write as ^
`Reg r' =>
{`Engine',
enumToNum `r',
castPtr `Ptr Int64'}
-> `Error' #}
{# fun uc_reg_read as ^
`Reg r' =>
{`Engine',
enumToNum `r',
allocaInt64ToVoid- `Int64' castPtrAndPeek*}
-> `Error' #}
-------------------------------------------------------------------------------
-- Memory operations
-------------------------------------------------------------------------------
{# fun uc_mem_write as ^
{`Engine',
`Word64',
withByteStringLen* `ByteString'&}
-> `Error' #}
{# fun uc_mem_read as ^
{`Engine',
`Word64',
castPtr `Ptr Word8',
`Int'}
-> `Error' #}
{# fun uc_mem_map as ^
{`Engine',
`Word64',
`Int',
combineEnums `[MemoryPermission]'}
-> `Error' #}
{# fun uc_mem_unmap as ^
{`Engine',
`Word64',
`Int'}
-> `Error' #}
{# fun uc_mem_protect as ^
{`Engine',
`Word64',
`Int',
combineEnums `[MemoryPermission]'}
-> `Error' #}
{# fun uc_mem_regions as ^
{`Engine',
alloca- `MemoryRegionPtr' peek*,
alloca- `Int' castPtrAndPeek*}
-> `Error' #}
-------------------------------------------------------------------------------
-- Misc.
-------------------------------------------------------------------------------
{# fun pure unsafe uc_version as ^
{id `Ptr CUInt',
id `Ptr CUInt'}
-> `Int' #}
{# fun unsafe uc_errno as ^
{`Engine'}
-> `Error' #}
{# fun pure unsafe uc_strerror as ^
{`Error'}
-> `String' #}
-------------------------------------------------------------------------------
-- Helper functions
-------------------------------------------------------------------------------
expandMemPerms :: (Integral a, Bits a) => a -> [MemoryPermission]
expandMemPerms perms =
-- Only interested in the 3 least-significant bits
let maskedPerms = fromIntegral $ perms .&. 0x7 in
if maskedPerms == 0x0 then
[ProtNone]
else if maskedPerms == 0x7 then
[ProtAll]
else
checkRWE maskedPerms [ProtRead, ProtWrite, ProtExec]
where
checkRWE p (x:xs) =
if p .&. (fromEnum x) /= 0 then
x : checkRWE p xs
else
checkRWE p xs
checkRWE _ [] =
[]
allocaInt64ToVoid :: (Ptr () -> IO b) -> IO b
allocaInt64ToVoid f =
alloca $ \(ptr :: Ptr Int64) -> poke ptr 0 >> f (castPtr ptr)
withByteStringLen :: ByteString -> ((Ptr (), CULong) -> IO a) -> IO a
withByteStringLen bs f =
useAsCStringLen bs $ \(ptr, len) -> f (castPtr ptr, fromIntegral len)

View file

@ -0,0 +1,26 @@
{-|
Module : Unicorn.Internal.Util
Description : Utility (aka helper) functions for the Unicorn emulator.
Copyright : (c) Adrian Herrera, 2016
License : GPL-2
-}
module Unicorn.Internal.Util where
import Control.Applicative
import Data.Bits
import Foreign
-- | Combine a list of Enums by performing a bitwise-OR.
combineEnums :: (Enum a, Num b, Bits b) => [a] -> b
combineEnums =
foldr ((.|.) <$> enumToNum) 0
-- | Cast a pointer and then peek inside it.
castPtrAndPeek :: Storable a => Ptr b -> IO a
castPtrAndPeek =
peek . castPtr
-- | Convert an 'Eum' to a 'Num'.
enumToNum :: (Enum a, Num b) => a -> b
enumToNum =
fromIntegral . fromEnum

View file

@ -0,0 +1,8 @@
#include "unicorn_wrapper.h"
void uc_close_wrapper(uc_engine *uc) {
uc_close(uc);
}
void uc_close_dummy(uc_engine *uc) {
}

View file

@ -0,0 +1,16 @@
#ifndef UNICORN_WRAPPER_H
#define UNICORN_WRAPPER_H
#include <unicorn/unicorn.h>
/*
* Wrap Unicorn's uc_close function and ignore the returned error code.
*/
void uc_close_wrapper(uc_engine *uc);
/*
* Doesn't actually do anything.
*/
void uc_close_dummy(uc_engine *uc);
#endif

View file

@ -0,0 +1,42 @@
-- Initial unicorn.cabal generated by cabal init. For further
-- documentation, see http://haskell.org/cabal/users-guide/
name: unicorn
version: 0.1.0.0
category: FFI, Emulation
synopsis: Unicorn CPU emulator engine
description: Haskell bindings for the Unicorn CPU emulator engine.
homepage: https://github.com/unicorn-engine/unicorn
author: Adrian Herrera
license: GPL
copyright: (c) 2016, Adrian Herrera
category: System
build-type: Simple
stability: experimental
cabal-version: >=1.10
extra-source-files: cbits/, include/
library
exposed-modules: Unicorn.Internal.Core
Unicorn.Internal.Unicorn
Unicorn.CPU.Arm64
Unicorn.CPU.Arm
Unicorn.CPU.M68k
Unicorn.CPU.Mips
Unicorn.CPU.Sparc
Unicorn.CPU.X86
Unicorn.Internal.Hook
Unicorn.Hook
Unicorn
other-modules: Unicorn.Internal.Util
build-depends: base >=4 && <5,
bytestring >= 0.9.1,
transformers <= 0.5,
either >= 4.4
hs-source-dirs: src
c-sources: src/cbits/unicorn_wrapper.c
include-dirs: src/include
build-tools: c2hs
pkgconfig-depends: unicorn
default-language: Haskell2010
ghc-options: -Wall

View file

@ -36,7 +36,7 @@ public class Sample_x86_mmr {
} }
// map 4k // map 4k
uc.mem_map(ADDRESS, 0x1000, Unicorn.UC_PROT_ALL); uc.mem_map(0x400000, 0x1000, Unicorn.UC_PROT_ALL);
X86_MMR ldtr1 = new X86_MMR(0x1111111122222222L, 0x33333333, 0x44444444, (short)0x5555); X86_MMR ldtr1 = new X86_MMR(0x1111111122222222L, 0x33333333, 0x44444444, (short)0x5555);
X86_MMR ldtr2; X86_MMR ldtr2;

View file

@ -90,6 +90,7 @@ public interface UnicornConst {
public static final int UC_HOOK_MEM_INVALID = 1008; public static final int UC_HOOK_MEM_INVALID = 1008;
public static final int UC_HOOK_MEM_VALID = 7168; public static final int UC_HOOK_MEM_VALID = 7168;
public static final int UC_QUERY_MODE = 1; public static final int UC_QUERY_MODE = 1;
public static final int UC_QUERY_PAGE_SIZE = 2;
public static final int UC_PROT_NONE = 0; public static final int UC_PROT_NONE = 0;
public static final int UC_PROT_READ = 1; public static final int UC_PROT_READ = 1;

View file

@ -252,7 +252,9 @@ public interface X86Const {
public static final int UC_X86_REG_GDTR = 243; public static final int UC_X86_REG_GDTR = 243;
public static final int UC_X86_REG_LDTR = 244; public static final int UC_X86_REG_LDTR = 244;
public static final int UC_X86_REG_TR = 245; public static final int UC_X86_REG_TR = 245;
public static final int UC_X86_REG_ENDING = 246; public static final int UC_X86_REG_FPCW = 246;
public static final int UC_X86_REG_FPTAG = 247;
public static final int UC_X86_REG_ENDING = 248;
// X86 instructions // X86 instructions

View file

@ -519,7 +519,7 @@ JNIEXPORT jlong JNICALL Java_unicorn_Unicorn_registerHook__JI
if (invokeInterruptCallbacks == 0) { if (invokeInterruptCallbacks == 0) {
invokeInterruptCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeInterruptCallbacks", "(JI)V"); invokeInterruptCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeInterruptCallbacks", "(JI)V");
} }
err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_hookintr, env); err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_hookintr, env, 1, 0);
break; break;
case UC_HOOK_MEM_FETCH_UNMAPPED: // Hook for all invalid memory access events case UC_HOOK_MEM_FETCH_UNMAPPED: // Hook for all invalid memory access events
case UC_HOOK_MEM_READ_UNMAPPED: // Hook for all invalid memory access events case UC_HOOK_MEM_READ_UNMAPPED: // Hook for all invalid memory access events
@ -530,7 +530,7 @@ JNIEXPORT jlong JNICALL Java_unicorn_Unicorn_registerHook__JI
if (invokeEventMemCallbacks == 0) { if (invokeEventMemCallbacks == 0) {
invokeEventMemCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeEventMemCallbacks", "(JIJIJ)Z"); invokeEventMemCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeEventMemCallbacks", "(JIJIJ)Z");
} }
err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_eventmem, env); err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_eventmem, env, 1, 0);
break; break;
} }
return (jlong)hh; return (jlong)hh;
@ -552,18 +552,18 @@ JNIEXPORT jlong JNICALL Java_unicorn_Unicorn_registerHook__JII
if (invokeOutCallbacks == 0) { if (invokeOutCallbacks == 0) {
invokeOutCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeOutCallbacks", "(JIII)V"); invokeOutCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeOutCallbacks", "(JIII)V");
} }
err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_insn_out, env, arg1); err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_insn_out, env, 1, 0, arg1);
case UC_X86_INS_IN: case UC_X86_INS_IN:
if (invokeInCallbacks == 0) { if (invokeInCallbacks == 0) {
invokeInCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeInCallbacks", "(JII)I"); invokeInCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeInCallbacks", "(JII)I");
} }
err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_insn_in, env, arg1); err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_insn_in, env, 1, 0, arg1);
case UC_X86_INS_SYSENTER: case UC_X86_INS_SYSENTER:
case UC_X86_INS_SYSCALL: case UC_X86_INS_SYSCALL:
if (invokeSyscallCallbacks == 0) { if (invokeSyscallCallbacks == 0) {
invokeSyscallCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeSyscallCallbacks", "(J)V"); invokeSyscallCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeSyscallCallbacks", "(J)V");
} }
err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_insn_syscall, env, arg1); err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_insn_syscall, env, 1, 0, arg1);
} }
break; break;
} }
@ -584,25 +584,25 @@ JNIEXPORT jlong JNICALL Java_unicorn_Unicorn_registerHook__JIJJ
if (invokeCodeCallbacks == 0) { if (invokeCodeCallbacks == 0) {
invokeCodeCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeCodeCallbacks", "(JJI)V"); invokeCodeCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeCodeCallbacks", "(JJI)V");
} }
err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_hookcode, env, arg1, arg2); err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_hookcode, env, 1, 0, arg1, arg2);
break; break;
case UC_HOOK_BLOCK: // Hook basic blocks case UC_HOOK_BLOCK: // Hook basic blocks
if (invokeBlockCallbacks == 0) { if (invokeBlockCallbacks == 0) {
invokeBlockCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeBlockCallbacks", "(JJI)V"); invokeBlockCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeBlockCallbacks", "(JJI)V");
} }
err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_hookblock, env, arg1, arg2); err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_hookblock, env, 1, 0, arg1, arg2);
break; break;
case UC_HOOK_MEM_READ: // Hook all memory read events. case UC_HOOK_MEM_READ: // Hook all memory read events.
if (invokeReadCallbacks == 0) { if (invokeReadCallbacks == 0) {
invokeReadCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeReadCallbacks", "(JJI)V"); invokeReadCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeReadCallbacks", "(JJI)V");
} }
err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_hookmem, env, arg1, arg2); err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_hookmem, env, 1, 0, arg1, arg2);
break; break;
case UC_HOOK_MEM_WRITE: // Hook all memory write events. case UC_HOOK_MEM_WRITE: // Hook all memory write events.
if (invokeWriteCallbacks == 0) { if (invokeWriteCallbacks == 0) {
invokeWriteCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeWriteCallbacks", "(JJIJ)V"); invokeWriteCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeWriteCallbacks", "(JJIJ)V");
} }
err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_hookmem, env, arg1, arg2); err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_hookmem, env, 1, 0, arg1, arg2);
break; break;
} }
return (jlong)hh; return (jlong)hh;

View file

@ -3,6 +3,7 @@ uc_version
uc_arch_supported uc_arch_supported
uc_open uc_open
uc_close uc_close
uc_query
uc_errno uc_errno
uc_strerror uc_strerror
uc_reg_write uc_reg_write
@ -14,5 +15,7 @@ uc_emu_stop
uc_hook_add uc_hook_add
uc_hook_del uc_hook_del
uc_mem_map uc_mem_map
uc_mem_map_ptr
uc_mem_unmap uc_mem_unmap
uc_mem_protect uc_mem_protect
uc_mem_regions

View file

@ -1,6 +1,6 @@
// //
// Dynamic loader for unicorn shared library in windows and linux. // Dynamic loader for unicorn shared library in windows and linux.
// This was made for v0.9 of unicorn. // This was made for v1.0 of unicorn.
// Newer versions of unicorn may require changes to these files. // Newer versions of unicorn may require changes to these files.
// //
// Windows Notes: // Windows Notes:
@ -62,6 +62,7 @@ typedef unsigned int (*uc_version_t)(unsigned int *major, unsigned int *minor);
typedef bool (*uc_arch_supported_t)(uc_arch arch); typedef bool (*uc_arch_supported_t)(uc_arch arch);
typedef uc_err (*uc_open_t)(uc_arch arch, uc_mode mode, uc_engine **uc); typedef uc_err (*uc_open_t)(uc_arch arch, uc_mode mode, uc_engine **uc);
typedef uc_err (*uc_close_t)(uc_engine *uc); typedef uc_err (*uc_close_t)(uc_engine *uc);
typedef uc_err (*uc_query_t)(uc_engine *uc, uc_query_type type, size_t *result);
typedef uc_err (*uc_errno_t)(uc_engine *uc); typedef uc_err (*uc_errno_t)(uc_engine *uc);
typedef const char* (*uc_strerror_t)(uc_err code); typedef const char* (*uc_strerror_t)(uc_err code);
typedef uc_err (*uc_reg_write_t)(uc_engine *uc, int regid, const void *value); typedef uc_err (*uc_reg_write_t)(uc_engine *uc, int regid, const void *value);
@ -70,17 +71,20 @@ typedef uc_err (*uc_mem_write_t)(uc_engine *uc, uint64_t address, const void *by
typedef uc_err (*uc_mem_read_t)(uc_engine *uc, uint64_t address, void *bytes, size_t size); typedef uc_err (*uc_mem_read_t)(uc_engine *uc, uint64_t address, void *bytes, size_t size);
typedef uc_err (*uc_emu_start_t)(uc_engine *uc, uint64_t begin, uint64_t until, uint64_t timeout, size_t count); typedef uc_err (*uc_emu_start_t)(uc_engine *uc, uint64_t begin, uint64_t until, uint64_t timeout, size_t count);
typedef uc_err (*uc_emu_stop_t)(uc_engine *uc); typedef uc_err (*uc_emu_stop_t)(uc_engine *uc);
typedef uc_err (*uc_hook_add_t)(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, ...); typedef uc_err (*uc_hook_add_t)(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, uint64_t begin, uint64_t end, ...);
typedef uc_err (*uc_hook_del_t)(uc_engine *uc, uc_hook hh); typedef uc_err (*uc_hook_del_t)(uc_engine *uc, uc_hook hh);
typedef uc_err (*uc_mem_map_t)(uc_engine *uc, uint64_t address, size_t size, uint32_t perms); typedef uc_err (*uc_mem_map_t)(uc_engine *uc, uint64_t address, size_t size, uint32_t perms);
typedef uc_err (*uc_mem_map_ptr_t)(uc_engine *uc, uint64_t address, size_t size, uint32_t perms, void *ptr);
typedef uc_err (*uc_mem_unmap_t)(uc_engine *uc, uint64_t address, size_t size); typedef uc_err (*uc_mem_unmap_t)(uc_engine *uc, uint64_t address, size_t size);
typedef uc_err (*uc_mem_protect_t)(uc_engine *uc, uint64_t address, size_t size, uint32_t perms); typedef uc_err (*uc_mem_protect_t)(uc_engine *uc, uint64_t address, size_t size, uint32_t perms);
typedef uc_err (*uc_mem_regions_t)(uc_engine *uc, uc_mem_region **regions, uint32_t *count);
static uc_version_t gp_uc_version = NULL; static uc_version_t gp_uc_version = NULL;
static uc_arch_supported_t gp_uc_arch_supported = NULL; static uc_arch_supported_t gp_uc_arch_supported = NULL;
static uc_open_t gp_uc_open = NULL; static uc_open_t gp_uc_open = NULL;
static uc_close_t gp_uc_close = NULL; static uc_close_t gp_uc_close = NULL;
static uc_query_t gp_uc_query = NULL;
static uc_errno_t gp_uc_errno = NULL; static uc_errno_t gp_uc_errno = NULL;
static uc_strerror_t gp_uc_strerror = NULL; static uc_strerror_t gp_uc_strerror = NULL;
static uc_reg_write_t gp_uc_reg_write = NULL; static uc_reg_write_t gp_uc_reg_write = NULL;
@ -92,8 +96,10 @@ static uc_emu_stop_t gp_uc_emu_stop = NULL;
static uc_hook_add_t gp_uc_hook_add = NULL; static uc_hook_add_t gp_uc_hook_add = NULL;
static uc_hook_del_t gp_uc_hook_del = NULL; static uc_hook_del_t gp_uc_hook_del = NULL;
static uc_mem_map_t gp_uc_mem_map = NULL; static uc_mem_map_t gp_uc_mem_map = NULL;
static uc_mem_map_ptr_t gp_uc_mem_map_ptr = NULL;
static uc_mem_unmap_t gp_uc_mem_unmap = NULL; static uc_mem_unmap_t gp_uc_mem_unmap = NULL;
static uc_mem_protect_t gp_uc_mem_protect = NULL; static uc_mem_protect_t gp_uc_mem_protect = NULL;
static uc_mem_regions_t gp_uc_mem_regions = NULL;
bool uc_dyn_load(const char* path, int flags) bool uc_dyn_load(const char* path, int flags)
@ -118,6 +124,7 @@ bool uc_dyn_load(const char* path, int flags)
gp_uc_arch_supported = (uc_arch_supported_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_arch_supported"); gp_uc_arch_supported = (uc_arch_supported_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_arch_supported");
gp_uc_open = (uc_open_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_open"); gp_uc_open = (uc_open_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_open");
gp_uc_close = (uc_close_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_close"); gp_uc_close = (uc_close_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_close");
gp_uc_query = (uc_query_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_query");
gp_uc_errno = (uc_errno_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_errno"); gp_uc_errno = (uc_errno_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_errno");
gp_uc_strerror = (uc_strerror_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_strerror"); gp_uc_strerror = (uc_strerror_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_strerror");
gp_uc_reg_write = (uc_reg_write_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_reg_write"); gp_uc_reg_write = (uc_reg_write_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_reg_write");
@ -129,8 +136,10 @@ bool uc_dyn_load(const char* path, int flags)
gp_uc_hook_add = (uc_hook_add_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_hook_add"); gp_uc_hook_add = (uc_hook_add_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_hook_add");
gp_uc_hook_del = (uc_hook_del_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_hook_del"); gp_uc_hook_del = (uc_hook_del_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_hook_del");
gp_uc_mem_map = (uc_mem_map_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_mem_map"); gp_uc_mem_map = (uc_mem_map_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_mem_map");
gp_uc_mem_map_ptr = (uc_mem_map_ptr_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_mem_map_ptr");
gp_uc_mem_unmap = (uc_mem_unmap_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_mem_unmap"); gp_uc_mem_unmap = (uc_mem_unmap_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_mem_unmap");
gp_uc_mem_protect = (uc_mem_protect_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_mem_protect"); gp_uc_mem_protect = (uc_mem_protect_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_mem_protect");
gp_uc_mem_regions = (uc_mem_regions_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_mem_regions");
return true; return true;
} }
@ -146,6 +155,7 @@ bool uc_dyn_free(void)
gp_uc_arch_supported = NULL; gp_uc_arch_supported = NULL;
gp_uc_open = NULL; gp_uc_open = NULL;
gp_uc_close = NULL; gp_uc_close = NULL;
gp_uc_query = NULL;
gp_uc_errno = NULL; gp_uc_errno = NULL;
gp_uc_strerror = NULL; gp_uc_strerror = NULL;
gp_uc_reg_write = NULL; gp_uc_reg_write = NULL;
@ -157,8 +167,10 @@ bool uc_dyn_free(void)
gp_uc_hook_add = NULL; gp_uc_hook_add = NULL;
gp_uc_hook_del = NULL; gp_uc_hook_del = NULL;
gp_uc_mem_map = NULL; gp_uc_mem_map = NULL;
gp_uc_mem_map_ptr = NULL;
gp_uc_mem_unmap = NULL; gp_uc_mem_unmap = NULL;
gp_uc_mem_protect = NULL; gp_uc_mem_protect = NULL;
gp_uc_mem_regions = NULL;
return true; return true;
} }
@ -183,6 +195,11 @@ uc_err uc_close(uc_engine *uc)
return gp_uc_close(uc); return gp_uc_close(uc);
} }
uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result)
{
return gp_uc_query(uc, type, result);
}
uc_err uc_errno(uc_engine *uc) uc_err uc_errno(uc_engine *uc)
{ {
return gp_uc_errno(uc); return gp_uc_errno(uc);
@ -223,43 +240,40 @@ uc_err uc_emu_stop(uc_engine *uc)
return gp_uc_emu_stop(uc); return gp_uc_emu_stop(uc);
} }
uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, ...) uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, uint64_t begin, uint64_t end, ...)
{ {
va_list valist; va_list valist;
uc_err ret = UC_ERR_OK; uc_err ret = UC_ERR_OK;
int id; int id;
uint64_t begin, end;
va_start(valist, user_data); va_start(valist, user_data);
switch(type) { switch(type) {
// note this default case will capture any combinations of // note this default case will capture any combinations of
// UC_HOOK_MEM_*_PROT and UC_HOOK_MEM_*_UNMAPPED // UC_HOOK_MEM_*_PROT and UC_HOOK_MEM_*_UNMAPPED
// as well as any combination of
// UC_HOOK_MEM_READ, UC_HOOK_MEM_WRITE and UC_HOOK_MEM_FETCH
default: default:
case UC_HOOK_INTR: case UC_HOOK_INTR:
case UC_HOOK_CODE:
case UC_HOOK_BLOCK:
// all combinations of UC_HOOK_MEM_*_PROT and UC_HOOK_MEM_*_UNMAPPED are caught by 'default'
case UC_HOOK_MEM_READ_UNMAPPED: case UC_HOOK_MEM_READ_UNMAPPED:
case UC_HOOK_MEM_WRITE_UNMAPPED: case UC_HOOK_MEM_WRITE_UNMAPPED:
case UC_HOOK_MEM_FETCH_UNMAPPED: case UC_HOOK_MEM_FETCH_UNMAPPED:
case UC_HOOK_MEM_READ_PROT: case UC_HOOK_MEM_READ_PROT:
case UC_HOOK_MEM_WRITE_PROT: case UC_HOOK_MEM_WRITE_PROT:
case UC_HOOK_MEM_FETCH_PROT: case UC_HOOK_MEM_FETCH_PROT:
// all combinations of read/write/fetch are caught by 'default'
case UC_HOOK_MEM_READ:
case UC_HOOK_MEM_WRITE:
case UC_HOOK_MEM_FETCH: case UC_HOOK_MEM_FETCH:
// 0 extra args // 0 extra args
ret = gp_uc_hook_add(uc, hh, type, callback, user_data); ret = gp_uc_hook_add(uc, hh, type, callback, user_data, begin, end);
break; break;
case UC_HOOK_INSN: case UC_HOOK_INSN:
// 1 extra arg // 1 extra arg
id = va_arg(valist, int); id = va_arg(valist, int);
ret = gp_uc_hook_add(uc, hh, type, callback, user_data, id); ret = gp_uc_hook_add(uc, hh, type, callback, user_data, begin, end, id);
break;
case UC_HOOK_CODE:
case UC_HOOK_BLOCK:
case UC_HOOK_MEM_READ:
case UC_HOOK_MEM_WRITE:
case UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE:
// 2 extra args
begin = va_arg(valist, uint64_t);
end = va_arg(valist, uint64_t);
ret = gp_uc_hook_add(uc, hh, type, callback, user_data, begin, end);
break; break;
} }
@ -277,6 +291,11 @@ uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms)
return gp_uc_mem_map(uc, address, size, perms); return gp_uc_mem_map(uc, address, size, perms);
} }
uc_err uc_mem_map_ptr(uc_engine *uc, uint64_t address, size_t size, uint32_t perms, void *ptr)
{
return gp_uc_mem_map_ptr(uc, address, size, perms, ptr);
}
uc_err uc_mem_unmap(uc_engine *uc, uint64_t address, size_t size) uc_err uc_mem_unmap(uc_engine *uc, uint64_t address, size_t size)
{ {
return gp_uc_mem_unmap(uc, address, size); return gp_uc_mem_unmap(uc, address, size);
@ -287,4 +306,9 @@ uc_err uc_mem_protect(uc_engine *uc, uint64_t address, size_t size, uint32_t per
return gp_uc_mem_protect(uc, address, size, perms); return gp_uc_mem_protect(uc, address, size, perms);
} }
uc_err uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count)
{
return gp_uc_mem_regions(uc, regions, count);
}
#endif // DYNLOAD #endif // DYNLOAD

View file

@ -1,6 +1,6 @@
// //
// Dynamic loader for unicorn shared library in windows and linux. // Dynamic loader for unicorn shared library in windows and linux.
// This was made for v0.9 of unicorn. // This was made for v1.0 of unicorn.
// Newer versions of unicorn may require changes to these files. // Newer versions of unicorn may require changes to these files.
// //
// Windows Notes: // Windows Notes:

View file

@ -24,7 +24,7 @@ PKG_NAME = 'unicorn'
if os.path.exists(PATH_LIB64) and os.path.exists(PATH_LIB32): if os.path.exists(PATH_LIB64) and os.path.exists(PATH_LIB32):
PKG_NAME = 'unicorn-windows' PKG_NAME = 'unicorn-windows'
VERSION = '0.9' VERSION = '1.0'
SYSTEM = sys.platform SYSTEM = sys.platform
# virtualenv breaks import, but get_python_lib() will work. # virtualenv breaks import, but get_python_lib() will work.
@ -63,7 +63,7 @@ def copy_sources():
src.extend(glob.glob("../../Makefile")) src.extend(glob.glob("../../Makefile"))
src.extend(glob.glob("../../LICENSE*")) src.extend(glob.glob("../../LICENSE*"))
src.extend(glob.glob("../../README")) src.extend(glob.glob("../../README.md"))
src.extend(glob.glob("../../*.TXT")) src.extend(glob.glob("../../*.TXT"))
src.extend(glob.glob("../../RELEASE_NOTES")) src.extend(glob.glob("../../RELEASE_NOTES"))
src.extend(glob.glob("../../make.sh")) src.extend(glob.glob("../../make.sh"))

View file

@ -1,4 +1,4 @@
# Unicorn Python bindings, by Nguyen Anh Quynnh <aquynh@gmail.com> # Unicorn Python bindings, by Nguyen Anh Quynnh <aquynh@gmail.com>
from . import arm_const, arm64_const, mips_const, sparc_const, m68k_const, x86_const from . import arm_const, arm64_const, mips_const, sparc_const, m68k_const, x86_const
from .unicorn_const import * from .unicorn_const import *
from .unicorn import Uc, uc_version, uc_arch_supported, version_bind, debug, UcError from .unicorn import Uc, uc_version, uc_arch_supported, version_bind, debug, UcError, __version__

View file

@ -1,42 +1,55 @@
# Unicorn Python bindings, by Nguyen Anh Quynnh <aquynh@gmail.com> # Unicorn Python bindings, by Nguyen Anh Quynnh <aquynh@gmail.com>
import ctypes
import ctypes.util
import distutils.sysconfig
import inspect
import os.path
import platform
import sys import sys
from . import x86_const, unicorn_const as uc
if not hasattr(sys.modules[__name__], "__file__"):
__file__ = inspect.getfile(inspect.currentframe())
_python2 = sys.version_info[0] < 3 _python2 = sys.version_info[0] < 3
if _python2: if _python2:
range = xrange range = xrange
from . import arm_const, arm64_const, mips_const, sparc_const, m68k_const, x86_const
from .unicorn_const import *
import ctypes, ctypes.util, sys _lib_path = os.path.split(__file__)[0]
from platform import system _all_libs = (
from os.path import split, join, dirname, exists "unicorn.dll",
import distutils.sysconfig "libunicorn.so",
"libunicorn.dylib",
)
import inspect
if not hasattr(sys.modules[__name__], '__file__'):
__file__ = inspect.getfile(inspect.currentframe())
_lib_path = split(__file__)[0]
_all_libs = ('unicorn.dll', 'libunicorn.so', 'libunicorn.dylib')
# Windows DLL in dependency order # Windows DLL in dependency order
_all_windows_dlls = ("libwinpthread-1.dll", "libgcc_s_seh-1.dll", "libgcc_s_dw2-1.dll", "libiconv-2.dll", "libintl-8.dll", "libglib-2.0-0.dll") _all_windows_dlls = (
"libwinpthread-1.dll",
"libgcc_s_seh-1.dll",
"libgcc_s_dw2-1.dll",
"libiconv-2.dll",
"libintl-8.dll",
"libglib-2.0-0.dll",
)
_found = False _found = False
for _lib in _all_libs: for _lib in _all_libs:
try: try:
if _lib == 'unicorn.dll': if _lib == "unicorn.dll":
for dll in _all_windows_dlls: # load all the rest DLLs first for dll in _all_windows_dlls: # load all the rest DLLs first
_lib_file = join(_lib_path, dll) _lib_file = os.path.join(_lib_path, dll)
if exists(_lib_file): if os.path.exists(_lib_file):
ctypes.cdll.LoadLibrary(_lib_file) ctypes.cdll.LoadLibrary(_lib_file)
_lib_file = join(_lib_path, _lib) _lib_file = os.path.join(_lib_path, _lib)
_uc = ctypes.cdll.LoadLibrary(_lib_file) _uc = ctypes.cdll.LoadLibrary(_lib_file)
_found = True _found = True
break break
except OSError: except OSError:
pass pass
if _found == False: if not _found:
# try loading from default paths # try loading from default paths
for _lib in _all_libs: for _lib in _all_libs:
try: try:
@ -46,17 +59,17 @@ if _found == False:
except OSError: except OSError:
pass pass
if _found == False: if not _found:
# last try: loading from python lib directory # last try: loading from python lib directory
_lib_path = distutils.sysconfig.get_python_lib() _lib_path = distutils.sysconfig.get_python_lib()
for _lib in _all_libs: for _lib in _all_libs:
try: try:
if _lib == 'unicorn.dll': if _lib == "unicorn.dll":
for dll in _all_windows_dlls: # load all the rest DLLs first for dll in _all_windows_dlls: # load all the rest DLLs first
_lib_file = join(_lib_path, 'unicorn', dll) _lib_file = os.path.join(_lib_path, "unicorn", dll)
if exists(_lib_file): if os.path.exists(_lib_file):
ctypes.cdll.LoadLibrary(_lib_file) ctypes.cdll.LoadLibrary(_lib_file)
_lib_file = join(_lib_path, 'unicorn', _lib) _lib_file = os.path.join(_lib_path, "unicorn", _lib)
_uc = ctypes.cdll.LoadLibrary(_lib_file) _uc = ctypes.cdll.LoadLibrary(_lib_file)
_found = True _found = True
break break
@ -65,11 +78,11 @@ if _found == False:
# Attempt Darwin specific load (10.11 specific), # Attempt Darwin specific load (10.11 specific),
# since LD_LIBRARY_PATH is not guaranteed to exist # since LD_LIBRARY_PATH is not guaranteed to exist
if (_found == False) and (system() == 'Darwin'): if not _found and platform.system() == "Darwin":
_lib_path = '/usr/local/lib/' _lib_path = "/usr/local/lib/"
for _lib in _all_libs: for _lib in _all_libs:
try: try:
_lib_file = join(_lib_path, _lib) _lib_file = os.path.join(_lib_path, _lib)
# print "Trying to load:", _lib_file # print "Trying to load:", _lib_file
_uc = ctypes.cdll.LoadLibrary(_lib_file) _uc = ctypes.cdll.LoadLibrary(_lib_file)
_found = True _found = True
@ -77,10 +90,12 @@ if (_found == False) and (system() == 'Darwin'):
except OSError: except OSError:
pass pass
if _found == False: if not _found:
raise ImportError("ERROR: fail to load the dynamic library.") raise ImportError("ERROR: fail to load the dynamic library.")
__version__ = "%s.%s" % (uc.UC_API_MAJOR, uc.UC_API_MINOR)
# setup all the function prototype # setup all the function prototype
def _setup_prototype(lib, fname, restype, *argtypes): def _setup_prototype(lib, fname, restype, *argtypes):
getattr(lib, fname).restype = restype getattr(lib, fname).restype = restype
@ -110,20 +125,28 @@ _setup_prototype(_uc, "uc_mem_protect", ucerr, uc_engine, ctypes.c_uint64, ctype
_setup_prototype(_uc, "uc_query", ucerr, uc_engine, ctypes.c_uint32, ctypes.POINTER(ctypes.c_size_t)) _setup_prototype(_uc, "uc_query", ucerr, uc_engine, ctypes.c_uint32, ctypes.POINTER(ctypes.c_size_t))
# uc_hook_add is special due to variable number of arguments # uc_hook_add is special due to variable number of arguments
_uc.uc_hook_add = getattr(_uc, "uc_hook_add") _uc.uc_hook_add = _uc.uc_hook_add
_uc.uc_hook_add.restype = ucerr _uc.uc_hook_add.restype = ucerr
UC_HOOK_CODE_CB = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_void_p) UC_HOOK_CODE_CB = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_void_p)
UC_HOOK_MEM_INVALID_CB = ctypes.CFUNCTYPE(ctypes.c_bool, uc_engine, ctypes.c_int, \ UC_HOOK_MEM_INVALID_CB = ctypes.CFUNCTYPE(
ctypes.c_uint64, ctypes.c_int, ctypes.c_int64, ctypes.c_void_p) ctypes.c_bool, uc_engine, ctypes.c_int,
UC_HOOK_MEM_ACCESS_CB = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_int, \ ctypes.c_uint64, ctypes.c_int, ctypes.c_int64, ctypes.c_void_p
ctypes.c_uint64, ctypes.c_int, ctypes.c_int64, ctypes.c_void_p) )
UC_HOOK_INTR_CB = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_uint32, \ UC_HOOK_MEM_ACCESS_CB = ctypes.CFUNCTYPE(
ctypes.c_void_p) None, uc_engine, ctypes.c_int,
UC_HOOK_INSN_IN_CB = ctypes.CFUNCTYPE(ctypes.c_uint32, uc_engine, ctypes.c_uint32, \ ctypes.c_uint64, ctypes.c_int, ctypes.c_int64, ctypes.c_void_p
ctypes.c_int, ctypes.c_void_p) )
UC_HOOK_INSN_OUT_CB = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_uint32, \ UC_HOOK_INTR_CB = ctypes.CFUNCTYPE(
ctypes.c_int, ctypes.c_uint32, ctypes.c_void_p) None, uc_engine, ctypes.c_uint32, ctypes.c_void_p
)
UC_HOOK_INSN_IN_CB = ctypes.CFUNCTYPE(
ctypes.c_uint32, uc_engine, ctypes.c_uint32, ctypes.c_int, ctypes.c_void_p
)
UC_HOOK_INSN_OUT_CB = ctypes.CFUNCTYPE(
None, uc_engine, ctypes.c_uint32,
ctypes.c_int, ctypes.c_uint32, ctypes.c_void_p
)
UC_HOOK_INSN_SYSCALL_CB = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_void_p) UC_HOOK_INSN_SYSCALL_CB = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_void_p)
@ -146,7 +169,10 @@ def uc_version():
# return the binding's version # return the binding's version
def version_bind(): def version_bind():
return (UC_API_MAJOR, UC_API_MINOR, (UC_API_MAJOR << 8) + UC_API_MINOR) return (
uc.UC_API_MAJOR, uc.UC_API_MINOR,
(uc.UC_API_MAJOR << 8) + uc.UC_API_MINOR,
)
# check to see if this engine supports a particular arch # check to see if this engine supports a particular arch
@ -154,19 +180,37 @@ def uc_arch_supported(query):
return _uc.uc_arch_supported(query) return _uc.uc_arch_supported(query)
class uc_x86_mmr(ctypes.Structure):
"""Memory-Management Register for instructions IDTR, GDTR, LDTR, TR."""
_fields_ = [
("selector", ctypes.c_uint16), # not used by GDTR and IDTR
("base", ctypes.c_uint64), # handle 32 or 64 bit CPUs
("limit", ctypes.c_uint32),
("flags", ctypes.c_uint32), # not used by GDTR and IDTR
]
class uc_x86_float80(ctypes.Structure):
"""Float80"""
_fields_ = [
("mantissa", ctypes.c_uint64),
("exponent", ctypes.c_uint16),
]
class Uc(object): class Uc(object):
def __init__(self, arch, mode): def __init__(self, arch, mode):
# verify version compatibility with the core before doing anything # verify version compatibility with the core before doing anything
(major, minor, _combined) = uc_version() (major, minor, _combined) = uc_version()
if major != UC_API_MAJOR or minor != UC_API_MINOR: if major != uc.UC_API_MAJOR or minor != uc.UC_API_MINOR:
self._uch = None self._uch = None
# our binding version is different from the core's API version # our binding version is different from the core's API version
raise UcError(UC_ERR_VERSION) raise UcError(uc.UC_ERR_VERSION)
self._arch, self._mode = arch, mode self._arch, self._mode = arch, mode
self._uch = ctypes.c_void_p() self._uch = ctypes.c_void_p()
status = _uc.uc_open(arch, mode, ctypes.byref(self._uch)) status = _uc.uc_open(arch, mode, ctypes.byref(self._uch))
if status != UC_ERR_OK: if status != uc.UC_ERR_OK:
self._uch = None self._uch = None
raise UcError(status) raise UcError(status)
# internal mapping table to save callback & userdata # internal mapping table to save callback & userdata
@ -174,146 +218,158 @@ class Uc(object):
self._ctype_cbs = {} self._ctype_cbs = {}
self._callback_count = 0 self._callback_count = 0
# destructor to be called automatically when object is destroyed. # destructor to be called automatically when object is destroyed.
def __del__(self): def __del__(self):
if self._uch: if self._uch:
try: try:
status = _uc.uc_close(self._uch) status = _uc.uc_close(self._uch)
self._uch = None self._uch = None
if status != UC_ERR_OK: if status != uc.UC_ERR_OK:
raise UcError(status) raise UcError(status)
except: # _uc might be pulled from under our feet except: # _uc might be pulled from under our feet
pass pass
# emulate from @begin, and stop when reaching address @until # emulate from @begin, and stop when reaching address @until
def emu_start(self, begin, until, timeout=0, count=0): def emu_start(self, begin, until, timeout=0, count=0):
status = _uc.uc_emu_start(self._uch, begin, until, timeout, count) status = _uc.uc_emu_start(self._uch, begin, until, timeout, count)
if status != UC_ERR_OK: if status != uc.UC_ERR_OK:
raise UcError(status) raise UcError(status)
# stop emulation # stop emulation
def emu_stop(self): def emu_stop(self):
status = _uc.uc_emu_stop(self._uch) status = _uc.uc_emu_stop(self._uch)
if status != UC_ERR_OK: if status != uc.UC_ERR_OK:
raise UcError(status) raise UcError(status)
# return the value of a register # return the value of a register
def reg_read(self, reg_id): def reg_read(self, reg_id):
if self._arch == uc.UC_ARCH_X86:
if reg_id in [x86_const.UC_X86_REG_IDTR, x86_const.UC_X86_REG_GDTR, x86_const.UC_X86_REG_LDTR, x86_const.UC_X86_REG_TR]:
reg = uc_x86_mmr()
status = _uc.uc_reg_read(self._uch, reg_id, ctypes.byref(reg))
if status != uc.UC_ERR_OK:
raise UcError(status)
return reg.selector, reg.base, reg.limit, reg.flags
if reg_id in range(x86_const.UC_X86_REG_FP0, x86_const.UC_X86_REG_FP0+8):
reg = uc_x86_float80()
status = _uc.uc_reg_read(self._uch, reg_id, ctypes.byref(reg))
if status != uc.UC_ERR_OK:
raise UcError(status)
return reg.mantissa, reg.exponent
# read to 64bit number to be safe # read to 64bit number to be safe
reg = ctypes.c_int64(0) reg = ctypes.c_int64(0)
status = _uc.uc_reg_read(self._uch, reg_id, ctypes.byref(reg)) status = _uc.uc_reg_read(self._uch, reg_id, ctypes.byref(reg))
if status != UC_ERR_OK: if status != uc.UC_ERR_OK:
raise UcError(status) raise UcError(status)
return reg.value return reg.value
# write to a register # write to a register
def reg_write(self, reg_id, value): def reg_write(self, reg_id, value):
reg = None
if self._arch == uc.UC_ARCH_X86:
if reg_id in [x86_const.UC_X86_REG_IDTR, x86_const.UC_X86_REG_GDTR, x86_const.UC_X86_REG_LDTR, x86_const.UC_X86_REG_TR]:
assert isinstance(value, tuple) and len(value) == 4
reg = uc_x86_mmr()
reg.selector = value[0]
reg.base = value[1]
reg.limit = value[2]
reg.flags = value[3]
if reg_id in range(x86_const.UC_X86_REG_FP0, x86_const.UC_X86_REG_FP0+8):
reg = uc_x86_float80()
reg.mantissa = value[0]
reg.exponent = value[1]
if reg is None:
# convert to 64bit number to be safe # convert to 64bit number to be safe
reg = ctypes.c_int64(value) reg = ctypes.c_int64(value)
status = _uc.uc_reg_write(self._uch, reg_id, ctypes.byref(reg))
if status != UC_ERR_OK:
raise UcError(status)
status = _uc.uc_reg_write(self._uch, reg_id, ctypes.byref(reg))
if status != uc.UC_ERR_OK:
raise UcError(status)
# read data from memory # read data from memory
def mem_read(self, address, size): def mem_read(self, address, size):
data = ctypes.create_string_buffer(size) data = ctypes.create_string_buffer(size)
status = _uc.uc_mem_read(self._uch, address, data, size) status = _uc.uc_mem_read(self._uch, address, data, size)
if status != UC_ERR_OK: if status != uc.UC_ERR_OK:
raise UcError(status) raise UcError(status)
return bytearray(data) return bytearray(data)
# write to memory # write to memory
def mem_write(self, address, data): def mem_write(self, address, data):
status = _uc.uc_mem_write(self._uch, address, data, len(data)) status = _uc.uc_mem_write(self._uch, address, data, len(data))
if status != UC_ERR_OK: if status != uc.UC_ERR_OK:
raise UcError(status) raise UcError(status)
# map a range of memory # map a range of memory
def mem_map(self, address, size, perms=UC_PROT_ALL): def mem_map(self, address, size, perms=uc.UC_PROT_ALL):
status = _uc.uc_mem_map(self._uch, address, size, perms) status = _uc.uc_mem_map(self._uch, address, size, perms)
if status != UC_ERR_OK: if status != uc.UC_ERR_OK:
raise UcError(status) raise UcError(status)
# map a range of memory from a raw host memory address # map a range of memory from a raw host memory address
def mem_map_ptr(self, address, size, perms, ptr): def mem_map_ptr(self, address, size, perms, ptr):
status = _uc.uc_mem_map_ptr(self._uch, address, size, perms, ptr) status = _uc.uc_mem_map_ptr(self._uch, address, size, perms, ptr)
if status != UC_ERR_OK: if status != uc.UC_ERR_OK:
raise UcError(status) raise UcError(status)
# unmap a range of memory # unmap a range of memory
def mem_unmap(self, address, size): def mem_unmap(self, address, size):
status = _uc.uc_mem_unmap(self._uch, address, size) status = _uc.uc_mem_unmap(self._uch, address, size)
if status != UC_ERR_OK: if status != uc.UC_ERR_OK:
raise UcError(status) raise UcError(status)
# protect a range of memory # protect a range of memory
def mem_protect(self, address, size, perms=UC_PROT_ALL): def mem_protect(self, address, size, perms=uc.UC_PROT_ALL):
status = _uc.uc_mem_protect(self._uch, address, size, perms) status = _uc.uc_mem_protect(self._uch, address, size, perms)
if status != UC_ERR_OK: if status != uc.UC_ERR_OK:
raise UcError(status) raise UcError(status)
# return CPU mode at runtime # return CPU mode at runtime
def query(self, query_mode): def query(self, query_mode):
result = ctypes.c_size_t(0) result = ctypes.c_size_t(0)
status = _uc.uc_query(self._uch, query_mode, ctypes.byref(result)) status = _uc.uc_query(self._uch, query_mode, ctypes.byref(result))
if status != UC_ERR_OK: if status != uc.UC_ERR_OK:
raise UcError(status) raise UcError(status)
return result.value return result.value
def _hookcode_cb(self, handle, address, size, user_data): def _hookcode_cb(self, handle, address, size, user_data):
# call user's callback with self object # call user's callback with self object
(cb, data) = self._callbacks[user_data] (cb, data) = self._callbacks[user_data]
cb(self, address, size, data) cb(self, address, size, data)
def _hook_mem_invalid_cb(self, handle, access, address, size, value, user_data): def _hook_mem_invalid_cb(self, handle, access, address, size, value, user_data):
# call user's callback with self object # call user's callback with self object
(cb, data) = self._callbacks[user_data] (cb, data) = self._callbacks[user_data]
return cb(self, access, address, size, value, data) return cb(self, access, address, size, value, data)
def _hook_mem_access_cb(self, handle, access, address, size, value, user_data): def _hook_mem_access_cb(self, handle, access, address, size, value, user_data):
# call user's callback with self object # call user's callback with self object
(cb, data) = self._callbacks[user_data] (cb, data) = self._callbacks[user_data]
cb(self, access, address, size, value, data) cb(self, access, address, size, value, data)
def _hook_intr_cb(self, handle, intno, user_data): def _hook_intr_cb(self, handle, intno, user_data):
# call user's callback with self object # call user's callback with self object
(cb, data) = self._callbacks[user_data] (cb, data) = self._callbacks[user_data]
cb(self, intno, data) cb(self, intno, data)
def _hook_insn_in_cb(self, handle, port, size, user_data): def _hook_insn_in_cb(self, handle, port, size, user_data):
# call user's callback with self object # call user's callback with self object
(cb, data) = self._callbacks[user_data] (cb, data) = self._callbacks[user_data]
return cb(self, port, size, data) return cb(self, port, size, data)
def _hook_insn_out_cb(self, handle, port, size, value, user_data): def _hook_insn_out_cb(self, handle, port, size, value, user_data):
# call user's callback with self object # call user's callback with self object
(cb, data) = self._callbacks[user_data] (cb, data) = self._callbacks[user_data]
cb(self, port, size, value, data) cb(self, port, size, value, data)
def _hook_insn_syscall_cb(self, handle, user_data): def _hook_insn_syscall_cb(self, handle, user_data):
# call user's callback with self object # call user's callback with self object
(cb, data) = self._callbacks[user_data] (cb, data) = self._callbacks[user_data]
cb(self, data) cb(self, data)
# add a hook # add a hook
def hook_add(self, htype, callback, user_data=None, begin=1, end=0, arg1=0): def hook_add(self, htype, callback, user_data=None, begin=1, end=0, arg1=0):
_h2 = uc_hook_h() _h2 = uc_hook_h()
@ -323,7 +379,7 @@ class Uc(object):
self._callbacks[self._callback_count] = (callback, user_data) self._callbacks[self._callback_count] = (callback, user_data)
cb = None cb = None
if htype == UC_HOOK_INSN: if htype == uc.UC_HOOK_INSN:
insn = ctypes.c_int(arg1) insn = ctypes.c_int(arg1)
if arg1 == x86_const.UC_X86_INS_IN: # IN instruction if arg1 == x86_const.UC_X86_INS_IN: # IN instruction
cb = ctypes.cast(UC_HOOK_INSN_IN_CB(self._hook_insn_in_cb), UC_HOOK_INSN_IN_CB) cb = ctypes.cast(UC_HOOK_INSN_IN_CB(self._hook_insn_in_cb), UC_HOOK_INSN_IN_CB)
@ -331,53 +387,75 @@ class Uc(object):
cb = ctypes.cast(UC_HOOK_INSN_OUT_CB(self._hook_insn_out_cb), UC_HOOK_INSN_OUT_CB) cb = ctypes.cast(UC_HOOK_INSN_OUT_CB(self._hook_insn_out_cb), UC_HOOK_INSN_OUT_CB)
if arg1 in (x86_const.UC_X86_INS_SYSCALL, x86_const.UC_X86_INS_SYSENTER): # SYSCALL/SYSENTER instruction if arg1 in (x86_const.UC_X86_INS_SYSCALL, x86_const.UC_X86_INS_SYSENTER): # SYSCALL/SYSENTER instruction
cb = ctypes.cast(UC_HOOK_INSN_SYSCALL_CB(self._hook_insn_syscall_cb), UC_HOOK_INSN_SYSCALL_CB) cb = ctypes.cast(UC_HOOK_INSN_SYSCALL_CB(self._hook_insn_syscall_cb), UC_HOOK_INSN_SYSCALL_CB)
status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \ status = _uc.uc_hook_add(
cb, ctypes.cast(self._callback_count, ctypes.c_void_p), ctypes.c_uint64(begin), ctypes.c_uint64(end), insn) self._uch, ctypes.byref(_h2), htype, cb,
elif htype == UC_HOOK_INTR: ctypes.cast(self._callback_count, ctypes.c_void_p),
ctypes.c_uint64(begin), ctypes.c_uint64(end), insn
)
elif htype == uc.UC_HOOK_INTR:
cb = ctypes.cast(UC_HOOK_INTR_CB(self._hook_intr_cb), UC_HOOK_INTR_CB) cb = ctypes.cast(UC_HOOK_INTR_CB(self._hook_intr_cb), UC_HOOK_INTR_CB)
status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \ status = _uc.uc_hook_add(
cb, ctypes.cast(self._callback_count, ctypes.c_void_p), ctypes.c_uint64(begin), ctypes.c_uint64(end)) self._uch, ctypes.byref(_h2), htype, cb,
ctypes.cast(self._callback_count, ctypes.c_void_p),
ctypes.c_uint64(begin), ctypes.c_uint64(end)
)
else: else:
if htype in (UC_HOOK_BLOCK, UC_HOOK_CODE): if htype in (uc.UC_HOOK_BLOCK, uc.UC_HOOK_CODE):
# set callback with wrapper, so it can be called # set callback with wrapper, so it can be called
# with this object as param # with this object as param
cb = ctypes.cast(UC_HOOK_CODE_CB(self._hookcode_cb), UC_HOOK_CODE_CB) cb = ctypes.cast(UC_HOOK_CODE_CB(self._hookcode_cb), UC_HOOK_CODE_CB)
status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, cb, \ status = _uc.uc_hook_add(
ctypes.cast(self._callback_count, ctypes.c_void_p), ctypes.c_uint64(begin), ctypes.c_uint64(end)) self._uch, ctypes.byref(_h2), htype, cb,
elif htype & UC_HOOK_MEM_READ_UNMAPPED or htype & UC_HOOK_MEM_WRITE_UNMAPPED or \ ctypes.cast(self._callback_count, ctypes.c_void_p),
htype & UC_HOOK_MEM_FETCH_UNMAPPED or htype & UC_HOOK_MEM_READ_PROT or \ ctypes.c_uint64(begin), ctypes.c_uint64(end)
htype & UC_HOOK_MEM_WRITE_PROT or htype & UC_HOOK_MEM_FETCH_PROT: )
elif htype & (uc.UC_HOOK_MEM_READ_UNMAPPED |
uc.UC_HOOK_MEM_WRITE_UNMAPPED |
uc.UC_HOOK_MEM_FETCH_UNMAPPED |
uc.UC_HOOK_MEM_READ_PROT |
uc.UC_HOOK_MEM_WRITE_PROT |
uc.UC_HOOK_MEM_FETCH_PROT):
cb = ctypes.cast(UC_HOOK_MEM_INVALID_CB(self._hook_mem_invalid_cb), UC_HOOK_MEM_INVALID_CB) cb = ctypes.cast(UC_HOOK_MEM_INVALID_CB(self._hook_mem_invalid_cb), UC_HOOK_MEM_INVALID_CB)
status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \ status = _uc.uc_hook_add(
cb, ctypes.cast(self._callback_count, ctypes.c_void_p), ctypes.c_uint64(begin), ctypes.c_uint64(end)) self._uch, ctypes.byref(_h2), htype, cb,
ctypes.cast(self._callback_count, ctypes.c_void_p),
ctypes.c_uint64(begin), ctypes.c_uint64(end)
)
else: else:
cb = ctypes.cast(UC_HOOK_MEM_ACCESS_CB(self._hook_mem_access_cb), UC_HOOK_MEM_ACCESS_CB) cb = ctypes.cast(UC_HOOK_MEM_ACCESS_CB(self._hook_mem_access_cb), UC_HOOK_MEM_ACCESS_CB)
status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \ status = _uc.uc_hook_add(
cb, ctypes.cast(self._callback_count, ctypes.c_void_p), ctypes.c_uint64(begin), ctypes.c_uint64(end)) self._uch, ctypes.byref(_h2), htype, cb,
ctypes.cast(self._callback_count, ctypes.c_void_p),
ctypes.c_uint64(begin), ctypes.c_uint64(end)
)
# save the ctype function so gc will leave it alone. # save the ctype function so gc will leave it alone.
self._ctype_cbs[self._callback_count] = cb self._ctype_cbs[self._callback_count] = cb
if status != UC_ERR_OK: if status != uc.UC_ERR_OK:
raise UcError(status) raise UcError(status)
return _h2.value return _h2.value
# delete a hook # delete a hook
def hook_del(self, h): def hook_del(self, h):
_h = uc_hook_h(h) _h = uc_hook_h(h)
status = _uc.uc_hook_del(self._uch, _h) status = _uc.uc_hook_del(self._uch, _h)
if status != UC_ERR_OK: if status != uc.UC_ERR_OK:
raise UcError(status) raise UcError(status)
h = 0 h = 0
# print out debugging info # print out debugging info
def debug(): def debug():
archs = { "arm": UC_ARCH_ARM, "arm64": UC_ARCH_ARM64, \ archs = {
"mips": UC_ARCH_MIPS, "sparc": UC_ARCH_SPARC, \ "arm": uc.UC_ARCH_ARM,
"m68k": UC_ARCH_M68K, "x86": UC_ARCH_X86 } "arm64": uc.UC_ARCH_ARM64,
"mips": uc.UC_ARCH_MIPS,
"sparc": uc.UC_ARCH_SPARC,
"m68k": uc.UC_ARCH_M68K,
"x86": uc.UC_ARCH_X86,
}
all_archs = "" all_archs = ""
keys = archs.keys() keys = archs.keys()
@ -385,7 +463,8 @@ def debug():
if uc_arch_supported(archs[k]): if uc_arch_supported(archs[k]):
all_archs += "-%s" % k all_archs += "-%s" % k
(major, minor, _combined) = uc_version() major, minor, _combined = uc_version()
return "python-%s-c%u.%u-b%u.%u" % (all_archs, major, minor, UC_API_MAJOR, UC_API_MINOR)
return "python-%s-c%u.%u-b%u.%u" % (
all_archs, major, minor, uc.UC_API_MAJOR, uc.UC_API_MINOR
)

View file

@ -86,6 +86,7 @@ UC_HOOK_MEM_FETCH_INVALID = 576
UC_HOOK_MEM_INVALID = 1008 UC_HOOK_MEM_INVALID = 1008
UC_HOOK_MEM_VALID = 7168 UC_HOOK_MEM_VALID = 7168
UC_QUERY_MODE = 1 UC_QUERY_MODE = 1
UC_QUERY_PAGE_SIZE = 2
UC_PROT_NONE = 0 UC_PROT_NONE = 0
UC_PROT_READ = 1 UC_PROT_READ = 1

View file

@ -248,7 +248,9 @@ UC_X86_REG_IDTR = 242
UC_X86_REG_GDTR = 243 UC_X86_REG_GDTR = 243
UC_X86_REG_LDTR = 244 UC_X86_REG_LDTR = 244
UC_X86_REG_TR = 245 UC_X86_REG_TR = 245
UC_X86_REG_ENDING = 246 UC_X86_REG_FPCW = 246
UC_X86_REG_FPTAG = 247
UC_X86_REG_ENDING = 248
# X86 instructions # X86 instructions

11
bindings/ruby/Makefile Normal file
View file

@ -0,0 +1,11 @@
# Ruby binding for Unicorn engine. Sascha Schirra <sashs@scoding.de>
.PHONY: gen_const
install:
$(MAKE) gen_const
cd unicorn_gem && rake build
cd unicorn_gem && gem install --local pkg/unicorn-0.9.0.gem
gen_const:
cd .. && python const_generator.py ruby

24
bindings/ruby/README.md Normal file
View file

@ -0,0 +1,24 @@
# Installation
## Software requirements
### Linux
- ruby >= 1.9.3
- rubygems
- make
- gcc
### Mac OS
- ruby >= 1.9.3
- rubygems
- make
- XCode
## Install unicorn
* cd path_to_unicorn
* ./make.sh install
## Install ruby binding
* cd bindings/ruby
* make install

106
bindings/ruby/sample_arm.rb Normal file
View file

@ -0,0 +1,106 @@
#!/usr/bin/env ruby
require 'unicorn'
require 'unicorn/arm_const'
include Unicorn
# code to be emulated
ARM_CODE = "\x37\x00\xa0\xe3\x03\x10\x42\xe0" # mov r0, #0x37; sub r1, r2, r3
THUMB_CODE = "\x83\xb0" # sub sp, #0xc
# memory address where emulation starts
ADDRESS = 0x10000
# callback for tracing basic blocks
$hook_block = Proc.new do |uc, address, size, user_data|
puts(">>> Tracing basic block at 0x%x, block size = 0x%x" % [address, size])
end
# callback for tracing instructions
$hook_code = Proc.new do |uc, address, size, user_data|
puts(">>> Tracing instruction at 0x%x, instruction size = %u" % [address, size])
end
# Test ARM
def test_arm()
puts("Emulate ARM code")
begin
# Initialize emulator in ARM mode
mu = Uc.new UC_ARCH_ARM, UC_MODE_ARM
# 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, ARM_CODE)
# initialize machine registers
mu.reg_write(UC_ARM_REG_R0, 0x1234)
mu.reg_write(UC_ARM_REG_R2, 0x6789)
mu.reg_write(UC_ARM_REG_R3, 0x3333)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, $hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, $hook_code)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + ARM_CODE.bytesize)
# now print out some registers
puts(">>> Emulation done. Below is the CPU context")
r0 = mu.reg_read(UC_ARM_REG_R0)
r1 = mu.reg_read(UC_ARM_REG_R1)
puts(">>> R0 = 0x%x" % r0)
puts(">>> R1 = 0x%x" % r1)
rescue UcError => e
puts("ERROR: %s" % e)
end
end
def test_thumb()
puts("Emulate THUMB code")
begin
# Initialize emulator in thumb mode
mu = Uc.new UC_ARCH_ARM, UC_MODE_THUMB
# 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, THUMB_CODE)
# initialize machine registers
mu.reg_write(UC_ARM_REG_SP, 0x1234)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, $hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, $hook_code)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + THUMB_CODE.bytesize)
# now print out some registers
puts(">>> Emulation done. Below is the CPU context")
sp = mu.reg_read(UC_ARM_REG_SP)
puts(">>> SP = 0x%x" % sp)
rescue UcError => e
puts("ERROR: %s" % e)
end
end
test_arm()
puts("=" * 20)
test_thumb()

View file

@ -0,0 +1,69 @@
#!/usr/bin/env ruby
# Sample code for ARM64 of Unicorn. Nguyen Anh Quynh <aquynh@gmail.com>
# Ruby sample ported by Sascha Schirra <sashs82@gmail.com>
require 'unicorn'
require 'unicorn/arm64_const'
include Unicorn
# code to be emulated
ARM64_CODE = "\xab\x01\x0f\x8b" #add x11, x13, x15
# memory address where emulation starts
ADDRESS = 0x10000
# callback for tracing basic blocks
$hook_block = Proc.new do |uc, address, size, user_data|
puts(">>> Tracing basic block at 0x%x, block size = 0x%x" % [address, size])
end
# callback for tracing instructions
$hook_code = Proc.new do |uc, address, size, user_data|
puts(">>> Tracing instruction at 0x%x, instruction size = %u" % [address, size])
end
# Test ARM64
def test_arm64()
puts("Emulate ARM64 code")
begin
# Initialize emulator in ARM mode
mu = Uc.new UC_ARCH_ARM64, UC_MODE_ARM
# 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, ARM64_CODE)
# initialize machine registers
mu.reg_write(UC_ARM64_REG_X11, 0x1234)
mu.reg_write(UC_ARM64_REG_X13, 0x6789)
mu.reg_write(UC_ARM64_REG_X15, 0x3333)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, $hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, $hook_code)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + ARM64_CODE.bytesize)
# now print out some registers
puts(">>> Emulation done. Below is the CPU context")
x11 = mu.reg_read(UC_ARM64_REG_X11)
x13 = mu.reg_read(UC_ARM64_REG_X13)
x15 = mu.reg_read(UC_ARM64_REG_X15)
puts(">>> X11 = 0x%x" % x11)
rescue UcError => e
puts("ERROR: %s" % e)
end
end
test_arm64()

View file

@ -0,0 +1,65 @@
#!/usr/bin/env ruby
# Sample code for ARM of Unicorn. Nguyen Anh Quynh <aquynh@gmail.com>
# Ruby sample ported by Sascha Schirra <sashs82@gmail.com>
require 'unicorn'
require 'unicorn/m68k_const'
include Unicorn
# code to be emulated
M68K_CODE = "\x76\xed" # movq #-19, %d3
# memory address where emulation starts
ADDRESS = 0x10000
# callback for tracing basic blocks
$hook_block = Proc.new do |uc, address, size, user_data|
puts(">>> Tracing basic block at 0x%x, block size = 0x%x" % [address, size])
end
# callback for tracing instructions
$hook_code = Proc.new do |uc, address, size, user_data|
puts(">>> Tracing instruction at 0x%x, instruction size = %u" % [address, size])
end
# Test m68k
def test_m68k()
puts("Emulate M68K code")
begin
# Initialize emulator in m68k mode
mu = Uc.new UC_ARCH_M68K, UC_MODE_BIG_ENDIAN
# 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, M68K_CODE)
# initialize machine registers
mu.reg_write(UC_M68K_REG_D3, 0x1234)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, $hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, $hook_code)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + M68K_CODE.bytesize)
# now print out some registers
puts(">>> Emulation done. Below is the CPU context")
d3 = mu.reg_read(UC_M68K_REG_D3)
puts(">>> D3 = 0x%x" % d3)
rescue UcError => e
puts("ERROR: %s" % e)
end
end
test_m68k()

View file

@ -0,0 +1,104 @@
#!/usr/bin/env ruby
# Sample code for MIPS of Unicorn. Nguyen Anh Quynh <aquynh@gmail.com>
# Ruby sample ported by Sascha Schirra <sashs82@gmail.com>
require 'unicorn'
require 'unicorn/mips_const'
include Unicorn
# code to be emulated
MIPS_CODE_EB = "\x34\x21\x34\x56" # ori $at, $at, 0x3456;
MIPS_CODE_EL = "\x56\x34\x21\x34" # ori $at, $at, 0x3456;
# memory address where emulation starts
ADDRESS = 0x10000
# callback for tracing basic blocks
$hook_block = Proc.new do |uc, address, size, user_data|
puts(">>> Tracing basic block at 0x%x, block size = 0x%x" % [address, size])
end
# callback for tracing instructions
$hook_code = Proc.new do |uc, address, size, user_data|
puts(">>> Tracing instruction at 0x%x, instruction size = %u" % [address, size])
end
# Test MIPS EB
def test_mips_eb()
puts("Emulate MIPS code (big-endian)")
begin
# Initialize emulator in MIPS32 + EB mode
mu = Uc.new UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_BIG_ENDIAN
# 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, MIPS_CODE_EB)
# initialize machine registers
mu.reg_write(UC_MIPS_REG_1, 0x6789)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, $hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, $hook_code)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + MIPS_CODE_EB.bytesize)
# now puts out some registers
puts(">>> Emulation done. Below is the CPU context")
r1 = mu.reg_read(UC_MIPS_REG_1)
puts(">>> r1 = 0x%x" % r1)
rescue UcError => e
puts("ERROR: %s" % e)
end
end
# Test MIPS EL
def test_mips_el()
puts("Emulate MIPS code (little-endian)")
begin
# Initialize emulator in MIPS32 + EL mode
mu = Uc.new UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN
# 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, MIPS_CODE_EL)
# initialize machine registers
mu.reg_write(UC_MIPS_REG_1, 0x6789)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, $hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, $hook_code)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + MIPS_CODE_EL.bytesize)
# now puts out some registers
puts(">>> Emulation done. Below is the CPU context")
r1 = mu.reg_read(UC_MIPS_REG_1)
puts(">>> r1 = 0x%x" % r1)
rescue UcError => e
puts("ERROR: %s" % e)
end
end
test_mips_eb()
puts("=" * 20)
test_mips_el()

View file

@ -0,0 +1,65 @@
#!/usr/bin/env ruby
# Sample code for SPARC of Unicorn. Nguyen Anh Quynh <aquynh@gmail.com>
# Ruby sample ported by Sascha Schirra <sashs82@gmail.com>
require 'unicorn'
require 'unicorn/sparc_const'
include Unicorn
# code to be emulated
SPARC_CODE = "\x86\x00\x40\x02" # add %g1, %g2, %g3;
# memory address where emulation starts
ADDRESS = 0x10000
# callback for tracing basic blocks
$hook_block = Proc.new do |uc, address, size, user_data|
puts(">>> Tracing basic block at 0x%x, block size = 0x%x" % [address, size])
end
# callback for tracing instructions
$hook_code = Proc.new do |uc, address, size, user_data|
puts(">>> Tracing instruction at 0x%x, instruction size = %u" % [address, size])
end
# Test SPARC
def test_sparc()
puts("Emulate SPARC code")
begin
# Initialize emulator in SPARC EB mode
mu = Uc.new UC_ARCH_SPARC, UC_MODE_SPARC32|UC_MODE_BIG_ENDIAN
# 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, SPARC_CODE)
# initialize machine registers
mu.reg_write(UC_SPARC_REG_G1, 0x1230)
mu.reg_write(UC_SPARC_REG_G2, 0x6789)
mu.reg_write(UC_SPARC_REG_G3, 0x5555)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, $hook_block)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, $hook_code)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + SPARC_CODE.bytesize)
# now puts out some registers
puts(">>> Emulation done. Below is the CPU context")
g3 = mu.reg_read(UC_SPARC_REG_G3)
puts(">>> G3 = 0x%x" %g3)
rescue UcError => e
puts("ERROR: %s" % e)
end
end
test_sparc()

509
bindings/ruby/sample_x86.rb Normal file
View file

@ -0,0 +1,509 @@
#!/usr/bin/env ruby
require 'unicorn'
require 'unicorn/x86_const'
include Unicorn
X86_CODE32 = "\x41\x4a" # INC ecx; DEC edx
X86_CODE32_LOOP = "\x41\x4a\xeb\xfe" # INC ecx; DEC edx; JMP self-loop
X86_CODE32_MEM_READ = "\x8B\x0D\xAA\xAA\xAA\xAA\x41\x4a" # mov ecx,[0xaaaaaaaa]; INC ecx; DEC edx
X86_CODE32_MEM_WRITE = "\x89\x0D\xAA\xAA\xAA\xAA\x41\x4a" # mov [0xaaaaaaaa], ecx; INC ecx; DEC edx
X86_CODE64 = "\x41\xBC\x3B\xB0\x28\x2A\x49\x0F\xC9\x90\x4D\x0F\xAD\xCF\x49\x87\xFD\x90\x48\x81\xD2\x8A\xCE\x77\x35\x48\xF7\xD9\x4D\x29\xF4\x49\x81\xC9\xF6\x8A\xC6\x53\x4D\x87\xED\x48\x0F\xAD\xD2\x49\xF7\xD4\x48\xF7\xE1\x4D\x19\xC5\x4D\x89\xC5\x48\xF7\xD6\x41\xB8\x4F\x8D\x6B\x59\x4D\x87\xD0\x68\x6A\x1E\x09\x3C\x59"
X86_CODE32_INOUT = "\x41\xE4\x3F\x4a\xE6\x46\x43" # INC ecx; IN AL, 0x3f; DEC edx; OUT 0x46, AL; INC ebx
X86_CODE64_SYSCALL = "\x0f\x05" # SYSCALL
X86_CODE16 = "\x00\x00" # add byte ptr [bx + si], al
# memory address where emulation starts
ADDRESS = 0x1000000
# callback for tracing basic blocks
HOOK_BLOCK = Proc.new do |uc, address, size, user_data |
puts(">>> Tracing basic block at 0x%x, block size = 0x%x" % [address, size])
end
# callback for tracing instructions
HOOK_CODE = Proc.new do |uc, address, size, user_data|
puts(">>> Tracing instruction at 0x%x, instruction size = %u" % [address, size])
end
# callback for tracing invalid memory access (READ or WRITE)
HOOK_MEM_INVALID = lambda do |uc, access, address, size, value, user_data|
if access == UC_MEM_WRITE_UNMAPPED
puts(">>> Missing memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" % [address, size, value])
# map this memory in with 2MB in size
uc.mem_map(0xaaaa0000, 2 * 1024*1024)
# return True to indicate we want to continue emulation
return true
else
puts(">>> Missing memory is being READ at 0x%x" % address)
# return False to indicate we want to stop emulation
return false
end
end
# callback for tracing memory access (READ or WRITE)
HOOK_MEM_ACCESS = Proc.new do |uc, access, address, size, value, user_data|
if access == UC_MEM_WRITE
puts(">>> Memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" % [address, size, value])
else # READ
puts(">>> Memory is being READ at 0x%x, data size = %u" % [address, size])
end
end
# callback for IN instruction
HOOK_IN = lambda do |uc, port, size, user_data|
eip = uc.reg_read(UC_X86_REG_EIP)
puts("--- reading from port 0x%x, size: %u, address: 0x%x" % [port, size, eip])
if size == 1
# read 1 byte to AL
return 0xf1
end
if size == 2
# read 2 byte to AX
return 0xf2
end
if size == 4
# read 4 byte to EAX
return 0xf4
end
# we should never reach here
return 0
end
# callback for OUT instruction
HOOK_OUT = Proc.new do |uc, port, size, value, user_data|
eip = uc.reg_read(UC_X86_REG_EIP)
puts("--- writing to port 0x%x, size: %u, value: 0x%x, address: 0x%x" % [port, size, value, eip])
# confirm that value is indeed the value of AL/AX/EAX
v = 0
if size == 1
# read 1 byte in AL
v = uc.reg_read(UC_X86_REG_AL)
end
if size == 2
# read 2 bytes in AX
v = uc.reg_read(UC_X86_REG_AX)
end
if size == 4
# read 4 bytes in EAX
v = uc.reg_read(UC_X86_REG_EAX)
end
puts("--- register value = 0x%x" %v)
end
# Test X86 32 bit
def test_i386()
puts("Emulate i386 code")
begin
# Initialize emulator in X86-32bit mode
mu = Uc.new 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)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, HOOK_BLOCK)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, HOOK_CODE)
mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED, HOOK_MEM_INVALID)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + X86_CODE32.bytesize)
# now print out some registers
puts(">>> 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)
puts(">>> ECX = 0x%x" % r_ecx)
puts(">>> EDX = 0x%x" % r_edx)
# read from memory
tmp = mu.mem_read(ADDRESS, 2)
print(">>> Read 2 bytes from [0x%x] =" % (ADDRESS))
tmp.each_byte { |i| print(" 0x%x" % i) }
puts
rescue UcError => e
puts("ERROR: %s" % e)
end
end
def test_i386_loop()
puts("Emulate i386 code with infinite loop - wait for 2 seconds then stop emulation")
begin
# Initialize emulator in X86-32bit mode
mu = Uc.new 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_LOOP)
# initialize machine registers
mu.reg_write(UC_X86_REG_ECX, 0x1234)
mu.reg_write(UC_X86_REG_EDX, 0x7890)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + X86_CODE32_LOOP.bytesize, 2 * UC_SECOND_SCALE)
# now print out some registers
puts(">>> 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)
puts(">>> ECX = 0x%x" % r_ecx)
puts(">>> EDX = 0x%x" % r_edx)
rescue UcError => e
puts("ERROR: %s" % e)
end
end
def test_i386_invalid_mem_read()
puts("Emulate i386 code that read from invalid memory")
begin
# Initialize emulator in X86-32bit mode
mu = Uc.new 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_MEM_READ)
# initialize machine registers
mu.reg_write(UC_X86_REG_ECX, 0x1234)
mu.reg_write(UC_X86_REG_EDX, 0x7890)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, HOOK_BLOCK)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, HOOK_CODE)
begin
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + X86_CODE32_MEM_READ.bytesize)
rescue UcError => e
puts("ERROR: %s" % e)
end
# now print out some registers
puts(">>> 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)
puts(">>> ECX = 0x%x" % r_ecx)
puts(">>> EDX = 0x%x" % r_edx)
rescue UcError => e
print("ERROR: %s" % e)
end
end
def test_i386_invalid_mem_write()
puts("Emulate i386 code that write to invalid memory")
begin
# Initialize emulator in X86-32bit mode
mu = Uc.new 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_MEM_WRITE)
# initialize machine registers
mu.reg_write(UC_X86_REG_ECX, 0x1234)
mu.reg_write(UC_X86_REG_EDX, 0x7890)
# tracing all basic blocks with customized callback
#mu.hook_add(UC_HOOK_BLOCK, HOOK_BLOCK)
# tracing all instructions with customized callback
#mu.hook_add(UC_HOOK_CODE, HOOK_CODE)
# intercept invalid memory events
mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, HOOK_MEM_INVALID)
begin
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + X86_CODE32_MEM_WRITE.bytesize)
rescue UcError => e
puts "ERROR: %s" % e
end
# now print out some registers
puts ">>> 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)
puts ">>> ECX = 0x%x" % r_ecx
puts ">>> EDX = 0x%x" % r_edx
begin
# read from memory
print ">>> Read 4 bytes from [0x%x] = " % (0xaaaaaaaa)
tmp = mu.mem_read(0xaaaaaaaa, 4)
tmp.each_byte { |i| print(" 0x%x" % i) }
puts
print ">>> Read 4 bytes from [0x%x] = " % 0xffffffaa
tmp = mu.mem_read(0xffffffaa, 4)
tmp.each_byte { |i| puts(" 0x%x" % i) }
puts
rescue UcError => e
puts "ERROR: %s" % e
end
rescue UcError => e
puts "ERROR: %s" % e
end
end
# Test X86 32 bit with IN/OUT instruction
def test_i386_inout()
puts("Emulate i386 code with IN/OUT instructions")
begin
# Initialize emulator in X86-32bit mode
mu = Uc.new 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_INOUT)
# initialize machine registers
mu.reg_write(UC_X86_REG_EAX, 0x1234)
mu.reg_write(UC_X86_REG_ECX, 0x6789)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, HOOK_BLOCK)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, HOOK_CODE)
# handle IN & OUT instruction
mu.hook_add(UC_HOOK_INSN, HOOK_IN, nil, 1, 0, UC_X86_INS_IN)
mu.hook_add(UC_HOOK_INSN, HOOK_OUT, nil, 1, 0, UC_X86_INS_OUT)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + X86_CODE32_INOUT.bytesize)
# now print out some registers
puts(">>> Emulation done. Below is the CPU context")
r_ecx = mu.reg_read(UC_X86_REG_ECX)
r_eax = mu.reg_read(UC_X86_REG_EAX)
puts ">>> EAX = 0x%x" % r_eax
puts ">>> ECX = 0x%x" % r_ecx
rescue UcError => e
puts("ERROR: %s" % e)
end
end
def test_x86_64()
puts("Emulate x86_64 code")
begin
# Initialize emulator in X86-64bit mode
mu = Uc.new UC_ARCH_X86, UC_MODE_64
# 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_CODE64)
# initialize machine registers
mu.reg_write(UC_X86_REG_RAX, 0x71f3029efd49d41d)
mu.reg_write(UC_X86_REG_RBX, 0xd87b45277f133ddb)
mu.reg_write(UC_X86_REG_RCX, 0xab40d1ffd8afc461)
mu.reg_write(UC_X86_REG_RDX, 0x919317b4a733f01)
mu.reg_write(UC_X86_REG_RSI, 0x4c24e753a17ea358)
mu.reg_write(UC_X86_REG_RDI, 0xe509a57d2571ce96)
mu.reg_write(UC_X86_REG_R8, 0xea5b108cc2b9ab1f)
mu.reg_write(UC_X86_REG_R9, 0x19ec097c8eb618c1)
mu.reg_write(UC_X86_REG_R10, 0xec45774f00c5f682)
mu.reg_write(UC_X86_REG_R11, 0xe17e9dbec8c074aa)
mu.reg_write(UC_X86_REG_R12, 0x80f86a8dc0f6d457)
mu.reg_write(UC_X86_REG_R13, 0x48288ca5671c5492)
mu.reg_write(UC_X86_REG_R14, 0x595f72f6e4017f6e)
mu.reg_write(UC_X86_REG_R15, 0x1efd97aea331cccc)
# setup stack
mu.reg_write(UC_X86_REG_RSP, ADDRESS + 0x200000)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, HOOK_BLOCK)
# tracing all instructions in range [ADDRESS, ADDRESS+20]
mu.hook_add(UC_HOOK_CODE, HOOK_CODE, 0, ADDRESS, ADDRESS+20)
# tracing all memory READ & WRITE access
mu.hook_add(UC_HOOK_MEM_WRITE, HOOK_MEM_ACCESS)
mu.hook_add(UC_HOOK_MEM_READ, HOOK_MEM_ACCESS)
# actually you can also use READ_WRITE to trace all memory access
#mu.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, hook_mem_access)
begin
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + X86_CODE64.bytesize)
rescue UcError => e
puts("ERROR: %s" % e)
end
# now print out some registers
puts(">>> Emulation done. Below is the CPU context")
rax = mu.reg_read(UC_X86_REG_RAX)
rbx = mu.reg_read(UC_X86_REG_RBX)
rcx = mu.reg_read(UC_X86_REG_RCX)
rdx = mu.reg_read(UC_X86_REG_RDX)
rsi = mu.reg_read(UC_X86_REG_RSI)
rdi = mu.reg_read(UC_X86_REG_RDI)
r8 = mu.reg_read(UC_X86_REG_R8)
r9 = mu.reg_read(UC_X86_REG_R9)
r10 = mu.reg_read(UC_X86_REG_R10)
r11 = mu.reg_read(UC_X86_REG_R11)
r12 = mu.reg_read(UC_X86_REG_R12)
r13 = mu.reg_read(UC_X86_REG_R13)
r14 = mu.reg_read(UC_X86_REG_R14)
r15 = mu.reg_read(UC_X86_REG_R15)
puts(">>> RAX = %d" % rax)
puts(">>> RBX = %d" % rbx)
puts(">>> RCX = %d" % rcx)
puts(">>> RDX = %d" % rdx)
puts(">>> RSI = %d" % rsi)
puts(">>> RDI = %d" % rdi)
puts(">>> R8 = %d" % r8)
puts(">>> R9 = %d" % r9)
puts(">>> R10 = %d" % r10)
puts(">>> R11 = %d" % r11)
puts(">>> R12 = %d" % r12)
puts(">>> R13 = %d" % r13)
puts(">>> R14 = %d" % r14)
puts(">>> R15 = %d" % r15)
#BUG
mu.emu_start(ADDRESS, ADDRESS + X86_CODE64.bytesize)
rescue UcError => e
puts("ERROR: %s" % e)
end
end
def test_x86_64_syscall()
puts("Emulate x86_64 code with 'syscall' instruction")
begin
# Initialize emulator in X86-64bit mode
mu = Uc.new UC_ARCH_X86, UC_MODE_64
# 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_CODE64_SYSCALL)
hook_syscall = Proc.new do |mu, user_data|
rax = mu.reg_read(UC_X86_REG_RAX)
if rax == 0x100
mu.reg_write(UC_X86_REG_RAX, 0x200)
else
puts('ERROR: was not expecting rax=%d in syscall' % rax)
end
end
# hook interrupts for syscall
mu.hook_add(UC_HOOK_INSN, hook_syscall, nil, 1, 0, UC_X86_INS_SYSCALL)
# syscall handler is expecting rax=0x100
mu.reg_write(UC_X86_REG_RAX, 0x100)
begin
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + X86_CODE64_SYSCALL.bytesize)
rescue UcError => e
puts("ERROR: %s" % e)
end
# now print out some registers
puts(">>> Emulation done. Below is the CPU context")
rax = mu.reg_read(UC_X86_REG_RAX)
puts(">>> RAX = 0x%x" % rax)
rescue UcError => e
puts("ERROR: %s" % e)
end
end
def test_x86_16()
puts("Emulate x86 16-bit code")
begin
# Initialize emulator in X86-16bit mode
mu = Uc.new UC_ARCH_X86, UC_MODE_16
# map 8KB memory for this emulation
mu.mem_map(0, 8 * 1024)
# set CPU registers
mu.reg_write(UC_X86_REG_EAX, 7)
mu.reg_write(UC_X86_REG_EBX, 5)
mu.reg_write(UC_X86_REG_ESI, 6)
# write machine code to be emulated to memory
mu.mem_write(0, X86_CODE16)
# emulate machine code in infinite time
mu.emu_start(0, X86_CODE16.bytesize)
# now print out some registers
puts(">>> Emulation done. Below is the CPU context")
tmp = mu.mem_read(11, 1)
puts("[0x%x] = 0x%x" % [11, tmp[0].ord])
rescue UcError => e
puts("ERROR: %s" % e)
end
end
test_i386()
puts("=" * 20)
test_i386_loop()
puts("=" * 20)
test_i386_invalid_mem_read()
puts("=" * 20)
test_i386_invalid_mem_write()
puts("=" * 20)
test_i386_inout()
puts("=" * 20)
test_x86_64()
puts("=" * 20)
test_x86_64_syscall()
puts("=" * 20)
test_x86_16()

View file

@ -0,0 +1,97 @@
#!/usr/bin/env ruby
require 'unicorn'
require 'unicorn/x86_const'
include Unicorn
F_GRANULARITY = 0x8
F_PROT_32 = 0x4
F_LONG = 0x2
F_AVAILABLE = 0x1
A_PRESENT = 0x80
A_PRIV_3 = 0x60
A_PRIV_2 = 0x40
A_PRIV_1 = 0x20
A_PRIV_0 = 0x0
A_CODE = 0x10
A_DATA = 0x10
A_TSS = 0x0
A_GATE = 0x0
A_DATA_WRITABLE = 0x2
A_CODE_READABLE = 0x2
A_DIR_CON_BIT = 0x4
S_GDT = 0x0
S_LDT = 0x4
S_PRIV_3 = 0x3
S_PRIV_2 = 0x2
S_PRIV_1 = 0x1
S_PRIV_0 = 0x0
def create_selector(idx, flags)
to_ret = flags
to_ret |= idx << 3
return to_ret
end
def create_gdt_entry(base, limit, access, flags)
to_ret = limit & 0xffff;
to_ret |= (base & 0xffffff) << 16;
to_ret |= (access & 0xff) << 40;
to_ret |= ((limit >> 16) & 0xf) << 48;
to_ret |= (flags & 0xff) << 52;
to_ret |= ((base >> 24) & 0xff) << 56;
return [to_ret].pack('Q')
end
def write_gdt(uc, gdt, mem)
gdt.each_index do |idx|
offset = idx * GDT_ENTRY_SIZE
uc.mem_write(mem + offset, gdt[idx])
end
end
CODE_ADDR = 0x40000
CODE_SIZE = 0x1000
GDT_ADDR = 0x3000
GDT_LIMIT = 0x1000
GDT_ENTRY_SIZE = 0x8
GS_SEGMENT_ADDR = 0x5000
GS_SEGMENT_SIZE = 0x1000
uc = Uc.new UC_ARCH_X86, UC_MODE_32
uc.mem_map(GDT_ADDR, GDT_LIMIT)
uc.mem_map(GS_SEGMENT_ADDR, GS_SEGMENT_SIZE)
uc.mem_map(CODE_ADDR, CODE_SIZE)
gdt = Array.new (31) {|i| create_gdt_entry(0,0,0,0)}
gdt[15] = create_gdt_entry(GS_SEGMENT_ADDR, GS_SEGMENT_SIZE, A_PRESENT | A_DATA | A_DATA_WRITABLE | A_PRIV_3 | A_DIR_CON_BIT, F_PROT_32)
gdt[16] = create_gdt_entry(0, 0xfffff000 , A_PRESENT | A_DATA | A_DATA_WRITABLE | A_PRIV_3 | A_DIR_CON_BIT, F_PROT_32) # Data Segment
gdt[17] = create_gdt_entry(0, 0xfffff000 , A_PRESENT | A_CODE | A_CODE_READABLE | A_PRIV_3 | A_DIR_CON_BIT, F_PROT_32) # Code Segment
gdt[18] = create_gdt_entry(0, 0xfffff000 , A_PRESENT | A_DATA | A_DATA_WRITABLE | A_PRIV_0 | A_DIR_CON_BIT, F_PROT_32) # Stack Segment
write_gdt(uc, gdt, GDT_ADDR)
uc.reg_write(UC_X86_REG_GDTR, [0, GDT_ADDR, gdt.length * GDT_ENTRY_SIZE-1, 0x0])
selector = create_selector(15, S_GDT | S_PRIV_3)
uc.reg_write(UC_X86_REG_GS, selector)
selector = create_selector(16, S_GDT | S_PRIV_3)
uc.reg_write(UC_X86_REG_DS, selector)
selector = create_selector(17, S_GDT | S_PRIV_3)
uc.reg_write(UC_X86_REG_CS, selector)
selector = create_selector(18, S_GDT | S_PRIV_0)
uc.reg_write(UC_X86_REG_SS, selector)

View file

@ -0,0 +1,3 @@
source 'https://rubygems.org'
gemspec

View file

@ -0,0 +1,2 @@
require "bundler/gem_tasks"
task :default => :spec

View file

@ -0,0 +1,8 @@
require 'mkmf'
extension_name = 'unicorn'
dir_config(extension_name)
have_library('unicorn')
create_makefile(extension_name)

View file

@ -0,0 +1,424 @@
/*
Ruby bindings for the Unicorn Emulator Engine
Copyright(c) 2016 Sascha Schirra
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "ruby.h"
#include <unicorn/unicorn.h>
#include <unicorn/x86.h>
#include "unicorn.h"
VALUE UnicornModule = Qnil;
VALUE UcClass = Qnil;
VALUE UcError = Qnil;
void Init_unicorn() {
rb_require("unicorn/unicorn_const");
UnicornModule = rb_define_module("Unicorn");
UcError = rb_define_class_under(UnicornModule, "UcError", rb_eStandardError);
UcClass = rb_define_class_under(UnicornModule, "Uc", rb_cObject);
rb_define_method(UcClass, "initialize", m_uc_initialize, 2);
rb_define_method(UcClass, "emu_start", m_uc_emu_start, -1);
rb_define_method(UcClass, "emu_stop", m_uc_emu_stop, 0);
rb_define_method(UcClass, "reg_read", m_uc_reg_read, 1);
rb_define_method(UcClass, "reg_write", m_uc_reg_write, 2);
rb_define_method(UcClass, "mem_read", m_uc_mem_read, 2);
rb_define_method(UcClass, "mem_write", m_uc_mem_write, 2);
rb_define_method(UcClass, "mem_map", m_uc_mem_map, -1);
rb_define_method(UcClass, "mem_unmap", m_uc_mem_unmap, 2);
rb_define_method(UcClass, "mem_protect", m_uc_mem_protect, 3);
rb_define_method(UcClass, "hook_add", m_uc_hook_add, -1);
rb_define_method(UcClass, "hook_del", m_uc_hook_del, 1);
rb_define_method(UcClass, "query", m_uc_hook_del, 1);
}
VALUE m_uc_initialize(VALUE self, VALUE arch, VALUE mode) {
uc_engine *_uc;
uc_err err;
err = uc_open(NUM2INT(arch), NUM2INT(mode), &_uc);
if (err != UC_ERR_OK) {
rb_raise(UcError, "%s", uc_strerror(err));
}
VALUE uc = Data_Wrap_Struct(UcClass, 0, uc_close, _uc);
rb_iv_set(self, "@uch", uc);
return self;
}
VALUE m_uc_emu_start(int argc, VALUE* argv, VALUE self){
VALUE begin;
VALUE until;
VALUE timeout;
VALUE count;
uc_err err;
uc_engine *_uc;
Data_Get_Struct(rb_iv_get(self,"@uch"), uc_engine, _uc);
rb_scan_args(argc, argv, "22",&begin, &until, &timeout, &count);
if (NIL_P(timeout))
timeout = INT2NUM(0);
if (NIL_P(count))
count = INT2NUM(0);
err = uc_emu_start(_uc, NUM2ULL(begin), NUM2ULL(until), NUM2INT(timeout), NUM2INT(count));
if (err != UC_ERR_OK) {
rb_raise(UcError, "%s", uc_strerror(err));
}
return Qnil;
}
VALUE m_uc_emu_stop(VALUE self){
uc_err err;
uc_engine *_uc;
Data_Get_Struct(rb_iv_get(self,"@uch"), uc_engine, _uc);
err = uc_emu_stop(_uc);
if (err != UC_ERR_OK) {
rb_raise(UcError, "%s", uc_strerror(err));
}
return Qnil;
}
VALUE m_uc_reg_read(VALUE self, VALUE reg_id){
uc_err err;
int32_t tmp_reg = NUM2INT(reg_id);
int64_t reg_value = 0;
VALUE to_ret;
uc_x86_mmr mmr;
uc_engine *_uc;
Data_Get_Struct(rb_iv_get(self,"@uch"), uc_engine, _uc);
switch(tmp_reg){
case UC_X86_REG_GDTR:
case UC_X86_REG_IDTR:
case UC_X86_REG_LDTR:
case UC_X86_REG_TR:
mmr.selector = 0;
mmr.base = 0;
mmr.limit = 0;
mmr.flags = 0;
err = uc_reg_read(_uc, tmp_reg, &mmr);
if (err != UC_ERR_OK) {
rb_raise(UcError, "%s", uc_strerror(err));
}
VALUE mmr_ary = rb_ary_new();
reg_value = mmr.selector;
rb_ary_store(mmr_ary, 0, UINT2NUM(reg_value));
rb_ary_store(mmr_ary, 1, ULL2NUM(mmr.base));
rb_ary_store(mmr_ary, 2, UINT2NUM(mmr.limit));
rb_ary_store(mmr_ary, 3, UINT2NUM(mmr.flags));
return mmr_ary;
default:
err = uc_reg_read(_uc, tmp_reg, &reg_value);
if (err != UC_ERR_OK) {
rb_raise(UcError, "%s", uc_strerror(err));
}
return LL2NUM(reg_value);
}
}
VALUE m_uc_reg_write(VALUE self, VALUE reg_id, VALUE reg_value){
uc_err err;
int32_t tmp_reg = NUM2INT(reg_id);
uc_x86_mmr mmr;
int64_t tmp;
uc_engine *_uc;
Data_Get_Struct(rb_iv_get(self,"@uch"), uc_engine, _uc);
switch(tmp_reg){
case UC_X86_REG_GDTR:
case UC_X86_REG_IDTR:
case UC_X86_REG_LDTR:
case UC_X86_REG_TR:
Check_Type(reg_value, T_ARRAY);
mmr.selector = NUM2USHORT(rb_ary_entry(reg_value,0));
mmr.base = NUM2ULL(rb_ary_entry(reg_value,1));
mmr.limit = NUM2UINT(rb_ary_entry(reg_value,2));
mmr.flags = NUM2UINT(rb_ary_entry(reg_value,3));
err = uc_reg_write(_uc, tmp_reg, &mmr);
break;
default:
tmp = NUM2ULL(reg_value);
err = uc_reg_write(_uc, NUM2INT(reg_id), &tmp);
break;
}
if (err != UC_ERR_OK) {
rb_raise(UcError, "%s", uc_strerror(err));
}
return Qnil;
}
VALUE m_uc_mem_read(VALUE self, VALUE address, VALUE size){
size_t isize = NUM2UINT(size);
uint8_t bytes[isize];
uc_err err;
uc_engine *_uc;
Data_Get_Struct(rb_iv_get(self,"@uch"), uc_engine, _uc);
err = uc_mem_read(_uc, NUM2ULL(address), &bytes, isize);
if (err != UC_ERR_OK) {
rb_raise(UcError, "%s", uc_strerror(err));
}
return rb_str_new(bytes, isize);
}
VALUE m_uc_mem_write(VALUE self, VALUE address, VALUE bytes){
uc_err err;
uc_engine *_uc;
Data_Get_Struct(rb_iv_get(self,"@uch"), uc_engine, _uc);
err = uc_mem_write(_uc, NUM2ULL(address), StringValuePtr(bytes), RSTRING_LEN(bytes));
if (err != UC_ERR_OK) {
rb_raise(UcError, "%s", uc_strerror(err));
}
return Qnil;
}
VALUE m_uc_mem_map(int argc, VALUE* argv, VALUE self){
uc_err err;
VALUE address;
VALUE size;
VALUE perms;
uc_engine *_uc;
Data_Get_Struct(rb_iv_get(self,"@uch"), uc_engine, _uc);
rb_scan_args(argc, argv, "21",&address, &size, &perms);
if (NIL_P(perms))
perms = INT2NUM(UC_PROT_ALL);
err = uc_mem_map(_uc, NUM2ULL(address), NUM2UINT(size), NUM2UINT(perms));
if (err != UC_ERR_OK) {
rb_raise(UcError, "%s", uc_strerror(err));
}
return Qnil;
}
VALUE m_uc_mem_unmap(VALUE self, VALUE address, VALUE size){
uc_err err;
uc_engine *_uc;
_uc = (uc_engine*) NUM2ULL(rb_iv_get(self, "@uch"));
err = uc_mem_unmap(_uc, NUM2ULL(address), NUM2UINT(size));
if (err != UC_ERR_OK) {
rb_raise(UcError, "%s", uc_strerror(err));
}
return Qnil;
}
VALUE m_uc_mem_protect(VALUE self, VALUE address, VALUE size, VALUE perms){
uc_err err;
uc_engine *_uc;
Data_Get_Struct(rb_iv_get(self,"@uch"), uc_engine, _uc);
err = uc_mem_protect(_uc, NUM2ULL(address), NUM2UINT(size), NUM2UINT(perms));
if (err != UC_ERR_OK) {
rb_raise(UcError, "%s", uc_strerror(err));
}
return Qnil;
}
static void cb_hook_code(uc_engine *uc, uint64_t address, uint32_t size, void *user_data){
VALUE passthrough = (VALUE)user_data;
VALUE cb;
VALUE ud;
VALUE rUc;
cb = rb_ary_entry(passthrough, 0);
ud = rb_ary_entry(passthrough, 1);
rUc = rb_ary_entry(passthrough, 2);
rb_funcall(cb, rb_intern("call"), 4, rUc, ULL2NUM(address), UINT2NUM(size), ud);
}
static void cb_hook_mem_access(uc_engine *uc, uint32_t access, uint64_t address, uint32_t size, int64_t value, void *user_data){
VALUE passthrough = (VALUE)user_data;
VALUE cb;
VALUE ud;
VALUE rUc;
cb = rb_ary_entry(passthrough, 0);
ud = rb_ary_entry(passthrough, 1);
rUc = rb_ary_entry(passthrough, 2);
rb_funcall(cb, rb_intern("call"), 6, rUc, UINT2NUM(access), ULL2NUM(address), UINT2NUM(size), LL2NUM(value), ud);
}
static bool cb_hook_mem_invalid(uc_engine *uc, uint32_t access, uint64_t address, uint32_t size, int64_t value, void *user_data){
VALUE passthrough = (VALUE)user_data;
VALUE cb;
VALUE ud;
VALUE rUc;
cb = rb_ary_entry(passthrough, 0);
ud = rb_ary_entry(passthrough, 1);
rUc = rb_ary_entry(passthrough, 2);
return RTEST(rb_funcall(cb, rb_intern("call"), 6, rUc, UINT2NUM(access), ULL2NUM(address), UINT2NUM(size), LL2NUM(value), ud));
}
static uint32_t cb_hook_insn_in(uc_engine *uc, uint32_t port, int size, void *user_data){
VALUE passthrough = (VALUE)user_data;
VALUE cb;
VALUE ud;
VALUE rUc;
cb = rb_ary_entry(passthrough, 0);
ud = rb_ary_entry(passthrough, 1);
rUc = rb_ary_entry(passthrough, 2);
return NUM2UINT(rb_funcall(cb, rb_intern("call"), 4, rUc, UINT2NUM(port), INT2NUM(size), ud));
}
static void cb_hook_insn_out(uc_engine *uc, uint32_t port, int size, uint32_t value, void *user_data){
VALUE passthrough = (VALUE)user_data;
VALUE cb;
VALUE ud;
VALUE rUc;
cb = rb_ary_entry(passthrough, 0);
ud = rb_ary_entry(passthrough, 1);
rUc = rb_ary_entry(passthrough, 2);
rb_funcall(cb, rb_intern("call"), 5, rUc, UINT2NUM(port), INT2NUM(size), UINT2NUM(value), ud);
}
static void cb_hook_insn_syscall(uc_engine *uc, void *user_data){
VALUE passthrough = (VALUE)user_data;
VALUE cb;
VALUE ud;
VALUE rUc;
cb = rb_ary_entry(passthrough, 0);
ud = rb_ary_entry(passthrough, 1);
rUc = rb_ary_entry(passthrough, 2);
rb_funcall(cb, rb_intern("call"), 2, rUc, ud);
}
static void cb_hook_intr(uc_engine *uc, uint64_t address, uint32_t size, int64_t value, void *user_data){
VALUE passthrough = (VALUE)user_data;
VALUE cb;
VALUE ud;
VALUE rUc;
cb = rb_ary_entry(passthrough, 0);
ud = rb_ary_entry(passthrough, 1);
rUc = rb_ary_entry(passthrough, 2);
rb_funcall(cb, rb_intern("call"), 5, rUc, ULL2NUM(address), UINT2NUM(size), LL2NUM(value), ud);
}
VALUE m_uc_hook_add(int argc, VALUE* argv, VALUE self){
VALUE hook_type;
VALUE callback;
VALUE user_data;
VALUE begin;
VALUE end;
VALUE arg1;
uc_engine *_uc;
Data_Get_Struct(rb_iv_get(self,"@uch"), uc_engine, _uc);
rb_scan_args(argc, argv, "24",&hook_type, &callback, &user_data, &begin, &end, &arg1);
if (NIL_P(begin))
begin = ULL2NUM(1);
if (NIL_P(end))
end = ULL2NUM(0);
if (NIL_P(arg1))
arg1 = INT2NUM(0);
VALUE passthrough;
uc_hook trace;
uc_err err;
if (rb_class_of(callback) != rb_cProc)
rb_raise(UcError, "Expected Proc callback");
passthrough = rb_ary_new();
rb_ary_store(passthrough, 0, callback);
rb_ary_store(passthrough, 1, user_data);
rb_ary_store(passthrough, 2, self);
uint32_t htype = NUM2UINT(hook_type);
if(htype == UC_HOOK_INSN){
switch(NUM2INT(arg1)){
case UC_X86_INS_IN:
err = uc_hook_add(_uc, &trace, htype, cb_hook_insn_in,(void *)passthrough, NUM2ULL(begin), NUM2ULL(end), NUM2INT(arg1));
break;
case UC_X86_INS_OUT:
err = uc_hook_add(_uc, &trace, htype, cb_hook_insn_out,(void *)passthrough, NUM2ULL(begin), NUM2ULL(end), NUM2INT(arg1));
break;
case UC_X86_INS_SYSCALL:
case UC_X86_INS_SYSENTER:
err = uc_hook_add(_uc, &trace, htype, cb_hook_insn_syscall,(void *)passthrough, NUM2ULL(begin), NUM2ULL(end), NUM2INT(arg1));
break;
}
}
else if(htype == UC_HOOK_INTR){
err = uc_hook_add(_uc, &trace, htype, cb_hook_intr,(void *)passthrough, NUM2ULL(begin), NUM2ULL(end));
}
else if(htype == UC_HOOK_CODE || htype == UC_HOOK_BLOCK){
err = uc_hook_add(_uc, &trace, htype, cb_hook_code,(void *)passthrough, NUM2ULL(begin), NUM2ULL(end));
}
else if (htype & UC_HOOK_MEM_READ_UNMAPPED
|| htype & UC_HOOK_MEM_WRITE_UNMAPPED
|| htype & UC_HOOK_MEM_FETCH_UNMAPPED
|| htype & UC_HOOK_MEM_READ_PROT
|| htype & UC_HOOK_MEM_WRITE_PROT
|| htype & UC_HOOK_MEM_FETCH_PROT
|| htype & UC_HOOK_MEM_READ_INVALID
|| htype & UC_HOOK_MEM_WRITE_INVALID
|| htype & UC_HOOK_MEM_FETCH_INVALID
|| htype & UC_HOOK_MEM_UNMAPPED
|| htype & UC_HOOK_MEM_PROT
|| htype & UC_HOOK_MEM_INVALID) {
err = uc_hook_add(_uc, &trace, htype, cb_hook_mem_invalid,(void *)passthrough, NUM2ULL(begin), NUM2ULL(end));
}
else{
err = uc_hook_add(_uc, &trace, htype, cb_hook_mem_access,(void *)passthrough, NUM2ULL(begin), NUM2ULL(end));
}
if (err != UC_ERR_OK) {
rb_raise(UcError, "%s", uc_strerror(err));
}
return INT2NUM(trace);
}
VALUE m_uc_hook_del(VALUE self, VALUE hook){
int h = NUM2INT(hook);
uc_err err;
uc_engine *_uc;
Data_Get_Struct(rb_iv_get(self,"@uch"), uc_engine, _uc);
err = uc_hook_del(_uc, h);
if (err != UC_ERR_OK) {
rb_raise(UcError, "%s", uc_strerror(err));
}
return Qnil;
}
VALUE m_uc_query(VALUE self, VALUE query_mode){
int qm = NUM2INT(query_mode);
size_t result;
uc_err err;
uc_engine *_uc;
Data_Get_Struct(rb_iv_get(self,"@uch"), uc_engine, _uc);
err = uc_query(_uc, qm, &result);
if (err != UC_ERR_OK) {
rb_raise(UcError, "%s", uc_strerror(err));
}
return INT2NUM(result);
}

View file

@ -0,0 +1,33 @@
/*
Ruby bindings for the Unicorn Emulator Engine
Copyright(c) 2016 Sascha Schirra
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
VALUE m_uc_initialize(VALUE self, VALUE arch, VALUE mode);
VALUE m_uc_emu_start(int argc, VALUE* argv, VALUE self);
VALUE m_uc_emu_stop(VALUE self);
VALUE m_uc_reg_read(VALUE self, VALUE reg_id);
VALUE m_uc_reg_write(VALUE self, VALUE reg_id, VALUE reg_value);
VALUE m_uc_mem_read(VALUE self, VALUE address, VALUE size);
VALUE m_uc_mem_write(VALUE self, VALUE address, VALUE bytes);
VALUE m_uc_mem_map(int argc, VALUE* argv, VALUE self);
VALUE m_uc_mem_unmap(VALUE self, VALUE address, VALUE size);
VALUE m_uc_mem_protect(VALUE self, VALUE address, VALUE size, VALUE perms);
VALUE m_uc_hook_add(int argc, VALUE* argv, VALUE self);
VALUE m_uc_hook_del(VALUE self, VALUE hook);
VALUE m_uc_query(VALUE self, VALUE query_mode);

View file

@ -0,0 +1,277 @@
# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [arm64_const.rb]
module Unicorn
# ARM64 registers
UC_ARM64_REG_INVALID = 0
UC_ARM64_REG_X29 = 1
UC_ARM64_REG_X30 = 2
UC_ARM64_REG_NZCV = 3
UC_ARM64_REG_SP = 4
UC_ARM64_REG_WSP = 5
UC_ARM64_REG_WZR = 6
UC_ARM64_REG_XZR = 7
UC_ARM64_REG_B0 = 8
UC_ARM64_REG_B1 = 9
UC_ARM64_REG_B2 = 10
UC_ARM64_REG_B3 = 11
UC_ARM64_REG_B4 = 12
UC_ARM64_REG_B5 = 13
UC_ARM64_REG_B6 = 14
UC_ARM64_REG_B7 = 15
UC_ARM64_REG_B8 = 16
UC_ARM64_REG_B9 = 17
UC_ARM64_REG_B10 = 18
UC_ARM64_REG_B11 = 19
UC_ARM64_REG_B12 = 20
UC_ARM64_REG_B13 = 21
UC_ARM64_REG_B14 = 22
UC_ARM64_REG_B15 = 23
UC_ARM64_REG_B16 = 24
UC_ARM64_REG_B17 = 25
UC_ARM64_REG_B18 = 26
UC_ARM64_REG_B19 = 27
UC_ARM64_REG_B20 = 28
UC_ARM64_REG_B21 = 29
UC_ARM64_REG_B22 = 30
UC_ARM64_REG_B23 = 31
UC_ARM64_REG_B24 = 32
UC_ARM64_REG_B25 = 33
UC_ARM64_REG_B26 = 34
UC_ARM64_REG_B27 = 35
UC_ARM64_REG_B28 = 36
UC_ARM64_REG_B29 = 37
UC_ARM64_REG_B30 = 38
UC_ARM64_REG_B31 = 39
UC_ARM64_REG_D0 = 40
UC_ARM64_REG_D1 = 41
UC_ARM64_REG_D2 = 42
UC_ARM64_REG_D3 = 43
UC_ARM64_REG_D4 = 44
UC_ARM64_REG_D5 = 45
UC_ARM64_REG_D6 = 46
UC_ARM64_REG_D7 = 47
UC_ARM64_REG_D8 = 48
UC_ARM64_REG_D9 = 49
UC_ARM64_REG_D10 = 50
UC_ARM64_REG_D11 = 51
UC_ARM64_REG_D12 = 52
UC_ARM64_REG_D13 = 53
UC_ARM64_REG_D14 = 54
UC_ARM64_REG_D15 = 55
UC_ARM64_REG_D16 = 56
UC_ARM64_REG_D17 = 57
UC_ARM64_REG_D18 = 58
UC_ARM64_REG_D19 = 59
UC_ARM64_REG_D20 = 60
UC_ARM64_REG_D21 = 61
UC_ARM64_REG_D22 = 62
UC_ARM64_REG_D23 = 63
UC_ARM64_REG_D24 = 64
UC_ARM64_REG_D25 = 65
UC_ARM64_REG_D26 = 66
UC_ARM64_REG_D27 = 67
UC_ARM64_REG_D28 = 68
UC_ARM64_REG_D29 = 69
UC_ARM64_REG_D30 = 70
UC_ARM64_REG_D31 = 71
UC_ARM64_REG_H0 = 72
UC_ARM64_REG_H1 = 73
UC_ARM64_REG_H2 = 74
UC_ARM64_REG_H3 = 75
UC_ARM64_REG_H4 = 76
UC_ARM64_REG_H5 = 77
UC_ARM64_REG_H6 = 78
UC_ARM64_REG_H7 = 79
UC_ARM64_REG_H8 = 80
UC_ARM64_REG_H9 = 81
UC_ARM64_REG_H10 = 82
UC_ARM64_REG_H11 = 83
UC_ARM64_REG_H12 = 84
UC_ARM64_REG_H13 = 85
UC_ARM64_REG_H14 = 86
UC_ARM64_REG_H15 = 87
UC_ARM64_REG_H16 = 88
UC_ARM64_REG_H17 = 89
UC_ARM64_REG_H18 = 90
UC_ARM64_REG_H19 = 91
UC_ARM64_REG_H20 = 92
UC_ARM64_REG_H21 = 93
UC_ARM64_REG_H22 = 94
UC_ARM64_REG_H23 = 95
UC_ARM64_REG_H24 = 96
UC_ARM64_REG_H25 = 97
UC_ARM64_REG_H26 = 98
UC_ARM64_REG_H27 = 99
UC_ARM64_REG_H28 = 100
UC_ARM64_REG_H29 = 101
UC_ARM64_REG_H30 = 102
UC_ARM64_REG_H31 = 103
UC_ARM64_REG_Q0 = 104
UC_ARM64_REG_Q1 = 105
UC_ARM64_REG_Q2 = 106
UC_ARM64_REG_Q3 = 107
UC_ARM64_REG_Q4 = 108
UC_ARM64_REG_Q5 = 109
UC_ARM64_REG_Q6 = 110
UC_ARM64_REG_Q7 = 111
UC_ARM64_REG_Q8 = 112
UC_ARM64_REG_Q9 = 113
UC_ARM64_REG_Q10 = 114
UC_ARM64_REG_Q11 = 115
UC_ARM64_REG_Q12 = 116
UC_ARM64_REG_Q13 = 117
UC_ARM64_REG_Q14 = 118
UC_ARM64_REG_Q15 = 119
UC_ARM64_REG_Q16 = 120
UC_ARM64_REG_Q17 = 121
UC_ARM64_REG_Q18 = 122
UC_ARM64_REG_Q19 = 123
UC_ARM64_REG_Q20 = 124
UC_ARM64_REG_Q21 = 125
UC_ARM64_REG_Q22 = 126
UC_ARM64_REG_Q23 = 127
UC_ARM64_REG_Q24 = 128
UC_ARM64_REG_Q25 = 129
UC_ARM64_REG_Q26 = 130
UC_ARM64_REG_Q27 = 131
UC_ARM64_REG_Q28 = 132
UC_ARM64_REG_Q29 = 133
UC_ARM64_REG_Q30 = 134
UC_ARM64_REG_Q31 = 135
UC_ARM64_REG_S0 = 136
UC_ARM64_REG_S1 = 137
UC_ARM64_REG_S2 = 138
UC_ARM64_REG_S3 = 139
UC_ARM64_REG_S4 = 140
UC_ARM64_REG_S5 = 141
UC_ARM64_REG_S6 = 142
UC_ARM64_REG_S7 = 143
UC_ARM64_REG_S8 = 144
UC_ARM64_REG_S9 = 145
UC_ARM64_REG_S10 = 146
UC_ARM64_REG_S11 = 147
UC_ARM64_REG_S12 = 148
UC_ARM64_REG_S13 = 149
UC_ARM64_REG_S14 = 150
UC_ARM64_REG_S15 = 151
UC_ARM64_REG_S16 = 152
UC_ARM64_REG_S17 = 153
UC_ARM64_REG_S18 = 154
UC_ARM64_REG_S19 = 155
UC_ARM64_REG_S20 = 156
UC_ARM64_REG_S21 = 157
UC_ARM64_REG_S22 = 158
UC_ARM64_REG_S23 = 159
UC_ARM64_REG_S24 = 160
UC_ARM64_REG_S25 = 161
UC_ARM64_REG_S26 = 162
UC_ARM64_REG_S27 = 163
UC_ARM64_REG_S28 = 164
UC_ARM64_REG_S29 = 165
UC_ARM64_REG_S30 = 166
UC_ARM64_REG_S31 = 167
UC_ARM64_REG_W0 = 168
UC_ARM64_REG_W1 = 169
UC_ARM64_REG_W2 = 170
UC_ARM64_REG_W3 = 171
UC_ARM64_REG_W4 = 172
UC_ARM64_REG_W5 = 173
UC_ARM64_REG_W6 = 174
UC_ARM64_REG_W7 = 175
UC_ARM64_REG_W8 = 176
UC_ARM64_REG_W9 = 177
UC_ARM64_REG_W10 = 178
UC_ARM64_REG_W11 = 179
UC_ARM64_REG_W12 = 180
UC_ARM64_REG_W13 = 181
UC_ARM64_REG_W14 = 182
UC_ARM64_REG_W15 = 183
UC_ARM64_REG_W16 = 184
UC_ARM64_REG_W17 = 185
UC_ARM64_REG_W18 = 186
UC_ARM64_REG_W19 = 187
UC_ARM64_REG_W20 = 188
UC_ARM64_REG_W21 = 189
UC_ARM64_REG_W22 = 190
UC_ARM64_REG_W23 = 191
UC_ARM64_REG_W24 = 192
UC_ARM64_REG_W25 = 193
UC_ARM64_REG_W26 = 194
UC_ARM64_REG_W27 = 195
UC_ARM64_REG_W28 = 196
UC_ARM64_REG_W29 = 197
UC_ARM64_REG_W30 = 198
UC_ARM64_REG_X0 = 199
UC_ARM64_REG_X1 = 200
UC_ARM64_REG_X2 = 201
UC_ARM64_REG_X3 = 202
UC_ARM64_REG_X4 = 203
UC_ARM64_REG_X5 = 204
UC_ARM64_REG_X6 = 205
UC_ARM64_REG_X7 = 206
UC_ARM64_REG_X8 = 207
UC_ARM64_REG_X9 = 208
UC_ARM64_REG_X10 = 209
UC_ARM64_REG_X11 = 210
UC_ARM64_REG_X12 = 211
UC_ARM64_REG_X13 = 212
UC_ARM64_REG_X14 = 213
UC_ARM64_REG_X15 = 214
UC_ARM64_REG_X16 = 215
UC_ARM64_REG_X17 = 216
UC_ARM64_REG_X18 = 217
UC_ARM64_REG_X19 = 218
UC_ARM64_REG_X20 = 219
UC_ARM64_REG_X21 = 220
UC_ARM64_REG_X22 = 221
UC_ARM64_REG_X23 = 222
UC_ARM64_REG_X24 = 223
UC_ARM64_REG_X25 = 224
UC_ARM64_REG_X26 = 225
UC_ARM64_REG_X27 = 226
UC_ARM64_REG_X28 = 227
UC_ARM64_REG_V0 = 228
UC_ARM64_REG_V1 = 229
UC_ARM64_REG_V2 = 230
UC_ARM64_REG_V3 = 231
UC_ARM64_REG_V4 = 232
UC_ARM64_REG_V5 = 233
UC_ARM64_REG_V6 = 234
UC_ARM64_REG_V7 = 235
UC_ARM64_REG_V8 = 236
UC_ARM64_REG_V9 = 237
UC_ARM64_REG_V10 = 238
UC_ARM64_REG_V11 = 239
UC_ARM64_REG_V12 = 240
UC_ARM64_REG_V13 = 241
UC_ARM64_REG_V14 = 242
UC_ARM64_REG_V15 = 243
UC_ARM64_REG_V16 = 244
UC_ARM64_REG_V17 = 245
UC_ARM64_REG_V18 = 246
UC_ARM64_REG_V19 = 247
UC_ARM64_REG_V20 = 248
UC_ARM64_REG_V21 = 249
UC_ARM64_REG_V22 = 250
UC_ARM64_REG_V23 = 251
UC_ARM64_REG_V24 = 252
UC_ARM64_REG_V25 = 253
UC_ARM64_REG_V26 = 254
UC_ARM64_REG_V27 = 255
UC_ARM64_REG_V28 = 256
UC_ARM64_REG_V29 = 257
UC_ARM64_REG_V30 = 258
UC_ARM64_REG_V31 = 259
# pseudo registers
UC_ARM64_REG_PC = 260
UC_ARM64_REG_ENDING = 261
# alias registers
UC_ARM64_REG_IP1 = 215
UC_ARM64_REG_IP0 = 216
UC_ARM64_REG_FP = 1
UC_ARM64_REG_LR = 2
end

View file

@ -0,0 +1,128 @@
# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [arm_const.rb]
module Unicorn
# ARM registers
UC_ARM_REG_INVALID = 0
UC_ARM_REG_APSR = 1
UC_ARM_REG_APSR_NZCV = 2
UC_ARM_REG_CPSR = 3
UC_ARM_REG_FPEXC = 4
UC_ARM_REG_FPINST = 5
UC_ARM_REG_FPSCR = 6
UC_ARM_REG_FPSCR_NZCV = 7
UC_ARM_REG_FPSID = 8
UC_ARM_REG_ITSTATE = 9
UC_ARM_REG_LR = 10
UC_ARM_REG_PC = 11
UC_ARM_REG_SP = 12
UC_ARM_REG_SPSR = 13
UC_ARM_REG_D0 = 14
UC_ARM_REG_D1 = 15
UC_ARM_REG_D2 = 16
UC_ARM_REG_D3 = 17
UC_ARM_REG_D4 = 18
UC_ARM_REG_D5 = 19
UC_ARM_REG_D6 = 20
UC_ARM_REG_D7 = 21
UC_ARM_REG_D8 = 22
UC_ARM_REG_D9 = 23
UC_ARM_REG_D10 = 24
UC_ARM_REG_D11 = 25
UC_ARM_REG_D12 = 26
UC_ARM_REG_D13 = 27
UC_ARM_REG_D14 = 28
UC_ARM_REG_D15 = 29
UC_ARM_REG_D16 = 30
UC_ARM_REG_D17 = 31
UC_ARM_REG_D18 = 32
UC_ARM_REG_D19 = 33
UC_ARM_REG_D20 = 34
UC_ARM_REG_D21 = 35
UC_ARM_REG_D22 = 36
UC_ARM_REG_D23 = 37
UC_ARM_REG_D24 = 38
UC_ARM_REG_D25 = 39
UC_ARM_REG_D26 = 40
UC_ARM_REG_D27 = 41
UC_ARM_REG_D28 = 42
UC_ARM_REG_D29 = 43
UC_ARM_REG_D30 = 44
UC_ARM_REG_D31 = 45
UC_ARM_REG_FPINST2 = 46
UC_ARM_REG_MVFR0 = 47
UC_ARM_REG_MVFR1 = 48
UC_ARM_REG_MVFR2 = 49
UC_ARM_REG_Q0 = 50
UC_ARM_REG_Q1 = 51
UC_ARM_REG_Q2 = 52
UC_ARM_REG_Q3 = 53
UC_ARM_REG_Q4 = 54
UC_ARM_REG_Q5 = 55
UC_ARM_REG_Q6 = 56
UC_ARM_REG_Q7 = 57
UC_ARM_REG_Q8 = 58
UC_ARM_REG_Q9 = 59
UC_ARM_REG_Q10 = 60
UC_ARM_REG_Q11 = 61
UC_ARM_REG_Q12 = 62
UC_ARM_REG_Q13 = 63
UC_ARM_REG_Q14 = 64
UC_ARM_REG_Q15 = 65
UC_ARM_REG_R0 = 66
UC_ARM_REG_R1 = 67
UC_ARM_REG_R2 = 68
UC_ARM_REG_R3 = 69
UC_ARM_REG_R4 = 70
UC_ARM_REG_R5 = 71
UC_ARM_REG_R6 = 72
UC_ARM_REG_R7 = 73
UC_ARM_REG_R8 = 74
UC_ARM_REG_R9 = 75
UC_ARM_REG_R10 = 76
UC_ARM_REG_R11 = 77
UC_ARM_REG_R12 = 78
UC_ARM_REG_S0 = 79
UC_ARM_REG_S1 = 80
UC_ARM_REG_S2 = 81
UC_ARM_REG_S3 = 82
UC_ARM_REG_S4 = 83
UC_ARM_REG_S5 = 84
UC_ARM_REG_S6 = 85
UC_ARM_REG_S7 = 86
UC_ARM_REG_S8 = 87
UC_ARM_REG_S9 = 88
UC_ARM_REG_S10 = 89
UC_ARM_REG_S11 = 90
UC_ARM_REG_S12 = 91
UC_ARM_REG_S13 = 92
UC_ARM_REG_S14 = 93
UC_ARM_REG_S15 = 94
UC_ARM_REG_S16 = 95
UC_ARM_REG_S17 = 96
UC_ARM_REG_S18 = 97
UC_ARM_REG_S19 = 98
UC_ARM_REG_S20 = 99
UC_ARM_REG_S21 = 100
UC_ARM_REG_S22 = 101
UC_ARM_REG_S23 = 102
UC_ARM_REG_S24 = 103
UC_ARM_REG_S25 = 104
UC_ARM_REG_S26 = 105
UC_ARM_REG_S27 = 106
UC_ARM_REG_S28 = 107
UC_ARM_REG_S29 = 108
UC_ARM_REG_S30 = 109
UC_ARM_REG_S31 = 110
UC_ARM_REG_ENDING = 111
# alias registers
UC_ARM_REG_R13 = 12
UC_ARM_REG_R14 = 10
UC_ARM_REG_R15 = 11
UC_ARM_REG_SB = 75
UC_ARM_REG_SL = 76
UC_ARM_REG_FP = 77
UC_ARM_REG_IP = 78
end

View file

@ -0,0 +1,27 @@
# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [m68k_const.rb]
module Unicorn
# M68K registers
UC_M68K_REG_INVALID = 0
UC_M68K_REG_A0 = 1
UC_M68K_REG_A1 = 2
UC_M68K_REG_A2 = 3
UC_M68K_REG_A3 = 4
UC_M68K_REG_A4 = 5
UC_M68K_REG_A5 = 6
UC_M68K_REG_A6 = 7
UC_M68K_REG_A7 = 8
UC_M68K_REG_D0 = 9
UC_M68K_REG_D1 = 10
UC_M68K_REG_D2 = 11
UC_M68K_REG_D3 = 12
UC_M68K_REG_D4 = 13
UC_M68K_REG_D5 = 14
UC_M68K_REG_D6 = 15
UC_M68K_REG_D7 = 16
UC_M68K_REG_SR = 17
UC_M68K_REG_PC = 18
UC_M68K_REG_ENDING = 19
end

View file

@ -0,0 +1,198 @@
# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [mips_const.rb]
module Unicorn
# MIPS registers
UC_MIPS_REG_INVALID = 0
# General purpose registers
UC_MIPS_REG_PC = 1
UC_MIPS_REG_0 = 2
UC_MIPS_REG_1 = 3
UC_MIPS_REG_2 = 4
UC_MIPS_REG_3 = 5
UC_MIPS_REG_4 = 6
UC_MIPS_REG_5 = 7
UC_MIPS_REG_6 = 8
UC_MIPS_REG_7 = 9
UC_MIPS_REG_8 = 10
UC_MIPS_REG_9 = 11
UC_MIPS_REG_10 = 12
UC_MIPS_REG_11 = 13
UC_MIPS_REG_12 = 14
UC_MIPS_REG_13 = 15
UC_MIPS_REG_14 = 16
UC_MIPS_REG_15 = 17
UC_MIPS_REG_16 = 18
UC_MIPS_REG_17 = 19
UC_MIPS_REG_18 = 20
UC_MIPS_REG_19 = 21
UC_MIPS_REG_20 = 22
UC_MIPS_REG_21 = 23
UC_MIPS_REG_22 = 24
UC_MIPS_REG_23 = 25
UC_MIPS_REG_24 = 26
UC_MIPS_REG_25 = 27
UC_MIPS_REG_26 = 28
UC_MIPS_REG_27 = 29
UC_MIPS_REG_28 = 30
UC_MIPS_REG_29 = 31
UC_MIPS_REG_30 = 32
UC_MIPS_REG_31 = 33
# DSP registers
UC_MIPS_REG_DSPCCOND = 34
UC_MIPS_REG_DSPCARRY = 35
UC_MIPS_REG_DSPEFI = 36
UC_MIPS_REG_DSPOUTFLAG = 37
UC_MIPS_REG_DSPOUTFLAG16_19 = 38
UC_MIPS_REG_DSPOUTFLAG20 = 39
UC_MIPS_REG_DSPOUTFLAG21 = 40
UC_MIPS_REG_DSPOUTFLAG22 = 41
UC_MIPS_REG_DSPOUTFLAG23 = 42
UC_MIPS_REG_DSPPOS = 43
UC_MIPS_REG_DSPSCOUNT = 44
# ACC registers
UC_MIPS_REG_AC0 = 45
UC_MIPS_REG_AC1 = 46
UC_MIPS_REG_AC2 = 47
UC_MIPS_REG_AC3 = 48
# COP registers
UC_MIPS_REG_CC0 = 49
UC_MIPS_REG_CC1 = 50
UC_MIPS_REG_CC2 = 51
UC_MIPS_REG_CC3 = 52
UC_MIPS_REG_CC4 = 53
UC_MIPS_REG_CC5 = 54
UC_MIPS_REG_CC6 = 55
UC_MIPS_REG_CC7 = 56
# FPU registers
UC_MIPS_REG_F0 = 57
UC_MIPS_REG_F1 = 58
UC_MIPS_REG_F2 = 59
UC_MIPS_REG_F3 = 60
UC_MIPS_REG_F4 = 61
UC_MIPS_REG_F5 = 62
UC_MIPS_REG_F6 = 63
UC_MIPS_REG_F7 = 64
UC_MIPS_REG_F8 = 65
UC_MIPS_REG_F9 = 66
UC_MIPS_REG_F10 = 67
UC_MIPS_REG_F11 = 68
UC_MIPS_REG_F12 = 69
UC_MIPS_REG_F13 = 70
UC_MIPS_REG_F14 = 71
UC_MIPS_REG_F15 = 72
UC_MIPS_REG_F16 = 73
UC_MIPS_REG_F17 = 74
UC_MIPS_REG_F18 = 75
UC_MIPS_REG_F19 = 76
UC_MIPS_REG_F20 = 77
UC_MIPS_REG_F21 = 78
UC_MIPS_REG_F22 = 79
UC_MIPS_REG_F23 = 80
UC_MIPS_REG_F24 = 81
UC_MIPS_REG_F25 = 82
UC_MIPS_REG_F26 = 83
UC_MIPS_REG_F27 = 84
UC_MIPS_REG_F28 = 85
UC_MIPS_REG_F29 = 86
UC_MIPS_REG_F30 = 87
UC_MIPS_REG_F31 = 88
UC_MIPS_REG_FCC0 = 89
UC_MIPS_REG_FCC1 = 90
UC_MIPS_REG_FCC2 = 91
UC_MIPS_REG_FCC3 = 92
UC_MIPS_REG_FCC4 = 93
UC_MIPS_REG_FCC5 = 94
UC_MIPS_REG_FCC6 = 95
UC_MIPS_REG_FCC7 = 96
# AFPR128
UC_MIPS_REG_W0 = 97
UC_MIPS_REG_W1 = 98
UC_MIPS_REG_W2 = 99
UC_MIPS_REG_W3 = 100
UC_MIPS_REG_W4 = 101
UC_MIPS_REG_W5 = 102
UC_MIPS_REG_W6 = 103
UC_MIPS_REG_W7 = 104
UC_MIPS_REG_W8 = 105
UC_MIPS_REG_W9 = 106
UC_MIPS_REG_W10 = 107
UC_MIPS_REG_W11 = 108
UC_MIPS_REG_W12 = 109
UC_MIPS_REG_W13 = 110
UC_MIPS_REG_W14 = 111
UC_MIPS_REG_W15 = 112
UC_MIPS_REG_W16 = 113
UC_MIPS_REG_W17 = 114
UC_MIPS_REG_W18 = 115
UC_MIPS_REG_W19 = 116
UC_MIPS_REG_W20 = 117
UC_MIPS_REG_W21 = 118
UC_MIPS_REG_W22 = 119
UC_MIPS_REG_W23 = 120
UC_MIPS_REG_W24 = 121
UC_MIPS_REG_W25 = 122
UC_MIPS_REG_W26 = 123
UC_MIPS_REG_W27 = 124
UC_MIPS_REG_W28 = 125
UC_MIPS_REG_W29 = 126
UC_MIPS_REG_W30 = 127
UC_MIPS_REG_W31 = 128
UC_MIPS_REG_HI = 129
UC_MIPS_REG_LO = 130
UC_MIPS_REG_P0 = 131
UC_MIPS_REG_P1 = 132
UC_MIPS_REG_P2 = 133
UC_MIPS_REG_MPL0 = 134
UC_MIPS_REG_MPL1 = 135
UC_MIPS_REG_MPL2 = 136
UC_MIPS_REG_ENDING = 137
UC_MIPS_REG_ZERO = 2
UC_MIPS_REG_AT = 3
UC_MIPS_REG_V0 = 4
UC_MIPS_REG_V1 = 5
UC_MIPS_REG_A0 = 6
UC_MIPS_REG_A1 = 7
UC_MIPS_REG_A2 = 8
UC_MIPS_REG_A3 = 9
UC_MIPS_REG_T0 = 10
UC_MIPS_REG_T1 = 11
UC_MIPS_REG_T2 = 12
UC_MIPS_REG_T3 = 13
UC_MIPS_REG_T4 = 14
UC_MIPS_REG_T5 = 15
UC_MIPS_REG_T6 = 16
UC_MIPS_REG_T7 = 17
UC_MIPS_REG_S0 = 18
UC_MIPS_REG_S1 = 19
UC_MIPS_REG_S2 = 20
UC_MIPS_REG_S3 = 21
UC_MIPS_REG_S4 = 22
UC_MIPS_REG_S5 = 23
UC_MIPS_REG_S6 = 24
UC_MIPS_REG_S7 = 25
UC_MIPS_REG_T8 = 26
UC_MIPS_REG_T9 = 27
UC_MIPS_REG_K0 = 28
UC_MIPS_REG_K1 = 29
UC_MIPS_REG_GP = 30
UC_MIPS_REG_SP = 31
UC_MIPS_REG_FP = 32
UC_MIPS_REG_S8 = 32
UC_MIPS_REG_RA = 33
UC_MIPS_REG_HI0 = 45
UC_MIPS_REG_HI1 = 46
UC_MIPS_REG_HI2 = 47
UC_MIPS_REG_HI3 = 48
UC_MIPS_REG_LO0 = 45
UC_MIPS_REG_LO1 = 46
UC_MIPS_REG_LO2 = 47
UC_MIPS_REG_LO3 = 48
end

View file

@ -0,0 +1,99 @@
# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [sparc_const.rb]
module Unicorn
# SPARC registers
UC_SPARC_REG_INVALID = 0
UC_SPARC_REG_F0 = 1
UC_SPARC_REG_F1 = 2
UC_SPARC_REG_F2 = 3
UC_SPARC_REG_F3 = 4
UC_SPARC_REG_F4 = 5
UC_SPARC_REG_F5 = 6
UC_SPARC_REG_F6 = 7
UC_SPARC_REG_F7 = 8
UC_SPARC_REG_F8 = 9
UC_SPARC_REG_F9 = 10
UC_SPARC_REG_F10 = 11
UC_SPARC_REG_F11 = 12
UC_SPARC_REG_F12 = 13
UC_SPARC_REG_F13 = 14
UC_SPARC_REG_F14 = 15
UC_SPARC_REG_F15 = 16
UC_SPARC_REG_F16 = 17
UC_SPARC_REG_F17 = 18
UC_SPARC_REG_F18 = 19
UC_SPARC_REG_F19 = 20
UC_SPARC_REG_F20 = 21
UC_SPARC_REG_F21 = 22
UC_SPARC_REG_F22 = 23
UC_SPARC_REG_F23 = 24
UC_SPARC_REG_F24 = 25
UC_SPARC_REG_F25 = 26
UC_SPARC_REG_F26 = 27
UC_SPARC_REG_F27 = 28
UC_SPARC_REG_F28 = 29
UC_SPARC_REG_F29 = 30
UC_SPARC_REG_F30 = 31
UC_SPARC_REG_F31 = 32
UC_SPARC_REG_F32 = 33
UC_SPARC_REG_F34 = 34
UC_SPARC_REG_F36 = 35
UC_SPARC_REG_F38 = 36
UC_SPARC_REG_F40 = 37
UC_SPARC_REG_F42 = 38
UC_SPARC_REG_F44 = 39
UC_SPARC_REG_F46 = 40
UC_SPARC_REG_F48 = 41
UC_SPARC_REG_F50 = 42
UC_SPARC_REG_F52 = 43
UC_SPARC_REG_F54 = 44
UC_SPARC_REG_F56 = 45
UC_SPARC_REG_F58 = 46
UC_SPARC_REG_F60 = 47
UC_SPARC_REG_F62 = 48
UC_SPARC_REG_FCC0 = 49
UC_SPARC_REG_FCC1 = 50
UC_SPARC_REG_FCC2 = 51
UC_SPARC_REG_FCC3 = 52
UC_SPARC_REG_G0 = 53
UC_SPARC_REG_G1 = 54
UC_SPARC_REG_G2 = 55
UC_SPARC_REG_G3 = 56
UC_SPARC_REG_G4 = 57
UC_SPARC_REG_G5 = 58
UC_SPARC_REG_G6 = 59
UC_SPARC_REG_G7 = 60
UC_SPARC_REG_I0 = 61
UC_SPARC_REG_I1 = 62
UC_SPARC_REG_I2 = 63
UC_SPARC_REG_I3 = 64
UC_SPARC_REG_I4 = 65
UC_SPARC_REG_I5 = 66
UC_SPARC_REG_FP = 67
UC_SPARC_REG_I7 = 68
UC_SPARC_REG_ICC = 69
UC_SPARC_REG_L0 = 70
UC_SPARC_REG_L1 = 71
UC_SPARC_REG_L2 = 72
UC_SPARC_REG_L3 = 73
UC_SPARC_REG_L4 = 74
UC_SPARC_REG_L5 = 75
UC_SPARC_REG_L6 = 76
UC_SPARC_REG_L7 = 77
UC_SPARC_REG_O0 = 78
UC_SPARC_REG_O1 = 79
UC_SPARC_REG_O2 = 80
UC_SPARC_REG_O3 = 81
UC_SPARC_REG_O4 = 82
UC_SPARC_REG_O5 = 83
UC_SPARC_REG_SP = 84
UC_SPARC_REG_O7 = 85
UC_SPARC_REG_Y = 86
UC_SPARC_REG_XCC = 87
UC_SPARC_REG_PC = 88
UC_SPARC_REG_ENDING = 89
UC_SPARC_REG_O6 = 84
UC_SPARC_REG_I6 = 67
end

View file

@ -0,0 +1,98 @@
# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [unicorn_const.rb]
module Unicorn
UC_API_MAJOR = 1
UC_API_MINOR = 0
UC_SECOND_SCALE = 1000000
UC_MILISECOND_SCALE = 1000
UC_ARCH_ARM = 1
UC_ARCH_ARM64 = 2
UC_ARCH_MIPS = 3
UC_ARCH_X86 = 4
UC_ARCH_PPC = 5
UC_ARCH_SPARC = 6
UC_ARCH_M68K = 7
UC_ARCH_MAX = 8
UC_MODE_LITTLE_ENDIAN = 0
UC_MODE_BIG_ENDIAN = 1073741824
UC_MODE_ARM = 0
UC_MODE_THUMB = 16
UC_MODE_MCLASS = 32
UC_MODE_V8 = 64
UC_MODE_MICRO = 16
UC_MODE_MIPS3 = 32
UC_MODE_MIPS32R6 = 64
UC_MODE_MIPS32 = 4
UC_MODE_MIPS64 = 8
UC_MODE_16 = 2
UC_MODE_32 = 4
UC_MODE_64 = 8
UC_MODE_PPC32 = 4
UC_MODE_PPC64 = 8
UC_MODE_QPX = 16
UC_MODE_SPARC32 = 4
UC_MODE_SPARC64 = 8
UC_MODE_V9 = 16
UC_ERR_OK = 0
UC_ERR_NOMEM = 1
UC_ERR_ARCH = 2
UC_ERR_HANDLE = 3
UC_ERR_MODE = 4
UC_ERR_VERSION = 5
UC_ERR_READ_UNMAPPED = 6
UC_ERR_WRITE_UNMAPPED = 7
UC_ERR_FETCH_UNMAPPED = 8
UC_ERR_HOOK = 9
UC_ERR_INSN_INVALID = 10
UC_ERR_MAP = 11
UC_ERR_WRITE_PROT = 12
UC_ERR_READ_PROT = 13
UC_ERR_FETCH_PROT = 14
UC_ERR_ARG = 15
UC_ERR_READ_UNALIGNED = 16
UC_ERR_WRITE_UNALIGNED = 17
UC_ERR_FETCH_UNALIGNED = 18
UC_ERR_HOOK_EXIST = 19
UC_ERR_RESOURCE = 20
UC_MEM_READ = 16
UC_MEM_WRITE = 17
UC_MEM_FETCH = 18
UC_MEM_READ_UNMAPPED = 19
UC_MEM_WRITE_UNMAPPED = 20
UC_MEM_FETCH_UNMAPPED = 21
UC_MEM_WRITE_PROT = 22
UC_MEM_READ_PROT = 23
UC_MEM_FETCH_PROT = 24
UC_HOOK_INTR = 1
UC_HOOK_INSN = 2
UC_HOOK_CODE = 4
UC_HOOK_BLOCK = 8
UC_HOOK_MEM_READ_UNMAPPED = 16
UC_HOOK_MEM_WRITE_UNMAPPED = 32
UC_HOOK_MEM_FETCH_UNMAPPED = 64
UC_HOOK_MEM_READ_PROT = 128
UC_HOOK_MEM_WRITE_PROT = 256
UC_HOOK_MEM_FETCH_PROT = 512
UC_HOOK_MEM_READ = 1024
UC_HOOK_MEM_WRITE = 2048
UC_HOOK_MEM_FETCH = 4096
UC_HOOK_MEM_UNMAPPED = 112
UC_HOOK_MEM_PROT = 896
UC_HOOK_MEM_READ_INVALID = 144
UC_HOOK_MEM_WRITE_INVALID = 288
UC_HOOK_MEM_FETCH_INVALID = 576
UC_HOOK_MEM_INVALID = 1008
UC_HOOK_MEM_VALID = 7168
UC_QUERY_MODE = 1
UC_QUERY_PAGE_SIZE = 2
UC_PROT_NONE = 0
UC_PROT_READ = 1
UC_PROT_WRITE = 2
UC_PROT_EXEC = 4
UC_PROT_ALL = 7
end

View file

@ -0,0 +1,3 @@
module Unicorn
VERSION = "1.0.0"
end

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,10 @@
/.bundle/
/.yardoc
/Gemfile.lock
/_yardoc/
/coverage/
/doc/
/pkg/
/spec/reports/
/tmp/
*.gem

View file

@ -0,0 +1,21 @@
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'unicorn/version'
Gem::Specification.new do |spec|
spec.name = "unicorn"
spec.version = Unicorn::VERSION
spec.authors = ["Sascha Schirra"]
spec.email = ["sashs@scoding.de"]
spec.license = 'GPL-2.0'
spec.summary = %q{Ruby binding for Unicorn-Engine}
spec.description = %q{Ruby binding for Unicorn-Engine <unicorn-engine.org>}
spec.homepage = "https://unicorn-engine.org"
spec.files = Dir["lib/unicorn/*.rb"] + Dir["ext/unicorn.c"] + Dir["ext/unicorn.h"] + Dir["ext/extconf.rb"]
spec.require_paths = ["lib","ext"]
spec.extensions = ["ext/extconf.rb"]
spec.add_development_dependency "bundler", "~> 1.11"
spec.add_development_dependency "rake", "~> 10.0"
end

170
docs/COMPILE-NIX.md Normal file
View file

@ -0,0 +1,170 @@
This documentation explains how to compile, install & run Unicorn on MacOSX,
Linux, BSD, Solaris, Android & iOS.
To compile for Microsoft Windows, see [COMPILE-WINDOWS.md](COMPILE-WINDOWS.md)
----
[0] Dependencies
Unicorn requires few dependent packages as follows.
- For Mac OS X, "pkg-config" and "glib" are needed.
Brew users can install "pkg-config" and "glib" with:
$ brew install pkg-config glib
- For Linux, "glib2-dev" is needed.
Ubuntu/Debian users can install this with:
$ sudo apt-get install libglib2.0-dev
[1] Tailor Unicorn to your need.
Out of 6 archtitectures supported by Unicorn (Arm, Arm64, M68K, Mips, Sparc,
& X86), if you just need several selected archs, choose which ones you want
to compile in by editing "config.mk" before going to next steps.
By default, all 6 architectures are compiled.
The other way of customize Unicorn without having to edit config.mk is to
pass the desired options on the commandline to ./make.sh. Currently,
Unicorn supports 4 options, as follows.
- UNICORN_ARCHS: specify list of architectures to compiled in.
- UNICORN_STATIC: build static library.
- UNICORN_SHARED: build dynamic (shared) library.
- UNICORN_QEMU_FLAGS: specify extra flags for qemu's configure script
To avoid editing config.mk for these customization, we can pass their values to
make.sh, as follows.
$ UNICORN_ARCHS="arm aarch64 x86" ./make.sh
NOTE: on commandline, put these values in front of ./make.sh, not after it.
For each option, refer to docs/README for more details.
[2] Compile and install from source on *nix
To build Unicorn on *nix (such as MacOSX, Linux, *BSD, Solaris):
- To compile for current platform, run:
$ ./make.sh
- Unicorn requires Python 2.x to compile. If Python 2.x is not the default
Python interpreter, ensure that the appropriate option is set:
$ UNICORN_QEMU_FLAGS="--python=/path/to/python2" ./make.sh
- To cross-compile Unicorn on 64-bit OS to target 32-bit binary, run:
$ ./make.sh nix32
After compiling, install Unicorn with:
$ sudo ./make.sh install
For FreeBSD/OpenBSD, where sudo is unavailable, run:
$ su; ./make.sh install
Users are then required to enter root password to copy Unicorn into machine
system directories.
Afterwards, run ./samples/sample_all.sh to test the sample emulations.
NOTE: The core framework installed by "./make.sh install" consist of
following files:
/usr/include/unicorn/unicorn.h
/usr/include/unicorn/x86.h
/usr/include/unicorn/arm.h
/usr/include/unicorn/arm64.h
/usr/include/unicorn/mips.h
/usr/include/unicorn/ppc.h
/usr/include/unicorn/sparc.h
/usr/include/unicorn/m68k.h
/usr/lib/libunicorn.so (for Linux/*nix), or /usr/lib/libunicorn.dylib (OSX)
/usr/lib/libunicorn.a
[3] Cross-compile for iOS from Mac OSX.
To cross-compile for iOS (iPhone/iPad/iPod), Mac OSX with XCode installed is required.
- To cross-compile for ArmV7 (iPod 4, iPad 1/2/3, iPhone4, iPhone4S), run:
$ ./make.sh ios_armv7
- To cross-compile for ArmV7s (iPad 4, iPhone 5C, iPad mini), run:
$ ./make.sh ios_armv7s
- To cross-compile for Arm64 (iPhone 5S, iPad mini Retina, iPad Air), run:
$ ./make.sh ios_arm64
- To cross-compile for all iDevices (armv7 + armv7s + arm64), run:
$ ./make.sh ios
Resulted files libunicorn.dylib, libunicorn.a & tests/test* can then
be used on iOS devices.
[4] Cross-compile for Android
To cross-compile for Android (smartphone/tablet), Android NDK is required.
NOTE: Only ARM and ARM64 are currently supported.
$ NDK=/android/android-ndk-r10e ./make.sh cross-android arm
or
$ NDK=/android/android-ndk-r10e ./make.sh cross-android arm64
Resulted files libunicorn.so, libunicorn.a & tests/test* can then
be used on Android devices.
[5] By default, "cc" (default C compiler on the system) is used as compiler.
- To use "clang" compiler instead, run the command below:
$ ./make.sh clang
- To use "gcc" compiler instead, run:
$ ./make.sh gcc
[6] To uninstall Unicorn, run the command below:
$ sudo ./make.sh uninstall
[7] Language bindings
Look for the bindings under directory bindings/, and refer to README file
of corresponding languages.
[8] Unit tests
Automated unit tests use the cmocka unit testing framework (https://cmocka.org/).
It can be installed in most Linux distros using the package manager, e.g.
`sudo yum install libcmocka libcmocka-devel`, or you can easily build and install it from source.
You can run the tests by running `make test` in the project directory.

201
docs/COMPILE-WINDOWS.md Normal file
View file

@ -0,0 +1,201 @@
We also show steps to cross-compile Unicorn for Microsoft Windows.
To compile for Linux, Mac OS X and Unix-based OS, see [COMPILE-NIX.md](COMPILE-NIX.md)
---
[0] Dependencies
For Windows, cross-compile requires Mingw. Mingw-glib2 is needed.
At the moment, it is confirmed that Unicorn can be compiled either on Ubuntu
or Windows.
- On Ubuntu 14.04 64-bit, do:
- Download DEB packages for Mingw64 from:
https://launchpad.net/~greg-hellings/+archive/ubuntu/mingw-libs/+build/2924251
- To cross-compile for Windows 32-bit, install Mingw with (ignore all the warnings):
$ sudo dpkg -i --force-depends mingw64-x86-glib2_2.31.0_all.deb
To cross-compile for Windows 64-bit, install Mingw with:
$ sudo dpkg -i --force-depends mingw64-x64-glib2_2.31.0_all.deb
- On Windows, install MinGW via package MSYS2 at https://msys2.github.io/
Follow the install instructions and don't forget to update the system packages with:
$ pacman --needed -Sy bash pacman pacman-mirrors msys2-runtime
Then close MSYS2, run it again from Start menu and update the rest with:
$ pacman -Su
Finally, install required toolchain to build C projects.
- To compile for Windows 32-bit, run:
$ pacman -S python2
$ pacman -S make
$ pacman -S pkg-config
$ pacman -S mingw-w64-i686-glib2
$ pacman -S mingw-w64-i686-toolchain
- To compile for Windows 64-bit, run:
$ pacman -S python2
$ pacman -S make
$ pacman -S pkg-config
$ pacman -S mingw-w64-x86_64-glib2
$ pacman -S mingw-w64-x86_64-toolchain
- For Cygwin, "make", "gcc-core", "pkg-config", "libpcre-devel", "zlib-devel"
and "libglib2.0-devel" are needed.
If apt-cyg is available, you can install these with:
$ apt-cyg install make gcc-core pkg-config libpcre-devel zlib-devel libglib2.0-devel
[1] Tailor Unicorn to your need.
Out of 6 archtitectures supported by Unicorn (Arm, Arm64, M68K, Mips, Sparc,
& X86), if you just need several selected archs, choose which ones you want
to compile in by editing "config.mk" before going to next steps.
By default, all 6 architectures are compiled.
The other way of customize Unicorn without having to edit config.mk is to
pass the desired options on the commandline to ./make.sh. Currently,
Unicorn supports 4 options, as follows.
- UNICORN_ARCHS: specify list of architectures to compiled in.
- UNICORN_STATIC: build static library.
- UNICORN_SHARED: build dynamic (shared) library.
- UNICORN_QEMU_FLAGS: specify extra flags for qemu's configure script
To avoid editing config.mk for these customization, we can pass their values to
make.sh, as follows.
$ UNICORN_ARCHS="arm aarch64 x86" ./make.sh
NOTE: on commandline, put these values in front of ./make.sh, not after it.
For each option, refer to docs/README for more details.
[2] Compile from source on Windows - with MinGW (MSYS2)
To compile with MinGW, install MSYS2 as instructed in the first section.
Then, build Unicorn with the next steps:
- To compile Windows 32-bit binary with MinGW, run:
$ ./make.sh cross-win32
- To compile Windows 64-bit binary with MinGW, run:
$ ./make.sh cross-win64
Resulted files unicorn.dll, unicorn.lib & samples/sample*.exe can then
be used on Windows machine.
To run sample_x86.exe on Windows 32-bit, you need the following files:
unicorn.dll
%MSYS2%\mingw32\bin\libiconv-2.dll
%MSYS2%\mingw32\bin\libintl-8.dll
%MSYS2%\mingw32\bin\libglib-2.0-0.dll
%MSYS2%\mingw32\bin\libgcc_s_dw2-1.dll
%MSYS2%\mingw32\bin\libwinpthread-1.dll
To run sample_x86.exe on Windows 64-bit, you need the following files:
unicorn.dll
%MSYS2%\mingw64\bin\libiconv-2.dll
%MSYS2%\mingw64\bin\libintl-8.dll
%MSYS2%\mingw64\bin\libglib-2.0-0.dll
%MSYS2%\mingw64\bin\libgcc_s_seh-1.dll
%MSYS2%\mingw64\bin\libwinpthread-1.dll
[3] Compile and install from source on Cygwin
To build Unicorn on Cygwin, run:
$ ./make.sh
After compiling, install Unicorn with:
$ ./make.sh install
Resulted files cygunicorn.dll, libunicorn.dll.a and libunicorn.a can be
used on Cygwin but not native Windows.
NOTE: The core framework installed by "./make.sh install" consist of
following files:
/usr/include/unicorn/*.h
/usr/bin/cygunicorn.dll
/usr/lib/libunicorn.dll.a
/usr/lib/libunicorn.a
[4] Cross-compile for Windows from *nix
To cross-compile for Windows, Linux & gcc-mingw-w64-i686 (and also gcc-mingw-w64-x86-64
for 64-bit binaries) are required.
- To cross-compile Windows 32-bit binary, simply run:
$ ./make.sh cross-win32
- To cross-compile Windows 64-bit binary, run:
$ ./make.sh cross-win64
Resulted files unicorn.dll, unicorn.lib & samples/sample*.exe can then
be used on Windows machine.
To run sample_x86.exe on Windows 32-bit, you need the following files:
unicorn.dll
/usr/i686-w64-mingw32/sys-root/mingw/bin/libglib-2.0-0.dll
/usr/lib/gcc/i686-w64-mingw32/4.8/libgcc_s_sjlj-1.dll
/usr/i686-w64-mingw32/lib/libwinpthread-1.dll
To run sample_x86.exe on Windows 64-bit, you need the following files:
unicorn.dll
/usr/x86_64-w64-mingw32/sys-root/mingw/bin/libglib-2.0-0.dll
/usr/lib/gcc/x86_64-w64-mingw32/4.8/libgcc_s_sjlj-1.dll
/usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll
Then run either "sample_x86.exe -32" or "sample_x86.exe -64" to test emulators for X86 32-bit or X86 64-bit.
For other architectures, run "sample_xxx.exe" found in the same directory.
[5] Language bindings
Look for the bindings under directory bindings/, and refer to README file
of corresponding languages.
[6] Unit tests
Automated unit tests use the cmocka unit testing framework (https://cmocka.org/).
It can be installed in most Linux distros using the package manager, e.g.
`sudo yum install libcmocka libcmocka-devel`, or you can easily build and install it from source.
You can run the tests by running `make test` in the project directory.

10
docs/COMPILE.md Normal file
View file

@ -0,0 +1,10 @@
To compile Unicorn on Mac OS X, Linux, BSD, Solaris and all kind of nix OS,
see [COMPILE-NIX.md](COMPILE-NIX.md)
To compile Unicorn on Windows, see [COMPILE-WINDOWS.md](COMPILE-WINDOWS.md)
Then learn more on how to code your own tools with our samples.
- For C sample code, see code in directory samples/sample*.c
- For Python sample code, see code in directory bindings/python/sample*.py
- For samples of other bindings, look into directories bindings/<language>/

View file

@ -22,6 +22,17 @@
#define ARR_SIZE(a) (sizeof(a)/sizeof(a[0])) #define ARR_SIZE(a) (sizeof(a)/sizeof(a[0]))
#define READ_QWORD(x) ((uint64)x)
#define READ_DWORD(x) (x & 0xffffffff)
#define READ_WORD(x) (x & 0xffff)
#define READ_BYTE_H(x) ((x & 0xffff) >> 8)
#define READ_BYTE_L(x) (x & 0xff)
#define WRITE_DWORD(x, w) (x = (x & ~0xffffffff) | (w & 0xffffffff))
#define WRITE_WORD(x, w) (x = (x & ~0xffff) | (w & 0xffff))
#define WRITE_BYTE_H(x, b) (x = (x & ~0xff00) | ((b & 0xff) << 8))
#define WRITE_BYTE_L(x, b) (x = (x & ~0xff) | (b & 0xff))
QTAILQ_HEAD(CPUTailQ, CPUState); QTAILQ_HEAD(CPUTailQ, CPUState);
typedef struct ModuleEntry { typedef struct ModuleEntry {
@ -35,8 +46,8 @@ typedef QTAILQ_HEAD(, ModuleEntry) ModuleTypeList;
typedef uc_err (*query_t)(struct uc_struct *uc, uc_query_type type, size_t *result); typedef uc_err (*query_t)(struct uc_struct *uc, uc_query_type type, size_t *result);
// return 0 on success, -1 on failure // return 0 on success, -1 on failure
typedef int (*reg_read_t)(struct uc_struct *uc, unsigned int regid, void *value); typedef int (*reg_read_t)(struct uc_struct *uc, unsigned int *regs, void **vals, int count);
typedef int (*reg_write_t)(struct uc_struct *uc, unsigned int regid, const void *value); typedef int (*reg_write_t)(struct uc_struct *uc, unsigned int *regs, void *const *vals, int count);
typedef void (*reg_reset_t)(struct uc_struct *uc); typedef void (*reg_reset_t)(struct uc_struct *uc);
@ -136,7 +147,6 @@ struct uc_struct {
uc_mode mode; uc_mode mode;
QemuMutex qemu_global_mutex; // qemu/cpus.c QemuMutex qemu_global_mutex; // qemu/cpus.c
QemuCond qemu_cpu_cond; // qemu/cpus.c QemuCond qemu_cpu_cond; // qemu/cpus.c
QemuThread *tcg_cpu_thread; // qemu/cpus.c
QemuCond *tcg_halt_cond; // qemu/cpus.c QemuCond *tcg_halt_cond; // qemu/cpus.c
struct CPUTailQ cpus; // qemu/cpu-exec.c struct CPUTailQ cpus; // qemu/cpu-exec.c
uc_err errnum; // qemu/cpu-exec.c uc_err errnum; // qemu/cpu-exec.c
@ -152,7 +162,7 @@ struct uc_struct {
uc_args_uc_u64_t set_pc; // set PC for tracecode uc_args_uc_u64_t set_pc; // set PC for tracecode
uc_args_int_t stop_interrupt; // check if the interrupt should stop emulation uc_args_int_t stop_interrupt; // check if the interrupt should stop emulation
uc_args_uc_t init_arch, pause_all_vcpus, cpu_exec_init_all; uc_args_uc_t init_arch, cpu_exec_init_all;
uc_args_int_uc_t vm_start; uc_args_int_uc_t vm_start;
uc_args_tcg_enable_t tcg_enabled; uc_args_tcg_enable_t tcg_enabled;
uc_args_uc_long_t tcg_exec_init; uc_args_uc_long_t tcg_exec_init;
@ -188,10 +198,12 @@ struct uc_struct {
QemuMutex flat_view_mutex; QemuMutex flat_view_mutex;
QTAILQ_HEAD(memory_listeners, MemoryListener) memory_listeners; QTAILQ_HEAD(memory_listeners, MemoryListener) memory_listeners;
QTAILQ_HEAD(, AddressSpace) address_spaces; QTAILQ_HEAD(, AddressSpace) address_spaces;
MachineState *machine_state;
// qom/object.c // qom/object.c
GHashTable *type_table; GHashTable *type_table;
Type type_interface; Type type_interface;
Object *root; Object *root;
Object *owner;
bool enumerating_types; bool enumerating_types;
// util/module.c // util/module.c
ModuleTypeList init_type_list[MODULE_INIT_MAX]; ModuleTypeList init_type_list[MODULE_INIT_MAX];

View file

@ -9,7 +9,6 @@ extern "C" {
#endif #endif
#include <stdint.h> #include <stdint.h>
#include "platform.h"
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(disable:4201) #pragma warning(disable:4201)

View file

@ -1,28 +0,0 @@
/* Unicorn Emulator Engine */
/* By Axel Souchet & Nguyen Anh Quynh, 2014 */
// handle C99 issue (for pre-2013 VisualStudio)
#ifndef UNICORN_PLATFORM_H
#define UNICORN_PLATFORM_H
#if !defined(__MINGW32__) && !defined(__MINGW64__) && (defined (WIN32) || defined (WIN64) || defined (_WIN32) || defined (_WIN64))
// MSVC
// stdbool.h
#if (_MSC_VER < 1800)
#ifndef __cplusplus
typedef unsigned char bool;
#define false 0
#define true 1
#endif
#else
// VisualStudio 2013+ -> C99 is supported
#include <stdbool.h>
#endif
#else // not MSVC -> C99 is supported
#include <stdbool.h>
#endif
#endif

View file

@ -9,7 +9,6 @@ extern "C" {
#endif #endif
#include <stdint.h> #include <stdint.h>
#include "platform.h"
// GCC SPARC toolchain has a default macro called "sparc" which breaks // GCC SPARC toolchain has a default macro called "sparc" which breaks
// compilation // compilation

View file

@ -9,6 +9,7 @@ extern "C" {
#endif #endif
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <stdarg.h> #include <stdarg.h>
#if defined(UNICORN_HAS_OSXKERNEL) #if defined(UNICORN_HAS_OSXKERNEL)
#include <libkern/libkern.h> #include <libkern/libkern.h>
@ -17,8 +18,6 @@ extern "C" {
#include <stdio.h> #include <stdio.h>
#endif #endif
#include "platform.h"
struct uc_struct; struct uc_struct;
typedef struct uc_struct uc_engine; typedef struct uc_struct uc_engine;
@ -262,6 +261,7 @@ typedef struct uc_mem_region {
typedef enum uc_query_type { typedef enum uc_query_type {
// Dynamically query current hardware mode. // Dynamically query current hardware mode.
UC_QUERY_MODE = 1, UC_QUERY_MODE = 1,
UC_QUERY_PAGE_SIZE,
} uc_query_type; } uc_query_type;
/* /*
@ -385,6 +385,34 @@ uc_err uc_reg_write(uc_engine *uc, int regid, const void *value);
UNICORN_EXPORT UNICORN_EXPORT
uc_err uc_reg_read(uc_engine *uc, int regid, void *value); uc_err uc_reg_read(uc_engine *uc, int regid, void *value);
/*
Write multiple register values.
@uc: handle returned by uc_open()
@rges: array of register IDs to store
@value: pointer to array of register values
@count: length of both *regs and *vals
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_reg_write_batch(uc_engine *uc, int *regs, void *const *vals, int count);
/*
Read multiple register values.
@uc: handle returned by uc_open()
@rges: array of register IDs to retrieve
@value: pointer to array of values to hold registers
@count: length of both *regs and *vals
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_reg_read_batch(uc_engine *uc, int *regs, void **vals, int count);
/* /*
Write to a range of bytes in memory. Write to a range of bytes in memory.
@ -458,7 +486,7 @@ uc_err uc_emu_stop(uc_engine *uc);
@user_data: user-defined data. This will be passed to callback function in its @user_data: user-defined data. This will be passed to callback function in its
last argument @user_data last argument @user_data
@begin: start address of the area where the callback is effect (inclusive) @begin: start address of the area where the callback is effect (inclusive)
@begin: end address of the area where the callback is effect (inclusive) @end: end address of the area where the callback is effect (inclusive)
NOTE 1: the callback is called only if related address is in range [@begin, @end] NOTE 1: the callback is called only if related address is in range [@begin, @end]
NOTE 2: if @begin > @end, callback is called whenever this hook type is triggered NOTE 2: if @begin > @end, callback is called whenever this hook type is triggered
@...: variable arguments (depending on @type) @...: variable arguments (depending on @type)

View file

@ -8,6 +8,8 @@
extern "C" { extern "C" {
#endif #endif
#include <stdint.h>
// Memory-Management Register for instructions IDTR, GDTR, LDTR, TR. // Memory-Management Register for instructions IDTR, GDTR, LDTR, TR.
// Borrow from SegmentCache in qemu/target-i386/cpu.h // Borrow from SegmentCache in qemu/target-i386/cpu.h
typedef struct uc_x86_mmr { typedef struct uc_x86_mmr {
@ -73,7 +75,8 @@ typedef enum uc_x86_reg {
UC_X86_REG_R9D, UC_X86_REG_R10D, UC_X86_REG_R11D, UC_X86_REG_R12D, UC_X86_REG_R13D, UC_X86_REG_R9D, UC_X86_REG_R10D, UC_X86_REG_R11D, UC_X86_REG_R12D, UC_X86_REG_R13D,
UC_X86_REG_R14D, UC_X86_REG_R15D, UC_X86_REG_R8W, UC_X86_REG_R9W, UC_X86_REG_R10W, UC_X86_REG_R14D, UC_X86_REG_R15D, UC_X86_REG_R8W, UC_X86_REG_R9W, UC_X86_REG_R10W,
UC_X86_REG_R11W, UC_X86_REG_R12W, UC_X86_REG_R13W, UC_X86_REG_R14W, UC_X86_REG_R15W, UC_X86_REG_R11W, UC_X86_REG_R12W, UC_X86_REG_R13W, UC_X86_REG_R14W, UC_X86_REG_R15W,
UC_X86_REG_IDTR, UC_X86_REG_GDTR, UC_X86_REG_LDTR, UC_X86_REG_TR, UC_X86_REG_IDTR, UC_X86_REG_GDTR, UC_X86_REG_LDTR, UC_X86_REG_TR, UC_X86_REG_FPCW,
UC_X86_REG_FPTAG,
UC_X86_REG_ENDING // <-- mark the end of the list of registers UC_X86_REG_ENDING // <-- mark the end of the list of registers
} uc_x86_reg; } uc_x86_reg;

2
list.c
View file

@ -54,6 +54,8 @@ bool list_remove(struct list *list, void *data)
if (cur->data == data) { if (cur->data == data) {
if (cur == list->head) { if (cur == list->head) {
list->head = next; list->head = next;
} else {
prev->next = next;
} }
if (cur == list->tail) { if (cur == list->tail) {
list->tail = prev; list->tail = prev;

14
make.sh
View file

@ -10,6 +10,13 @@ MAKE_JOBS=$((${MAKE_JOBS}+0))
[ ${MAKE_JOBS} -lt 1 ] && \ [ ${MAKE_JOBS} -lt 1 ] && \
MAKE_JOBS=4 MAKE_JOBS=4
# build for ASAN
asan() {
UNICORN_DEBUG=yes
UNICORN_ASAN=yes
${MAKE} V=1
}
# build iOS lib for all iDevices, or only specific device # build iOS lib for all iDevices, or only specific device
build_iOS() { build_iOS() {
IOS_SDK=`xcrun --sdk iphoneos --show-sdk-path` IOS_SDK=`xcrun --sdk iphoneos --show-sdk-path`
@ -51,7 +58,7 @@ install() {
rm -rf /usr/lib/libunicorn* rm -rf /usr/lib/libunicorn*
rm -rf /usr/include/unicorn rm -rf /usr/include/unicorn
# install into /usr/local # install into /usr/local
PREFIX=/usr/local PREFIX="${PREFIX-/usr/local}"
${MAKE} install ${MAKE} install
else # not OSX else # not OSX
test -d /usr/lib64 && LIBDIRARCH=lib64 test -d /usr/lib64 && LIBDIRARCH=lib64
@ -64,7 +71,7 @@ uninstall() {
if [ "$UNAME" = "Darwin" ]; then if [ "$UNAME" = "Darwin" ]; then
# find the directory automatically, so we can support both Macport & Brew # find the directory automatically, so we can support both Macport & Brew
PKGCFGDIR="$(pkg-config --variable pc_path pkg-config | cut -d ':' -f 1)" PKGCFGDIR="$(pkg-config --variable pc_path pkg-config | cut -d ':' -f 1)"
PREFIX=/usr/local PREFIX="${PREFIX-/usr/local}"
${MAKE} uninstall ${MAKE} uninstall
else # not OSX else # not OSX
test -d /usr/lib64 && LIBDIRARCH=lib64 test -d /usr/lib64 && LIBDIRARCH=lib64
@ -80,7 +87,7 @@ fi
if [ -n "`echo "$UNAME" | grep BSD`" ]; then if [ -n "`echo "$UNAME" | grep BSD`" ]; then
MAKE=gmake MAKE=gmake
PREFIX=/usr/local PREFIX="${PREFIX-/usr/local}"
fi fi
[ -z "${UNAME}" ] && UNAME=$(uname) [ -z "${UNAME}" ] && UNAME=$(uname)
@ -90,6 +97,7 @@ export CC INSTALL_BIN PREFIX PKGCFGDIR LIBDIRARCH LIBARCHS CFLAGS LDFLAGS
case "$1" in case "$1" in
"" ) build;; "" ) build;;
"asan" ) asan;;
"default" ) build;; "default" ) build;;
"install" ) install;; "install" ) install;;
"uninstall" ) uninstall;; "uninstall" ) uninstall;;

View file

@ -19,7 +19,7 @@ ifeq ($(CONFIG_SOFTMMU),y)
common-obj-y += hw/ common-obj-y += hw/
common-obj-y += accel.o common-obj-y += accel.o
common-obj-y += vl.o main-loop.o qemu-timer.o common-obj-y += vl.o qemu-timer.o
endif endif

View file

@ -2258,7 +2258,6 @@
#define parse_value parse_value_aarch64 #define parse_value parse_value_aarch64
#define par_write par_write_aarch64 #define par_write par_write_aarch64
#define patch_reloc patch_reloc_aarch64 #define patch_reloc patch_reloc_aarch64
#define pause_all_vcpus pause_all_vcpus_aarch64
#define phys_map_node_alloc phys_map_node_alloc_aarch64 #define phys_map_node_alloc phys_map_node_alloc_aarch64
#define phys_map_node_reserve phys_map_node_reserve_aarch64 #define phys_map_node_reserve phys_map_node_reserve_aarch64
#define phys_mem_alloc phys_mem_alloc_aarch64 #define phys_mem_alloc phys_mem_alloc_aarch64
@ -2417,9 +2416,6 @@
#define qemu_clock_get_us qemu_clock_get_us_aarch64 #define qemu_clock_get_us qemu_clock_get_us_aarch64
#define qemu_clock_ptr qemu_clock_ptr_aarch64 #define qemu_clock_ptr qemu_clock_ptr_aarch64
#define qemu_clocks qemu_clocks_aarch64 #define qemu_clocks qemu_clocks_aarch64
#define qemu_cond_destroy qemu_cond_destroy_aarch64
#define qemu_cpu_is_self qemu_cpu_is_self_aarch64
#define qemu_cpu_kick_thread qemu_cpu_kick_thread_aarch64
#define qemu_daemon qemu_daemon_aarch64 #define qemu_daemon qemu_daemon_aarch64
#define qemu_event_destroy qemu_event_destroy_aarch64 #define qemu_event_destroy qemu_event_destroy_aarch64
#define qemu_event_init qemu_event_init_aarch64 #define qemu_event_init qemu_event_init_aarch64
@ -2445,7 +2441,6 @@
#define qemu_log_flush qemu_log_flush_aarch64 #define qemu_log_flush qemu_log_flush_aarch64
#define qemu_loglevel_mask qemu_loglevel_mask_aarch64 #define qemu_loglevel_mask qemu_loglevel_mask_aarch64
#define qemu_log_vprintf qemu_log_vprintf_aarch64 #define qemu_log_vprintf qemu_log_vprintf_aarch64
#define qemu_mutex_destroy qemu_mutex_destroy_aarch64
#define qemu_mutex_lock_ramlist qemu_mutex_lock_ramlist_aarch64 #define qemu_mutex_lock_ramlist qemu_mutex_lock_ramlist_aarch64
#define qemu_mutex_trylock qemu_mutex_trylock_aarch64 #define qemu_mutex_trylock qemu_mutex_trylock_aarch64
#define qemu_mutex_unlock_ramlist qemu_mutex_unlock_ramlist_aarch64 #define qemu_mutex_unlock_ramlist qemu_mutex_unlock_ramlist_aarch64
@ -2516,9 +2511,7 @@
#define qemu_st_helpers qemu_st_helpers_aarch64 #define qemu_st_helpers qemu_st_helpers_aarch64
#define qemu_strnlen qemu_strnlen_aarch64 #define qemu_strnlen qemu_strnlen_aarch64
#define qemu_strsep qemu_strsep_aarch64 #define qemu_strsep qemu_strsep_aarch64
#define qemu_tcg_cpu_thread_fn qemu_tcg_cpu_thread_fn_aarch64
#define qemu_tcg_init_vcpu qemu_tcg_init_vcpu_aarch64 #define qemu_tcg_init_vcpu qemu_tcg_init_vcpu_aarch64
#define qemu_thread_exit qemu_thread_exit_aarch64
#define qemu_try_memalign qemu_try_memalign_aarch64 #define qemu_try_memalign qemu_try_memalign_aarch64
#define qentry_destroy qentry_destroy_aarch64 #define qentry_destroy qentry_destroy_aarch64
#define qerror_human qerror_human_aarch64 #define qerror_human qerror_human_aarch64

View file

@ -2258,7 +2258,6 @@
#define parse_value parse_value_arm #define parse_value parse_value_arm
#define par_write par_write_arm #define par_write par_write_arm
#define patch_reloc patch_reloc_arm #define patch_reloc patch_reloc_arm
#define pause_all_vcpus pause_all_vcpus_arm
#define phys_map_node_alloc phys_map_node_alloc_arm #define phys_map_node_alloc phys_map_node_alloc_arm
#define phys_map_node_reserve phys_map_node_reserve_arm #define phys_map_node_reserve phys_map_node_reserve_arm
#define phys_mem_alloc phys_mem_alloc_arm #define phys_mem_alloc phys_mem_alloc_arm
@ -2417,9 +2416,6 @@
#define qemu_clock_get_us qemu_clock_get_us_arm #define qemu_clock_get_us qemu_clock_get_us_arm
#define qemu_clock_ptr qemu_clock_ptr_arm #define qemu_clock_ptr qemu_clock_ptr_arm
#define qemu_clocks qemu_clocks_arm #define qemu_clocks qemu_clocks_arm
#define qemu_cond_destroy qemu_cond_destroy_arm
#define qemu_cpu_is_self qemu_cpu_is_self_arm
#define qemu_cpu_kick_thread qemu_cpu_kick_thread_arm
#define qemu_daemon qemu_daemon_arm #define qemu_daemon qemu_daemon_arm
#define qemu_event_destroy qemu_event_destroy_arm #define qemu_event_destroy qemu_event_destroy_arm
#define qemu_event_init qemu_event_init_arm #define qemu_event_init qemu_event_init_arm
@ -2445,7 +2441,6 @@
#define qemu_log_flush qemu_log_flush_arm #define qemu_log_flush qemu_log_flush_arm
#define qemu_loglevel_mask qemu_loglevel_mask_arm #define qemu_loglevel_mask qemu_loglevel_mask_arm
#define qemu_log_vprintf qemu_log_vprintf_arm #define qemu_log_vprintf qemu_log_vprintf_arm
#define qemu_mutex_destroy qemu_mutex_destroy_arm
#define qemu_mutex_lock_ramlist qemu_mutex_lock_ramlist_arm #define qemu_mutex_lock_ramlist qemu_mutex_lock_ramlist_arm
#define qemu_mutex_trylock qemu_mutex_trylock_arm #define qemu_mutex_trylock qemu_mutex_trylock_arm
#define qemu_mutex_unlock_ramlist qemu_mutex_unlock_ramlist_arm #define qemu_mutex_unlock_ramlist qemu_mutex_unlock_ramlist_arm
@ -2516,9 +2511,7 @@
#define qemu_st_helpers qemu_st_helpers_arm #define qemu_st_helpers qemu_st_helpers_arm
#define qemu_strnlen qemu_strnlen_arm #define qemu_strnlen qemu_strnlen_arm
#define qemu_strsep qemu_strsep_arm #define qemu_strsep qemu_strsep_arm
#define qemu_tcg_cpu_thread_fn qemu_tcg_cpu_thread_fn_arm
#define qemu_tcg_init_vcpu qemu_tcg_init_vcpu_arm #define qemu_tcg_init_vcpu qemu_tcg_init_vcpu_arm
#define qemu_thread_exit qemu_thread_exit_arm
#define qemu_try_memalign qemu_try_memalign_arm #define qemu_try_memalign qemu_try_memalign_arm
#define qentry_destroy qentry_destroy_arm #define qentry_destroy qentry_destroy_arm
#define qerror_human qerror_human_arm #define qerror_human qerror_human_arm

View file

@ -28,6 +28,7 @@
#include "config-host.h" #include "config-host.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "sysemu/cpus.h" #include "sysemu/cpus.h"
#include "qemu/thread.h"
#include "exec/address-spaces.h" // debug, can be removed later #include "exec/address-spaces.h" // debug, can be removed later
@ -38,17 +39,13 @@ static void cpu_handle_guest_debug(CPUState *cpu);
static int tcg_cpu_exec(struct uc_struct *uc, CPUArchState *env); static int tcg_cpu_exec(struct uc_struct *uc, CPUArchState *env);
static bool tcg_exec_all(struct uc_struct* uc); static bool tcg_exec_all(struct uc_struct* uc);
static int qemu_tcg_init_vcpu(CPUState *cpu); static int qemu_tcg_init_vcpu(CPUState *cpu);
static void *qemu_tcg_cpu_thread_fn(void *arg); static void *qemu_tcg_cpu_loop(struct uc_struct *uc);
int vm_start(struct uc_struct* uc) int vm_start(struct uc_struct* uc)
{ {
if (resume_all_vcpus(uc)) { if (resume_all_vcpus(uc)) {
return -1; return -1;
} }
// kick off TCG thread
qemu_mutex_unlock_iothread(uc);
return 0; return 0;
} }
@ -59,28 +56,9 @@ bool cpu_is_stopped(CPUState *cpu)
void run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data) void run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data)
{ {
if (qemu_cpu_is_self(cpu)) {
func(data); func(data);
return;
}
} }
// send halt_cond/tcg_halt_cond to @cpu
bool qemu_cpu_is_self(CPUState *cpu)
{
return qemu_thread_is_self(cpu->thread);
}
void pause_all_vcpus(struct uc_struct *uc)
{
CPUState *cpu;
CPU_FOREACH(cpu) {
qemu_thread_join(cpu->thread); // qq: fix qemu_thread_join() to work for instance
}
}
int resume_all_vcpus(struct uc_struct *uc) int resume_all_vcpus(struct uc_struct *uc)
{ {
CPUState *cpu; CPUState *cpu;
@ -99,7 +77,6 @@ int resume_all_vcpus(struct uc_struct *uc)
if (qemu_init_vcpu(cpu)) if (qemu_init_vcpu(cpu))
return -1; return -1;
} }
qemu_mutex_lock_iothread(uc);
} }
} }
@ -107,6 +84,7 @@ int resume_all_vcpus(struct uc_struct *uc)
CPU_FOREACH(cpu) { CPU_FOREACH(cpu) {
cpu_resume(cpu); cpu_resume(cpu);
} }
qemu_tcg_cpu_loop(uc);
return 0; return 0;
} }
@ -116,7 +94,6 @@ int qemu_init_vcpu(CPUState *cpu)
cpu->nr_cores = smp_cores; cpu->nr_cores = smp_cores;
cpu->nr_threads = smp_threads; cpu->nr_threads = smp_threads;
cpu->stopped = true; cpu->stopped = true;
cpu->uc->tcg_cpu_thread = NULL;
if (tcg_enabled(cpu->uc)) if (tcg_enabled(cpu->uc))
return qemu_tcg_init_vcpu(cpu); return qemu_tcg_init_vcpu(cpu);
@ -125,45 +102,28 @@ int qemu_init_vcpu(CPUState *cpu)
} }
static void *qemu_tcg_cpu_thread_fn(void *arg) static void *qemu_tcg_cpu_loop(struct uc_struct *uc)
{ {
CPUState *cpu = arg; CPUState *cpu;
struct uc_struct *uc = cpu->uc;
//qemu_tcg_init_cpu_signals(); //qemu_tcg_init_cpu_signals();
qemu_thread_get_self(uc, cpu->thread);
qemu_mutex_lock(&uc->qemu_global_mutex); qemu_mutex_lock(&uc->qemu_global_mutex);
CPU_FOREACH(cpu) { CPU_FOREACH(cpu) {
cpu->thread_id = qemu_get_thread_id();
cpu->created = true; cpu->created = true;
} }
qemu_cond_signal(&uc->qemu_cpu_cond); qemu_cond_signal(&uc->qemu_cpu_cond);
/* wait for initial kick-off after machine start */
while (QTAILQ_FIRST(&uc->cpus)->stopped) {
qemu_cond_wait(uc->tcg_halt_cond, &uc->qemu_global_mutex);
}
while (1) { while (1) {
#if 0
int count = 0;
if (count < 10) {
count++;
unsigned int eip = X86_CPU(mycpu)->env.eip;
printf(">>> current EIP = %x\n", eip);
printf(">>> ECX = %x\n", (unsigned int)X86_CPU(mycpu)->env.regs[R_ECX]);
printf(">>> EDX = %x\n", (unsigned int)X86_CPU(mycpu)->env.regs[R_EDX]);
}
#endif
if (tcg_exec_all(uc)) if (tcg_exec_all(uc))
break; break;
} }
CPU_FOREACH(cpu) { CPU_FOREACH(cpu) {
cpu->thread_id = 0;
cpu->created = false; cpu->created = false;
qemu_cond_destroy(cpu->halt_cond);
free(cpu->halt_cond);
cpu->halt_cond = NULL;
} }
qemu_mutex_unlock(&uc->qemu_global_mutex); qemu_mutex_unlock(&uc->qemu_global_mutex);
@ -173,38 +133,16 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
/* For temporary buffers for forming a name */
#define VCPU_THREAD_NAME_SIZE 16
static int qemu_tcg_init_vcpu(CPUState *cpu) static int qemu_tcg_init_vcpu(CPUState *cpu)
{ {
struct uc_struct *uc = cpu->uc; struct uc_struct *uc = cpu->uc;
char thread_name[VCPU_THREAD_NAME_SIZE];
tcg_cpu_address_space_init(cpu, cpu->as); tcg_cpu_address_space_init(cpu, cpu->as);
/* share a single thread for all cpus with TCG */ /* share a single thread for all cpus with TCG */
if (!uc->tcg_cpu_thread) {
cpu->thread = g_malloc0(sizeof(QemuThread));
cpu->halt_cond = g_malloc0(sizeof(QemuCond)); cpu->halt_cond = g_malloc0(sizeof(QemuCond));
qemu_cond_init(cpu->halt_cond); qemu_cond_init(cpu->halt_cond);
uc->tcg_halt_cond = cpu->halt_cond; uc->tcg_halt_cond = cpu->halt_cond;
snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG",
cpu->cpu_index);
if (qemu_thread_create(uc, cpu->thread, thread_name, qemu_tcg_cpu_thread_fn,
cpu, QEMU_THREAD_JOINABLE))
return -1;
#ifdef _WIN32
cpu->hThread = qemu_thread_get_handle(cpu->thread);
#endif
while (!cpu->created) {
qemu_cond_wait(&uc->qemu_cpu_cond, &uc->qemu_global_mutex);
}
uc->tcg_cpu_thread = cpu->thread;
} else {
cpu->thread = uc->tcg_cpu_thread;
cpu->halt_cond = uc->tcg_halt_cond;
}
return 0; return 0;
} }

View file

@ -432,10 +432,7 @@ void cpu_exec_init(CPUArchState *env, void *opaque)
QTAILQ_INIT(&cpu->watchpoints); QTAILQ_INIT(&cpu->watchpoints);
cpu->as = &uc->as; cpu->as = &uc->as;
#ifndef CONFIG_USER_ONLY
//cpu->as = &address_space_memory;
cpu->thread_id = qemu_get_thread_id();
#endif
QTAILQ_INSERT_TAIL(&uc->cpus, cpu, node); QTAILQ_INSERT_TAIL(&uc->cpus, cpu, node);
//QTAILQ_INSERT_TAIL(&uc->cpus, cpu, node); //QTAILQ_INSERT_TAIL(&uc->cpus, cpu, node);
#if defined(CONFIG_USER_ONLY) #if defined(CONFIG_USER_ONLY)

View file

@ -2264,7 +2264,6 @@ symbols = (
'parse_value', 'parse_value',
'par_write', 'par_write',
'patch_reloc', 'patch_reloc',
'pause_all_vcpus',
'phys_map_node_alloc', 'phys_map_node_alloc',
'phys_map_node_reserve', 'phys_map_node_reserve',
'phys_mem_alloc', 'phys_mem_alloc',
@ -2423,9 +2422,6 @@ symbols = (
'qemu_clock_get_us', 'qemu_clock_get_us',
'qemu_clock_ptr', 'qemu_clock_ptr',
'qemu_clocks', 'qemu_clocks',
'qemu_cond_destroy',
'qemu_cpu_is_self',
'qemu_cpu_kick_thread',
'qemu_daemon', 'qemu_daemon',
'qemu_event_destroy', 'qemu_event_destroy',
'qemu_event_init', 'qemu_event_init',
@ -2451,7 +2447,6 @@ symbols = (
'qemu_log_flush', 'qemu_log_flush',
'qemu_loglevel_mask', 'qemu_loglevel_mask',
'qemu_log_vprintf', 'qemu_log_vprintf',
'qemu_mutex_destroy',
'qemu_mutex_lock_ramlist', 'qemu_mutex_lock_ramlist',
'qemu_mutex_trylock', 'qemu_mutex_trylock',
'qemu_mutex_unlock_ramlist', 'qemu_mutex_unlock_ramlist',
@ -2522,9 +2517,7 @@ symbols = (
'qemu_st_helpers', 'qemu_st_helpers',
'qemu_strnlen', 'qemu_strnlen',
'qemu_strsep', 'qemu_strsep',
'qemu_tcg_cpu_thread_fn',
'qemu_tcg_init_vcpu', 'qemu_tcg_init_vcpu',
'qemu_thread_exit',
'qemu_try_memalign', 'qemu_try_memalign',
'qentry_destroy', 'qentry_destroy',
'qerror_human', 'qerror_human',

View file

@ -20,7 +20,7 @@
static int tosa_init(struct uc_struct *uc, MachineState *machine) static int tosa_init(struct uc_struct *uc, MachineState *machine)
{ {
//cpu_arm_init(uc, "pxa255"); //cpu_arm_init(uc, "pxa255");
cpu_arm_init(uc, "cortex-a15"); // FIXME uc->cpu = cpu_arm_init(uc, "cortex-a15"); // FIXME
return 0; return 0;
} }

View file

@ -54,7 +54,7 @@ static int machvirt_init(struct uc_struct *uc, MachineState *machine)
return -1; return -1;
} }
cpuobj = object_new(uc, object_class_get_name(oc)); uc->cpu = cpuobj = object_new(uc, object_class_get_name(oc));
object_property_set_bool(uc, cpuobj, true, "realized", NULL); object_property_set_bool(uc, cpuobj, true, "realized", NULL);
} }

View file

@ -70,7 +70,6 @@ static void apic_sync_vapic(APICCommonState *s, int sync_type)
//length = offsetof(VAPICState, enabled) - offsetof(VAPICState, isr); //length = offsetof(VAPICState, enabled) - offsetof(VAPICState, isr);
if (sync_type & SYNC_TO_VAPIC) { if (sync_type & SYNC_TO_VAPIC) {
assert(qemu_cpu_is_self(CPU(s->cpu)));
vapic_state.tpr = s->tpr; vapic_state.tpr = s->tpr;
vapic_state.enabled = 1; vapic_state.enabled = 1;

View file

@ -1,77 +0,0 @@
/*
* QEMU System Emulator
*
* Copyright (c) 2003-2008 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef QEMU_MAIN_LOOP_H
#define QEMU_MAIN_LOOP_H 1
#define SIG_IPI SIGUSR1
struct uc_struct;
/**
* qemu_init_main_loop: Set up the process so that it can run the main loop.
*
* This includes setting up signal handlers. It should be called before
* any other threads are created. In addition, threads other than the
* main one should block signals that are trapped by the main loop.
* For simplicity, you can consider these signals to be safe: SIGUSR1,
* SIGUSR2, thread signals (SIGFPE, SIGILL, SIGSEGV, SIGBUS) and real-time
* signals if available. Remember that Windows in practice does not have
* signals, though.
*
* In the case of QEMU tools, this will also start/initialize timers.
*/
int qemu_init_main_loop(void);
/**
* qemu_mutex_lock_iothread: Lock the main loop mutex.
*
* This function locks the main loop mutex. The mutex is taken by
* qemu_init_main_loop and always taken except while waiting on
* external events (such as with select). The mutex should be taken
* by threads other than the main loop thread when calling
* qemu_bh_new(), qemu_set_fd_handler() and basically all other
* functions documented in this file.
*
* NOTE: tools currently are single-threaded and qemu_mutex_lock_iothread
* is a no-op there.
*/
void qemu_mutex_lock_iothread(struct uc_struct* uc);
/**
* qemu_mutex_unlock_iothread: Unlock the main loop mutex.
*
* This function unlocks the main loop mutex. The mutex is taken by
* qemu_init_main_loop and always taken except while waiting on
* external events (such as with select). The mutex should be unlocked
* as soon as possible by threads other than the main loop thread,
* because it prevents the main loop from processing callbacks,
* including timers and bottom halves.
*
* NOTE: tools currently are single-threaded and qemu_mutex_unlock_iothread
* is a no-op there.
*/
void qemu_mutex_unlock_iothread(struct uc_struct* uc);
#endif

Some files were not shown because too many files have changed in this diff Show more