mirror of
https://github.com/yuzu-emu/unicorn.git
synced 2025-02-25 22:57:03 +00:00
commit
1afbee3a12
16
.appveyor.yml
Normal file
16
.appveyor.yml
Normal 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
22
.gitignore
vendored
|
@ -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
15
.travis.yml
Normal 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
|
304
COMPILE.TXT
304
COMPILE.TXT
|
@ -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.
|
|
|
@ -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
|
||||||
|
|
15
Makefile
15
Makefile
|
@ -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)
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
Unicorn Engine
|
Unicorn Engine
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
[](https://gitter.im/unicorn-engine/chat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
|
|
||||||
|
[](https://travis-ci.org/unicorn-engine/unicorn)
|
||||||
|
[](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).
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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': ")",
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
25
bindings/go/unicorn/uc.c
Normal 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
2
bindings/go/unicorn/uc.h
Normal 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);
|
|
@ -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),
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
23
bindings/haskell/.gitignore
vendored
Normal 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
|
31
bindings/haskell/README.TXT
Normal file
31
bindings/haskell/README.TXT
Normal 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
|
2
bindings/haskell/Setup.hs
Normal file
2
bindings/haskell/Setup.hs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
import Distribution.Simple
|
||||||
|
main = defaultMain
|
133
bindings/haskell/samples/SampleArm.hs
Normal file
133
bindings/haskell/samples/SampleArm.hs
Normal 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
|
85
bindings/haskell/samples/SampleArm64.hs
Normal file
85
bindings/haskell/samples/SampleArm64.hs
Normal 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
|
142
bindings/haskell/samples/SampleM68k.hs
Normal file
142
bindings/haskell/samples/SampleM68k.hs
Normal 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
|
129
bindings/haskell/samples/SampleMips.hs
Normal file
129
bindings/haskell/samples/SampleMips.hs
Normal 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
|
85
bindings/haskell/samples/SampleSparc.hs
Normal file
85
bindings/haskell/samples/SampleSparc.hs
Normal 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
|
675
bindings/haskell/samples/SampleX86.hs
Normal file
675
bindings/haskell/samples/SampleX86.hs
Normal 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>"
|
||||||
|
|
153
bindings/haskell/samples/Shellcode.hs
Normal file
153
bindings/haskell/samples/Shellcode.hs
Normal 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>"
|
284
bindings/haskell/src/Unicorn.hs
Normal file
284
bindings/haskell/src/Unicorn.hs
Normal 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
|
29
bindings/haskell/src/Unicorn/CPU/Arm.chs
Normal file
29
bindings/haskell/src/Unicorn/CPU/Arm.chs
Normal 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
|
29
bindings/haskell/src/Unicorn/CPU/Arm64.chs
Normal file
29
bindings/haskell/src/Unicorn/CPU/Arm64.chs
Normal 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
|
29
bindings/haskell/src/Unicorn/CPU/M68k.chs
Normal file
29
bindings/haskell/src/Unicorn/CPU/M68k.chs
Normal 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
|
61
bindings/haskell/src/Unicorn/CPU/Mips.chs
Normal file
61
bindings/haskell/src/Unicorn/CPU/Mips.chs
Normal 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
|
29
bindings/haskell/src/Unicorn/CPU/Sparc.chs
Normal file
29
bindings/haskell/src/Unicorn/CPU/Sparc.chs
Normal 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
|
65
bindings/haskell/src/Unicorn/CPU/X86.chs
Normal file
65
bindings/haskell/src/Unicorn/CPU/X86.chs
Normal 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) #}
|
223
bindings/haskell/src/Unicorn/Hook.hs
Normal file
223
bindings/haskell/src/Unicorn/Hook.hs
Normal 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
|
52
bindings/haskell/src/Unicorn/Internal/Core.chs
Normal file
52
bindings/haskell/src/Unicorn/Internal/Core.chs
Normal 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
|
415
bindings/haskell/src/Unicorn/Internal/Hook.chs
Normal file
415
bindings/haskell/src/Unicorn/Internal/Hook.chs
Normal 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
|
242
bindings/haskell/src/Unicorn/Internal/Unicorn.chs
Normal file
242
bindings/haskell/src/Unicorn/Internal/Unicorn.chs
Normal 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)
|
26
bindings/haskell/src/Unicorn/Internal/Util.hs
Normal file
26
bindings/haskell/src/Unicorn/Internal/Util.hs
Normal 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
|
8
bindings/haskell/src/cbits/unicorn_wrapper.c
Normal file
8
bindings/haskell/src/cbits/unicorn_wrapper.c
Normal 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) {
|
||||||
|
}
|
16
bindings/haskell/src/include/unicorn_wrapper.h
Normal file
16
bindings/haskell/src/include/unicorn_wrapper.h
Normal 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
|
42
bindings/haskell/unicorn.cabal
Normal file
42
bindings/haskell/unicorn.cabal
Normal 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
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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"))
|
||||||
|
|
|
@ -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__
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
11
bindings/ruby/Makefile
Normal 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
24
bindings/ruby/README.md
Normal 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
106
bindings/ruby/sample_arm.rb
Normal 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()
|
69
bindings/ruby/sample_arm64.rb
Normal file
69
bindings/ruby/sample_arm64.rb
Normal 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()
|
65
bindings/ruby/sample_m68k.rb
Normal file
65
bindings/ruby/sample_m68k.rb
Normal 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()
|
104
bindings/ruby/sample_mips.rb
Normal file
104
bindings/ruby/sample_mips.rb
Normal 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()
|
65
bindings/ruby/sample_sparc.rb
Normal file
65
bindings/ruby/sample_sparc.rb
Normal 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
509
bindings/ruby/sample_x86.rb
Normal 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()
|
97
bindings/ruby/sample_x86_gdt.rb
Normal file
97
bindings/ruby/sample_x86_gdt.rb
Normal 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)
|
||||||
|
|
||||||
|
|
3
bindings/ruby/unicorn_gem/Gemfile
Normal file
3
bindings/ruby/unicorn_gem/Gemfile
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
|
gemspec
|
2
bindings/ruby/unicorn_gem/Rakefile
Normal file
2
bindings/ruby/unicorn_gem/Rakefile
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
require "bundler/gem_tasks"
|
||||||
|
task :default => :spec
|
8
bindings/ruby/unicorn_gem/ext/extconf.rb
Normal file
8
bindings/ruby/unicorn_gem/ext/extconf.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
require 'mkmf'
|
||||||
|
|
||||||
|
extension_name = 'unicorn'
|
||||||
|
|
||||||
|
dir_config(extension_name)
|
||||||
|
have_library('unicorn')
|
||||||
|
|
||||||
|
create_makefile(extension_name)
|
424
bindings/ruby/unicorn_gem/ext/unicorn.c
Normal file
424
bindings/ruby/unicorn_gem/ext/unicorn.c
Normal 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, ®_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);
|
||||||
|
}
|
33
bindings/ruby/unicorn_gem/ext/unicorn.h
Normal file
33
bindings/ruby/unicorn_gem/ext/unicorn.h
Normal 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);
|
277
bindings/ruby/unicorn_gem/lib/unicorn/arm64_const.rb
Normal file
277
bindings/ruby/unicorn_gem/lib/unicorn/arm64_const.rb
Normal 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
|
128
bindings/ruby/unicorn_gem/lib/unicorn/arm_const.rb
Normal file
128
bindings/ruby/unicorn_gem/lib/unicorn/arm_const.rb
Normal 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
|
27
bindings/ruby/unicorn_gem/lib/unicorn/m68k_const.rb
Normal file
27
bindings/ruby/unicorn_gem/lib/unicorn/m68k_const.rb
Normal 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
|
198
bindings/ruby/unicorn_gem/lib/unicorn/mips_const.rb
Normal file
198
bindings/ruby/unicorn_gem/lib/unicorn/mips_const.rb
Normal 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
|
99
bindings/ruby/unicorn_gem/lib/unicorn/sparc_const.rb
Normal file
99
bindings/ruby/unicorn_gem/lib/unicorn/sparc_const.rb
Normal 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
|
98
bindings/ruby/unicorn_gem/lib/unicorn/unicorn_const.rb
Normal file
98
bindings/ruby/unicorn_gem/lib/unicorn/unicorn_const.rb
Normal 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
|
3
bindings/ruby/unicorn_gem/lib/unicorn/version.rb
Normal file
3
bindings/ruby/unicorn_gem/lib/unicorn/version.rb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module Unicorn
|
||||||
|
VERSION = "1.0.0"
|
||||||
|
end
|
1598
bindings/ruby/unicorn_gem/lib/unicorn/x86_const.rb
Normal file
1598
bindings/ruby/unicorn_gem/lib/unicorn/x86_const.rb
Normal file
File diff suppressed because it is too large
Load diff
10
bindings/ruby/unicorn_gem/pkg/.gitignore
vendored
Normal file
10
bindings/ruby/unicorn_gem/pkg/.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/.bundle/
|
||||||
|
/.yardoc
|
||||||
|
/Gemfile.lock
|
||||||
|
/_yardoc/
|
||||||
|
/coverage/
|
||||||
|
/doc/
|
||||||
|
/pkg/
|
||||||
|
/spec/reports/
|
||||||
|
/tmp/
|
||||||
|
*.gem
|
21
bindings/ruby/unicorn_gem/unicorn.gemspec
Normal file
21
bindings/ruby/unicorn_gem/unicorn.gemspec
Normal 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
170
docs/COMPILE-NIX.md
Normal 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
201
docs/COMPILE-WINDOWS.md
Normal 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
10
docs/COMPILE.md
Normal 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>/
|
|
@ -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];
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
2
list.c
|
@ -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
14
make.sh
|
@ -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;;
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
78
qemu/cpus.c
78
qemu/cpus.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
Loading…
Reference in a new issue