make cleanup

This commit is contained in:
Stephen 2016-10-28 16:31:52 -07:00 committed by sgroat
parent 4d5738eeb5
commit 186540e160
38 changed files with 1149 additions and 800 deletions

View file

@ -7,26 +7,15 @@ environment:
CYG_CACHE: C:\cygwin64\var\cache\setup CYG_CACHE: C:\cygwin64\var\cache\setup
CYG_BASH: C:\cygwin64\bin\bash CYG_BASH: C:\cygwin64\bin\bash
CC: gcc CC: gcc
# TODO: Uncomment
# - CYG_ROOT: C:\cygwin64
# CYG_SETUP: setup-x86_64.exe
# CYG_MIRROR: http://cygwin.mirror.constant.com
# CYG_CACHE: C:\cygwin64\var\cache\setup
# CYG_BASH: C:\cygwin64\bin\bash
# CC: clang
- CYG_ROOT: C:\cygwin - CYG_ROOT: C:\cygwin
CYG_SETUP: setup-x86.exe CYG_SETUP: setup-x86.exe
CYG_MIRROR: http://cygwin.mirror.constant.com CYG_MIRROR: http://cygwin.mirror.constant.com
CYG_CACHE: C:\cygwin\var\cache\setup CYG_CACHE: C:\cygwin\var\cache\setup
CYG_BASH: C:\cygwin\bin\bash CYG_BASH: C:\cygwin\bin\bash
CC: gcc CC: gcc
# TODO: Uncomment # - MSYS_ROOT: C:\msys64
# - CYG_ROOT: C:\cygwin # MSYS_BASH: C:\msys64\mingw64\bin\sh
# CYG_SETUP: setup-x86.exe # CC: x86_64-w64-mingw32-gcc
# CYG_MIRROR: http://cygwin.mirror.constant.com
# CYG_CACHE: C:\cygwin\var\cache\setup
# CYG_BASH: C:\cygwin\bin\bash
# CC: clang
# Cache Cygwin files to speed up build # Cache Cygwin files to speed up build
cache: cache:
@ -42,13 +31,11 @@ init:
# Install needed build dependencies # Install needed build dependencies
install: install:
- ps: 'if ($env:CYG_ROOT) { Start-FileDownload "http://cygwin.com/$env:CYG_SETUP" -FileName "$env:CYG_SETUP" }' - ps: 'if ($env:CYG_ROOT) { Start-FileDownload "http://cygwin.com/$env:CYG_SETUP" -FileName "$env:CYG_SETUP" }'
- '%CYG_SETUP% --quiet-mode --no-shortcuts --only-site --root "%CYG_ROOT%" --site "%CYG_MIRROR%" --local-package-dir "%CYG_CACHE%" --packages make,gcc-core,clang,pkg-config,libpcre-devel,libglib2.0-devel,cmake --upgrade-also' - if defined CYG_ROOT (%CYG_SETUP% --quiet-mode --no-shortcuts --only-site --root "%CYG_ROOT%" --site "%CYG_MIRROR%" --local-package-dir "%CYG_CACHE%" --packages make,gcc-core,clang,pkg-config,libpcre-devel,libglib2.0-devel,cmake,python-setuptools --upgrade-also)
- '%CYG_BASH% -lc "cygcheck -dc cygwin"' - if defined MSYS_ROOT (%MSYS_BASH% -lc "pacman -S --noconfirm mingw-w64-x86_64-glib2")
build_script: build_script:
# TODO: uncomment and enable tests - if defined CYG_ROOT (%CYG_BASH% -lc "export CYGWIN=winsymlinks:native; cd $APPVEYOR_BUILD_FOLDER; ./install-cmocka-linux.sh; make; export PATH=$PATH:../../:../../cmocka/src:../:../cmocka/src; make test")
- '%CYG_BASH% -lc "export CYGWIN=winsymlinks:native; cd $APPVEYOR_BUILD_FOLDER; ./install-cmocka-linux.sh; ./make.sh; export PATH=$PATH:../../:../../cmocka/src; make test"' - if defined MSYS_ROOT (%MSYS_BASH% -lc "MSYS=winsymlinks, cd $(cygpath ${APPVEYOR_BUILD_FOLDER}); x86_64-w64-mingw32-gcc --version; ./install-cmocka-linux.sh; make")
#- '%CYG_BASH% -lc "export CYGWIN=winsymlinks:native; cd $APPVEYOR_BUILD_FOLDER; ./install-cmocka-linux.sh; ./make.sh"'
#- 'cd %APPVEYOR_BUILD_FOLDER% && cd bindings\dotnet && msbuild UnicornDotNet.sln' #- 'cd %APPVEYOR_BUILD_FOLDER% && cd bindings\dotnet && msbuild UnicornDotNet.sln'
# Allows RDP # Allows RDP
#on_finish: #on_finish:

View file

@ -2,42 +2,30 @@ language: c
sudo: false sudo: false
before_install: before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install glib cmocka; fi - if [[ "$TRAVIS_OS_NAME" == "osx" && "$MACOS_UNIVERSAL" != "yes" ]]; then brew install glib cmocka; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" && "$MACOS_UNIVERSAL" == "yes" ]]; then brew install glib --universal cmocka; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./install-cmocka-linux.sh; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./install-cmocka-linux.sh; fi
script: script:
- if [[ $CC == *"x86_64"* ]]; then ./make.sh cross-win64; elif [[ $CC == *"i686"* ]]; then ./make.sh cross-win32; else ./make.sh && make test; fi - make && make test
# TODO make bindings enabled # TODO make bindings enabled
# - ./make.sh && make test && make bindings
# TODO make universal build
# - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew remove glib && brew install glib --universal && make clean && ./make.sh macos-universal && make test; fi
# TODO test iOS builds
# - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then make clean && ./make.sh ios; fi # - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then make clean && ./make.sh ios; fi
compiler: compiler:
- clang - clang
- gcc - gcc
# TODO update mingw32 to gcc 4.7+ for compilation
# - i686-w64-mingw32-gcc
# - x86_64-w64-mingw32-gcc
os: os:
- linux - linux
- osx - osx
#matrix: matrix:
# exclude: include:
# - os: osx - os: osx
# compiler: i686-w64-mingw32-gcc compiler: clang
env: MACOS_UNIVERSAL=yes
- os: osx
compiler: gcc
env: MACOS_UNIVERSAL=yes
# - os: osx # - os: osx
# compiler: x86_64-w64-mingw32-gcc # compiler: x86_64-w64-mingw32-gcc
addons: addons:
apt: apt:
packages: packages:
- mingw-w64 # mingw-w64 packages too old in precise
- gcc-mingw-w64
- mingw-w64-dev
- gcc-mingw-w64-i686
- gcc-mingw-w64-x86-64
- binutils-mingw-w64-i686
- binutils-mingw-w64-x86-64
# TODO are mingw32 builds necessary?
# - mingw32
# - mingw32-binutils
# - mingw32-runtime

View file

@ -63,6 +63,7 @@ ifeq ($(UNICORN_DEBUG),yes)
CFLAGS += -g CFLAGS += -g
else else
CFLAGS += -O3 CFLAGS += -O3
UNICORN_QEMU_FLAGS += --disable-debug-info
endif endif
ifeq ($(UNICORN_ASAN),yes) ifeq ($(UNICORN_ASAN),yes)
@ -97,9 +98,6 @@ PKG_VERSION = $(PKG_MAJOR).$(PKG_MINOR).$(PKG_EXTRA)
endif endif
API_MAJOR=$(shell echo `grep -e UC_API_MAJOR include/unicorn/unicorn.h | grep -v = | awk '{print $$3}'` | awk '{print $$1}') API_MAJOR=$(shell echo `grep -e UC_API_MAJOR include/unicorn/unicorn.h | grep -v = | awk '{print $$3}'` | awk '{print $$1}')
VERSION_EXT =
BIN_EXT =
# Apple? # Apple?
ifeq ($(UNAME_S),Darwin) ifeq ($(UNAME_S),Darwin)
@ -127,6 +125,8 @@ else ifneq ($(filter MINGW%,$(UNAME_S)),)
EXT = dll EXT = dll
AR_EXT = lib AR_EXT = lib
BIN_EXT = .exe BIN_EXT = .exe
UNICORN_QEMU_FLAGS += --disable-stack-protector
UNICORN_CFLAGS := $(UNICORN_CFLAGS:-fPIC=)
# Linux, Darwin # Linux, Darwin
else else
@ -139,27 +139,25 @@ endif
ifeq ($(UNICORN_SHARED),yes) ifeq ($(UNICORN_SHARED),yes)
ifneq ($(filter MINGW%,$(UNAME_S)),) ifneq ($(filter MINGW%,$(UNAME_S)),)
LIBRARY = $(BLDIR)/$(LIBNAME).$(EXT) LIBRARY = $(LIBNAME).$(EXT)
else ifneq ($(filter CYGWIN%,$(UNAME_S)),) else ifneq ($(filter CYGWIN%,$(UNAME_S)),)
LIBRARY = $(BLDIR)/cyg$(LIBNAME).$(EXT) LIBRARY = cyg$(LIBNAME).$(EXT)
LIBRARY_DLLA = $(BLDIR)/lib$(LIBNAME).$(EXT).$(AR_EXT) LIBRARY_DLLA = lib$(LIBNAME).$(EXT).$(AR_EXT)
$(LIBNAME)_LDFLAGS += -Wl,--out-implib=$(LIBRARY_DLLA) $(LIBNAME)_LDFLAGS += -Wl,--out-implib=$(LIBRARY_DLLA)
$(LIBNAME)_LDFLAGS += -lssp $(LIBNAME)_LDFLAGS += -lssp
# Linux, Darwin # Linux, Darwin
else else
LIBRARY = $(BLDIR)/lib$(LIBNAME).$(VERSION_EXT) LIBRARY = lib$(LIBNAME).$(VERSION_EXT)
LIBRARY_SYMLINK = $(BLDIR)/lib$(LIBNAME).$(EXT) LIBRARY_SYMLINK = lib$(LIBNAME).$(EXT)
endif endif
endif endif
ifeq ($(UNICORN_STATIC),yes) ifeq ($(UNICORN_STATIC),yes)
ifneq ($(filter MINGW%,$(UNAME_S)),) ifneq ($(filter MINGW%,$(UNAME_S)),)
ARCHIVE = $(BLDIR)/$(LIBNAME).$(AR_EXT) ARCHIVE = $(LIBNAME).$(AR_EXT)
else ifneq ($(filter CYGWIN%,$(UNAME_S)),) # Cygwin, Linux, Darwin
ARCHIVE = $(BLDIR)/lib$(LIBNAME).$(AR_EXT)
# Linux, Darwin
else else
ARCHIVE = $(BLDIR)/lib$(LIBNAME).$(AR_EXT) ARCHIVE = lib$(LIBNAME).$(AR_EXT)
endif endif
endif endif
@ -169,8 +167,6 @@ INSTALL_LIB ?= $(INSTALL_BIN) -m0755
PKGCFGF = $(LIBNAME).pc PKGCFGF = $(LIBNAME).pc
PREFIX ?= /usr PREFIX ?= /usr
DESTDIR ?= DESTDIR ?=
BLDIR = .
OBJDIR = .
LIBDIRARCH ?= lib LIBDIRARCH ?= lib
# Uncomment the below line to installs x86_64 libs to lib64/ directory. # Uncomment the below line to installs x86_64 libs to lib64/ directory.
@ -200,75 +196,39 @@ else
PKGCFGDIR ?= $(LIBDATADIR)/pkgconfig PKGCFGDIR ?= $(LIBDATADIR)/pkgconfig
endif endif
all: compile_lib .PHONY: all
ifeq (,$(findstring yes,$(UNICORN_BUILD_CORE_ONLY))) all: unicorn
ifeq ($(UNICORN_SHARED),yes) $(MAKE) -C samples
ifeq ($(V),0)
@$(INSTALL_LIB) $(LIBRARY) $(BLDIR)/samples/
else
$(INSTALL_LIB) $(LIBRARY) $(BLDIR)/samples/
endif
endif
@cd samples && $(MAKE)
endif
config: config:
if [ "$(UNICORN_ARCHS)" != "`cat config.log`" ]; then $(MAKE) clean; fi if [ "$(UNICORN_ARCHS)" != "`cat config.log`" ]; then $(MAKE) clean; fi
qemu/config-host.h-timestamp: qemu/config-host.h-timestamp:
ifeq ($(UNICORN_DEBUG),yes)
cd qemu && \ cd qemu && \
./configure --cc="${CC}" --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
cd qemu && \
./configure --cc="${CC}" --disable-debug-info --extra-cflags="$(UNICORN_CFLAGS)" --target-list="$(UNICORN_TARGETS)" ${UNICORN_QEMU_FLAGS}
printf "$(UNICORN_ARCHS)" > config.log
endif
compile_lib: config qemu/config-host.h-timestamp compile_lib: config qemu/config-host.h-timestamp
cd qemu && $(MAKE) -j 4 $(MAKE) -C qemu -j 4
$(MAKE) unicorn
unicorn: $(LIBRARY) $(ARCHIVE) unicorn: compile_lib $(LIBRARY) $(ARCHIVE)
$(LIBRARY): $(UC_TARGET_OBJ) uc.o list.o $(LIBRARY): $(UC_TARGET_OBJ)
ifeq ($(UNICORN_SHARED),yes) $(CC) $(CFLAGS) -shared $(GENOBJ) uc.o list.o -o $(LIBRARY) $(GLIB) -lm $($(LIBNAME)_LDFLAGS)
ifeq ($(V),0) ln -sf $(LIBRARY) $(LIBRARY_SYMLINK)
$(call log,GEN,$(LIBRARY))
@$(CC) $(CFLAGS) -shared $^ -o $(LIBRARY) $(GLIB) -lm $($(LIBNAME)_LDFLAGS)
else
$(CC) $(CFLAGS) -shared $^ -o $(LIBRARY) $(GLIB) -lm $($(LIBNAME)_LDFLAGS)
endif
ifneq (,$(LIBRARY_SYMLINK))
@ln -sf $(LIBRARY) $(LIBRARY_SYMLINK)
endif
endif
$(ARCHIVE): $(UC_TARGET_OBJ) uc.o list.o $(ARCHIVE): $(UC_TARGET_OBJ) uc.o list.o
ifeq ($(UNICORN_STATIC),yes) $(AR) q $(ARCHIVE) $^
ifeq ($(V),0) $(RANLIB) $(ARCHIVE)
$(call log,GEN,$(ARCHIVE))
@$(create-archive)
else
$(create-archive)
endif
endif
$(PKGCFGF): $(PKGCFGF):
ifeq ($(V),0)
$(call log,GEN,$(@:$(BLDIR)/%=%))
@$(generate-pkgcfg)
else
$(generate-pkgcfg) $(generate-pkgcfg)
endif
.PHONY: test .PHONY: test
test: all test: all
$(MAKE) -C tests/unit test $(MAKE) -C tests/unit test
$(MAKE) -C bindings test
install: compile_lib $(PKGCFGF) install: compile_lib $(PKGCFGF)
mkdir -p $(DESTDIR)$(LIBDIR) mkdir -p $(DESTDIR)$(LIBDIR)
@ -309,7 +269,7 @@ dist:
git archive --format=zip --prefix=unicorn-$(DIST_VERSION)/ $(TAG) > unicorn-$(DIST_VERSION).zip git archive --format=zip --prefix=unicorn-$(DIST_VERSION)/ $(TAG) > unicorn-$(DIST_VERSION).zip
header: FORCE header:
$(eval TARGETS := m68k arm aarch64 mips mipsel mips64 mips64el\ $(eval TARGETS := m68k arm aarch64 mips mipsel mips64 mips64el\
powerpc sparc sparc64 x86_64) powerpc sparc sparc64 x86_64)
$(foreach var,$(TARGETS),\ $(foreach var,$(TARGETS),\
@ -328,10 +288,7 @@ clean:
$(MAKE) -C qemu clean $(MAKE) -C qemu clean
rm -rf *.d *.o rm -rf *.d *.o
rm -rf lib$(LIBNAME)* $(LIBNAME)*.lib $(LIBNAME)*.dll cyg$(LIBNAME)*.dll rm -rf lib$(LIBNAME)* $(LIBNAME)*.lib $(LIBNAME)*.dll cyg$(LIBNAME)*.dll
ifeq (,$(findstring yes,$(UNICORN_BUILD_CORE_ONLY))) $(MAKE) -C samples clean
cd samples && $(MAKE) clean
rm -f $(BLDIR)/samples/lib$(LIBNAME).$(EXT)
endif
$(MAKE) -C tests/unit clean $(MAKE) -C tests/unit clean
@ -351,10 +308,3 @@ define log
@printf " %-7s %s\n" "$(1)" "$(2)" @printf " %-7s %s\n" "$(1)" "$(2)"
endef endef
define create-archive
$(AR) q $(ARCHIVE) $^
$(RANLIB) $(ARCHIVE)
endef
FORCE:

View file

@ -13,7 +13,7 @@ SAMPLE_X86 = $(TMP_DIR)/sample_x86
ENV_VARS = LD_LIBRARY_PATH=../ DYLD_LIBRARY_PATH=../ ENV_VARS = LD_LIBRARY_PATH=../ DYLD_LIBRARY_PATH=../
.PHONY: build install samples sample_python expected python sample_diff clean check .PHONY: build install expected python sample_diff clean check test
build: build:
$(MAKE) -C python gen_const $(MAKE) -C python gen_const
@ -26,9 +26,7 @@ install: build
$(MAKE) -C python install $(MAKE) -C python install
$(MAKE) -C java install $(MAKE) -C java install
samples: expected python test: expected python sample_diff
sample_python: expected python
expected: expected:
$(MAKE) -C ../samples $(MAKE) -C ../samples
@ -38,9 +36,11 @@ expected:
$(ENV_VARS) ../samples/sample_mips > $(SAMPLE_MIPS)_e $(ENV_VARS) ../samples/sample_mips > $(SAMPLE_MIPS)_e
$(ENV_VARS) ../samples/sample_sparc > $(SAMPLE_SPARC)_e $(ENV_VARS) ../samples/sample_sparc > $(SAMPLE_SPARC)_e
$(ENV_VARS) ../samples/sample_m68k > $(SAMPLE_M68K)_e $(ENV_VARS) ../samples/sample_m68k > $(SAMPLE_M68K)_e
$(ENV_VARS) ../samples/sample_x86 > $(SAMPLE_X86)_e $(ENV_VARS) ../samples/sample_x86 -16 > $(SAMPLE_X86)_e
$(ENV_VARS) ../samples/sample_x86 -32 >> $(SAMPLE_X86)_e
$(ENV_VARS) ../samples/sample_x86 -64 >> $(SAMPLE_X86)_e
python: FORCE python:
$(MAKE) -C python $(MAKE) -C python
$(ENV_VARS) python python/sample_arm.py > $(SAMPLE_ARM)_o $(ENV_VARS) python python/sample_arm.py > $(SAMPLE_ARM)_o
$(ENV_VARS) python python/sample_arm64.py > $(SAMPLE_ARM64)_o $(ENV_VARS) python python/sample_arm64.py > $(SAMPLE_ARM64)_o
@ -48,9 +48,8 @@ python: FORCE
$(ENV_VARS) python python/sample_sparc.py > $(SAMPLE_SPARC)_o $(ENV_VARS) python python/sample_sparc.py > $(SAMPLE_SPARC)_o
$(ENV_VARS) python python/sample_m68k.py > $(SAMPLE_M68K)_o $(ENV_VARS) python python/sample_m68k.py > $(SAMPLE_M68K)_o
$(ENV_VARS) python python/sample_x86.py > $(SAMPLE_X86)_o $(ENV_VARS) python python/sample_x86.py > $(SAMPLE_X86)_o
$(MAKE) sample_diff
sample_diff: FORCE sample_diff:
$(DIFF) $(SAMPLE_ARM)_e $(SAMPLE_ARM)_o $(DIFF) $(SAMPLE_ARM)_e $(SAMPLE_ARM)_o
$(DIFF) $(SAMPLE_ARM64)_e $(SAMPLE_ARM64)_o $(DIFF) $(SAMPLE_ARM64)_e $(SAMPLE_ARM64)_o
$(DIFF) $(SAMPLE_MIPS)_e $(SAMPLE_MIPS)_o $(DIFF) $(SAMPLE_MIPS)_e $(SAMPLE_MIPS)_o
@ -65,5 +64,3 @@ clean:
check: check:
make -C python check make -C python check
FORCE:

View file

@ -0,0 +1,28 @@
package unicorn
import (
"runtime"
)
// #include <unicorn/unicorn.h>
import "C"
type Context **C.uc_context
func (u *uc) ContextSave(reuse Context) (Context, error) {
ctx := reuse
if ctx == nil {
ctx = new(*C.uc_context)
}
if err := errReturn(C.uc_context_alloc(u.handle, ctx)); err != nil {
return nil, err
}
runtime.SetFinalizer(ctx, func(p Context) { C.uc_context_free(*p) })
if err := errReturn(C.uc_context_save(u.handle, *ctx)); err != nil {
}
return ctx, nil
}
func (u *uc) ContextRestore(ctx Context) error {
return errReturn(C.uc_context_restore(u.handle, *ctx))
}

View file

@ -0,0 +1,26 @@
package unicorn
import (
"testing"
)
func TestContext(t *testing.T) {
u, err := NewUnicorn(ARCH_X86, MODE_32)
if err != nil {
t.Fatal(err)
}
u.RegWrite(X86_REG_EBP, 100)
ctx, err := u.ContextSave(nil)
if err != nil {
t.Fatal(err)
}
u.RegWrite(X86_REG_EBP, 200)
err = u.ContextRestore(ctx)
if err != nil {
t.Fatal(err)
}
val, _ := u.RegRead(X86_REG_EBP)
if val != 100 {
t.Fatal("context restore failed")
}
}

View file

@ -55,6 +55,9 @@ type Unicorn interface {
HookDel(hook Hook) error HookDel(hook Hook) error
Query(queryType int) (uint64, error) Query(queryType int) (uint64, error)
Close() error Close() error
ContextSave(reuse Context) (Context, error)
ContextRestore(Context) error
} }
type uc struct { type uc struct {

View file

@ -40,6 +40,10 @@ x86Code32JmpInvalid = BS.pack [0xe9, 0xe9, 0xee, 0xee, 0xee, 0x41, 0x4a]
x86Code32InOut :: BS.ByteString x86Code32InOut :: BS.ByteString
x86Code32InOut = BS.pack [0x41, 0xe4, 0x3f, 0x4a, 0xe6, 0x46, 0x43] x86Code32InOut = BS.pack [0x41, 0xe4, 0x3f, 0x4a, 0xe6, 0x46, 0x43]
-- inc eax
x86Code32Inc :: BS.ByteString
x86Code32Inc = BS.pack [0x40]
x86Code64 :: BS.ByteString x86Code64 :: BS.ByteString
x86Code64 = BS.pack [0x41, 0xbc, 0x3b, 0xb0, 0x28, 0x2a, 0x49, 0x0f, 0xc9, x86Code64 = BS.pack [0x41, 0xbc, 0x3b, 0xb0, 0x28, 0x2a, 0x49, 0x0f, 0xc9,
0x90, 0x4d, 0x0f, 0xad, 0xcf, 0x49, 0x87, 0xfd, 0x90, 0x90, 0x4d, 0x0f, 0xad, 0xcf, 0x49, 0x87, 0xfd, 0x90,
@ -494,6 +498,70 @@ testI386InOut = do
Left err -> putStrLn $ "Failed with error " ++ show err ++ ": " ++ Left err -> putStrLn $ "Failed with error " ++ show err ++ ": " ++
strerror err strerror err
-- Emulate code and save/restore the CPU context
testI386ContextSave :: IO ()
testI386ContextSave = do
putStrLn "==================================="
putStrLn "Save/restore CPU context in opaque blob"
result <- runEmulator $ do
-- Initialize emulator in X86-32bit mode
uc <- open ArchX86 [Mode32]
-- Map 8KB memory for this emulation
memMap uc address (8 * 1024) [ProtAll]
-- Write machine code to be emulated to memory
memWrite uc address x86Code32Inc
-- Initialize machine registers
regWrite uc X86.Eax 0x1
-- Emulate machine code in infinite time
emuPutStrLn ">>> Running emulation for the first time"
let codeLen = codeLength x86Code32Inc
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
emuPutStrLn $ ">>> EAX = 0x" ++ showHex eax
-- Allocate and save the CPU context
emuPutStrLn ">>> Saving CPU context"
context <- contextAllocate uc
contextSave uc context
-- Emulate machine code again
emuPutStrLn ">>> Running emulation for the second time"
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
emuPutStrLn $ ">>> EAX = 0x" ++ showHex eax
-- Restore CPU context
contextRestore uc context
-- Now print out some registers
emuPutStrLn ">>> Emulation done. Below is the CPU context"
eax <- regRead uc X86.Eax
emuPutStrLn $ ">>> EAX = 0x" ++ showHex eax
case result of
Right _ -> return ()
Left err -> putStrLn $ "Failed with error " ++ show err ++ ": " ++
strerror err
testX8664 :: IO () testX8664 :: IO ()
testX8664 = do testX8664 = do
putStrLn "Emulate x86_64 code" putStrLn "Emulate x86_64 code"
@ -660,6 +728,7 @@ main = do
["-32"] -> do ["-32"] -> do
testI386 testI386
testI386InOut testI386InOut
testI386ContextSave
testI386Jump testI386Jump
testI386Loop testI386Loop
testI386InvalidMemRead testI386InvalidMemRead

View file

@ -9,41 +9,47 @@ framework based on QEMU.
Further information is available at <http://www.unicorn-engine.org>. Further information is available at <http://www.unicorn-engine.org>.
-} -}
module Unicorn ( module Unicorn
-- * Emulator control ( -- * Emulator control
Emulator, Emulator
Engine, , Engine
Architecture(..), , Architecture(..)
Mode(..), , Mode(..)
QueryType(..), , QueryType(..)
runEmulator, , runEmulator
open, , open
query, , query
start, , start
stop, , stop
-- * Register operations -- * Register operations
regWrite, , regWrite
regRead, , regRead
-- * Memory operations -- * Memory operations
MemoryPermission(..), , MemoryPermission(..)
MemoryRegion(..), , MemoryRegion(..)
memWrite, , memWrite
memRead, , memRead
memMap, , memMap
memUnmap, , memUnmap
memProtect, , memProtect
memRegions, , memRegions
-- * Error handling -- * Context operations
Error(..), , Context
errno, , contextAllocate
strerror, , contextSave
, contextRestore
-- * Misc. -- * Error handling
version, , Error(..)
) where , errno
, strerror
-- * Misc.
, version
) where
import Control.Monad (liftM) import Control.Monad (liftM)
import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Class (lift)
@ -132,8 +138,8 @@ stop uc = do
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- | Write to register. -- | Write to register.
regWrite :: Reg r => regWrite :: Reg r
Engine -- ^ 'Unicorn' engine handle => Engine -- ^ 'Unicorn' engine handle
-> r -- ^ Register ID to write to -> r -- ^ Register ID to write to
-> Int64 -- ^ Value to write to register -> Int64 -- ^ Value to write to register
-> Emulator () -- ^ An 'Error' on failure -> Emulator () -- ^ An 'Error' on failure
@ -147,8 +153,8 @@ regWrite uc regId value = do
left err left err
-- | Read register value. -- | Read register value.
regRead :: Reg r => regRead :: Reg r
Engine -- ^ 'Unicorn' engine handle => Engine -- ^ 'Unicorn' engine handle
-> r -- ^ Register ID to read from -> r -- ^ Register ID to read from
-> Emulator Int64 -- ^ The value read from the register on success, -> Emulator Int64 -- ^ The value read from the register on success,
-- or an 'Error' on failure -- or an 'Error' on failure
@ -259,6 +265,46 @@ memRegions uc = do
else else
left err left err
-------------------------------------------------------------------------------
-- Context operations
-------------------------------------------------------------------------------
-- | Allocate a region that can be used to perform quick save/rollback of the
-- CPU context, which includes registers and some internal metadata. Contexts
-- may not be shared across engine instances with differing architectures or
-- modes.
contextAllocate :: Engine -- ^ 'Unicon' engine handle
-> Emulator Context -- ^ A CPU context
contextAllocate uc = do
(err, contextPtr) <- lift $ ucContextAlloc uc
if err == ErrOk then
-- Return a CPU context if ucContextAlloc completed successfully
lift $ mkContext contextPtr
else
left err
-- | Save a copy of the internal CPU context.
contextSave :: Engine -- ^ 'Unicorn' engine handle
-> Context -- ^ A CPU context
-> Emulator () -- ^ An error on failure
contextSave uc context = do
err <- lift $ ucContextSave uc context
if err == ErrOk then
right ()
else
left err
-- | Restore the current CPU context from a saved copy.
contextRestore :: Engine -- ^ 'Unicorn' engine handle
-> Context -- ^ A CPU context
-> Emulator () -- ^ An error on failure
contextRestore uc context = do
err <- lift $ ucContextRestore uc context
if err == ErrOk then
right ()
else
left err
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Misc. -- Misc.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------

View file

@ -8,22 +8,25 @@ License : GPL-2
Definitions for the ARM architecture. Definitions for the ARM architecture.
-} -}
module Unicorn.CPU.Arm ( module Unicorn.CPU.Arm
Register(..), (
) where Register(..)
) where
import Unicorn.Internal.Core (Reg) import Unicorn.Internal.Core (Reg)
{# context lib="unicorn" #} {# context lib = "unicorn" #}
#include <unicorn/arm.h> #include <unicorn/arm.h>
-- | ARM registers. -- | ARM registers.
{# enum uc_arm_reg as Register {# enum uc_arm_reg as Register
{underscoreToCase} { underscoreToCase }
omit (UC_ARM_REG_INVALID, omit ( UC_ARM_REG_INVALID
UC_ARM_REG_ENDING) , UC_ARM_REG_ENDING
with prefix="UC_ARM_REG_" )
deriving (Show, Eq, Bounded) #} with prefix = "UC_ARM_REG_"
deriving (Show, Eq, Bounded)
#}
instance Reg Register instance Reg Register

View file

@ -8,22 +8,25 @@ License : GPL-2
Definitions for the ARM64 (ARMv8) architecture. Definitions for the ARM64 (ARMv8) architecture.
-} -}
module Unicorn.CPU.Arm64 ( module Unicorn.CPU.Arm64
Register(..), (
) where Register(..)
) where
import Unicorn.Internal.Core (Reg) import Unicorn.Internal.Core (Reg)
{# context lib="unicorn" #} {# context lib = "unicorn" #}
#include <unicorn/arm64.h> #include <unicorn/arm64.h>
-- | ARM64 registers. -- | ARM64 registers.
{# enum uc_arm64_reg as Register {# enum uc_arm64_reg as Register
{underscoreToCase} { underscoreToCase }
omit (UC_ARM64_REG_INVALID, omit ( UC_ARM64_REG_INVALID
UC_ARM64_REG_ENDING) , UC_ARM64_REG_ENDING
with prefix="UC_ARM64_REG_" )
deriving (Show, Eq, Bounded) #} with prefix = "UC_ARM64_REG_"
deriving (Show, Eq, Bounded)
#}
instance Reg Register instance Reg Register

View file

@ -8,22 +8,25 @@ License : GPL-2
Definitions for the MK68K architecture. Definitions for the MK68K architecture.
-} -}
module Unicorn.CPU.M68k ( module Unicorn.CPU.M68k
Register(..), (
) where Register(..)
) where
import Unicorn.Internal.Core (Reg) import Unicorn.Internal.Core (Reg)
{# context lib="unicorn" #} {# context lib = "unicorn" #}
#include <unicorn/m68k.h> #include <unicorn/m68k.h>
-- | M68K registers. -- | M68K registers.
{# enum uc_m68k_reg as Register {# enum uc_m68k_reg as Register
{underscoreToCase} { underscoreToCase }
omit (UC_M68K_REG_INVALID, omit ( UC_M68K_REG_INVALID
UC_M68K_REG_ENDING) , UC_M68K_REG_ENDING
with prefix="UC_M68K_REG_" )
deriving (Show, Eq, Bounded) #} with prefix = "UC_M68K_REG_"
deriving (Show, Eq, Bounded)
#}
instance Reg Register instance Reg Register

View file

@ -8,54 +8,58 @@ License : GPL-2
Definitions for the MIPS architecture. Definitions for the MIPS architecture.
-} -}
module Unicorn.CPU.Mips ( module Unicorn.CPU.Mips
Register(..), (
) where Register(..)
) where
import Unicorn.Internal.Core (Reg) import Unicorn.Internal.Core (Reg)
{# context lib="unicorn" #} {# context lib = "unicorn" #}
#include <unicorn/mips.h> #include <unicorn/mips.h>
-- | MIPS registers. -- | MIPS registers.
{# enum UC_MIPS_REG as Register {# enum UC_MIPS_REG as Register
{underscoreToCase, { underscoreToCase
UC_MIPS_REG_0 as Reg0, , UC_MIPS_REG_0 as Reg0g
UC_MIPS_REG_1 as Reg1, , UC_MIPS_REG_1 as Reg1g
UC_MIPS_REG_2 as Reg2, , UC_MIPS_REG_2 as Reg2g
UC_MIPS_REG_3 as Reg3, , UC_MIPS_REG_3 as Reg3g
UC_MIPS_REG_4 as Reg4, , UC_MIPS_REG_4 as Reg4g
UC_MIPS_REG_5 as Reg5, , UC_MIPS_REG_5 as Reg5g
UC_MIPS_REG_6 as Reg6, , UC_MIPS_REG_6 as Reg6g
UC_MIPS_REG_7 as Reg7, , UC_MIPS_REG_7 as Reg7g
UC_MIPS_REG_8 as Reg8, , UC_MIPS_REG_8 as Reg8g
UC_MIPS_REG_9 as Reg9, , UC_MIPS_REG_9 as Reg9g
UC_MIPS_REG_10 as Reg10, , UC_MIPS_REG_10 as Reg10g
UC_MIPS_REG_11 as Reg11, , UC_MIPS_REG_11 as Reg11g
UC_MIPS_REG_12 as Reg12, , UC_MIPS_REG_12 as Reg12g
UC_MIPS_REG_13 as Reg13, , UC_MIPS_REG_13 as Reg13g
UC_MIPS_REG_14 as Reg14, , UC_MIPS_REG_14 as Reg14g
UC_MIPS_REG_15 as Reg15, , UC_MIPS_REG_15 as Reg15g
UC_MIPS_REG_16 as Reg16, , UC_MIPS_REG_16 as Reg16g
UC_MIPS_REG_17 as Reg17, , UC_MIPS_REG_17 as Reg17g
UC_MIPS_REG_18 as Reg18, , UC_MIPS_REG_18 as Reg18g
UC_MIPS_REG_19 as Reg19, , UC_MIPS_REG_19 as Reg19g
UC_MIPS_REG_20 as Reg20, , UC_MIPS_REG_20 as Reg20g
UC_MIPS_REG_21 as Reg21, , UC_MIPS_REG_21 as Reg21g
UC_MIPS_REG_22 as Reg22, , UC_MIPS_REG_22 as Reg22g
UC_MIPS_REG_23 as Reg23, , UC_MIPS_REG_23 as Reg23g
UC_MIPS_REG_24 as Reg24, , UC_MIPS_REG_24 as Reg24g
UC_MIPS_REG_25 as Reg25, , UC_MIPS_REG_25 as Reg25g
UC_MIPS_REG_26 as Reg26, , UC_MIPS_REG_26 as Reg26g
UC_MIPS_REG_27 as Reg27, , UC_MIPS_REG_27 as Reg27g
UC_MIPS_REG_28 as Reg28, , UC_MIPS_REG_28 as Reg28g
UC_MIPS_REG_29 as Reg29, , UC_MIPS_REG_29 as Reg29g
UC_MIPS_REG_30 as Reg30, , UC_MIPS_REG_30 as Reg30g
UC_MIPS_REG_31 as Reg31} , UC_MIPS_REG_31 as Reg31
omit (UC_MIPS_REG_INVALID, }
UC_MIPS_REG_ENDING) omit ( UC_MIPS_REG_INVALID
with prefix="UC_MIPS_REG_" , UC_MIPS_REG_ENDING
deriving (Show, Eq, Bounded) #} )
with prefix = "UC_MIPS_REG_"
deriving (Show, Eq, Bounded)
#}
instance Reg Register instance Reg Register

View file

@ -8,22 +8,25 @@ License : GPL-2
Definitions for the SPARC architecture. Definitions for the SPARC architecture.
-} -}
module Unicorn.CPU.Sparc ( module Unicorn.CPU.Sparc
Register(..), (
) where Register(..)
) where
import Unicorn.Internal.Core (Reg) import Unicorn.Internal.Core (Reg)
{# context lib="unicorn" #} {# context lib = "unicorn" #}
#include <unicorn/sparc.h> #include <unicorn/sparc.h>
-- | SPARC registers. -- | SPARC registers.
{# enum uc_sparc_reg as Register {# enum uc_sparc_reg as Register
{underscoreToCase} { underscoreToCase }
omit (UC_SPARC_REG_INVALID, omit (UC_SPARC_REG_INVALID
UC_SPARC_REG_ENDING) , UC_SPARC_REG_ENDING
with prefix="UC_SPARC_REG_" )
deriving (Show, Eq, Bounded) #} with prefix = "UC_SPARC_REG_"
deriving (Show, Eq, Bounded)
#}
instance Reg Register instance Reg Register

View file

@ -8,11 +8,12 @@ License : GPL-2
Definitions for the X86 architecture. Definitions for the X86 architecture.
-} -}
module Unicorn.CPU.X86 ( module Unicorn.CPU.X86
Mmr(..), (
Register(..), Mmr(..)
Instruction(..), , Register(..)
) where , Instruction(..)
) where
import Control.Applicative import Control.Applicative
import Data.Word import Data.Word
@ -20,18 +21,18 @@ import Foreign
import Unicorn.Internal.Core (Reg) import Unicorn.Internal.Core (Reg)
{# context lib="unicorn" #} {# context lib = "unicorn" #}
#include <unicorn/x86.h> #include <unicorn/x86.h>
-- | Memory-managemen Register for instructions IDTR, GDTR, LDTR, TR. -- | Memory-managemen 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
data Mmr = Mmr { data Mmr = Mmr
mmrSelector :: Word16, -- ^ Not used by GDTR and IDTR { mmrSelector :: Word16 -- ^ Not used by GDTR and IDTR
mmrBase :: Word64, -- ^ Handle 32 or 64 bit CPUs , mmrBase :: Word64 -- ^ Handle 32 or 64 bit CPUs
mmrLimit :: Word32, , mmrLimit :: Word32
mmrFlags :: Word32 -- ^ Not used by GDTR and IDTR , mmrFlags :: Word32 -- ^ Not used by GDTR and IDTR
} }
instance Storable Mmr where instance Storable Mmr where
sizeOf _ = {# sizeof uc_x86_mmr #} sizeOf _ = {# sizeof uc_x86_mmr #}
@ -48,18 +49,22 @@ instance Storable Mmr where
-- | X86 registers. -- | X86 registers.
{# enum uc_x86_reg as Register {# enum uc_x86_reg as Register
{underscoreToCase} { underscoreToCase }
omit (UC_X86_REG_INVALID, omit ( UC_X86_REG_INVALID
UC_X86_REG_ENDING) , UC_X86_REG_ENDING
with prefix="UC_X86_REG_" )
deriving (Show, Eq, Bounded) #} with prefix = "UC_X86_REG_"
deriving (Show, Eq, Bounded)
#}
instance Reg Register instance Reg Register
-- | X86 instructions. -- | X86 instructions.
{# enum uc_x86_insn as Instruction {# enum uc_x86_insn as Instruction
{underscoreToCase} { underscoreToCase }
omit (UC_X86_INS_INVALID, omit ( UC_X86_INS_INVALID
UC_X86_INS_ENDING) , UC_X86_INS_ENDING
with prefix="UC_X86_INS_" )
deriving (Show, Eq, Bounded) #} with prefix = "UC_X86_INS_"
deriving (Show, Eq, Bounded)
#}

View file

@ -6,36 +6,36 @@ License : GPL-2
Insert hook points into the Unicorn emulator engine. Insert hook points into the Unicorn emulator engine.
-} -}
module Unicorn.Hook ( module Unicorn.Hook
-- * Hook types ( -- * Hook types
Hook, Hook
MemoryHookType(..), , MemoryHookType(..)
MemoryEventHookType(..), , MemoryEventHookType(..)
MemoryAccess(..), , MemoryAccess(..)
-- * Hook callbacks -- * Hook callbacks
CodeHook, , CodeHook
InterruptHook, , InterruptHook
BlockHook, , BlockHook
InHook, , InHook
OutHook, , OutHook
SyscallHook, , SyscallHook
MemoryHook, , MemoryHook
MemoryReadHook, , MemoryReadHook
MemoryWriteHook, , MemoryWriteHook
MemoryEventHook, , MemoryEventHook
-- * Hook callback management -- * Hook callback management
codeHookAdd, , codeHookAdd
interruptHookAdd, , interruptHookAdd
blockHookAdd, , blockHookAdd
inHookAdd, , inHookAdd
outHookAdd, , outHookAdd
syscallHookAdd, , syscallHookAdd
memoryHookAdd, , memoryHookAdd
memoryEventHookAdd, , memoryEventHookAdd
hookDel, , hookDel
) where ) where
import Control.Monad import Control.Monad
import Control.Monad.Trans.Class import Control.Monad.Trans.Class
@ -213,7 +213,8 @@ hookDel uc hook = do
-- Takes the tuple returned by `ucHookAdd`, an IO (Error, Hook), and -- 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 -- returns either a `Right Hook` if no error occurred or a `Left Error` if an
-- error occurred -- error occurred
getResult :: IO (Error, Hook) -> IO (Either Error Hook) getResult :: IO (Error, Hook)
-> IO (Either Error Hook)
getResult = getResult =
liftM (uncurry checkResult) liftM (uncurry checkResult)
where checkResult err hook = where checkResult err hook =

View file

@ -17,31 +17,34 @@ import Control.Monad
import Control.Monad.Trans.Either (EitherT) import Control.Monad.Trans.Either (EitherT)
import Foreign import Foreign
{# context lib="unicorn" #} {# context lib = "unicorn" #}
#include <unicorn/unicorn.h> #include <unicorn/unicorn.h>
#include "unicorn_wrapper.h" #include "unicorn_wrapper.h"
-- | The Unicorn engine. -- | The Unicorn engine.
{# pointer *uc_engine as Engine {# pointer *uc_engine as Engine
foreign finalizer uc_close_wrapper as close foreign finalizer uc_close_wrapper as close
newtype #} newtype
#}
-- | A pointer to a Unicorn engine. -- | A pointer to a Unicorn engine.
{# pointer *uc_engine as EnginePtr -> Engine #} {# pointer *uc_engine as EnginePtr -> Engine #}
-- | Make a new Unicorn engine out of an engine pointer. The returned Unicorn -- | Make a new Unicorn engine out of an engine pointer. The returned Unicorn
-- engine will automatically call 'uc_close_wrapper' when it goes out of scope. -- engine will automatically call 'uc_close_wrapper' when it goes out of scope.
mkEngine :: EnginePtr -> IO Engine mkEngine :: EnginePtr
-> IO Engine
mkEngine ptr = mkEngine ptr =
liftM Engine (newForeignPtr close ptr) liftM Engine (newForeignPtr close ptr)
-- | Errors encountered by the Unicorn API. These values are returned by -- | Errors encountered by the Unicorn API. These values are returned by
-- 'errno'. -- 'errno'.
{# enum uc_err as Error {# enum uc_err as Error
{underscoreToCase} { underscoreToCase }
with prefix="UC_" with prefix = "UC_"
deriving (Show, Eq, Bounded) #} deriving (Show, Eq, Bounded)
#}
-- | The emulator runs in the IO monad and allows for the handling of errors -- | The emulator runs in the IO monad and allows for the handling of errors
-- "under the hood". -- "under the hood".

View file

@ -11,54 +11,54 @@ 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 This module should not be directly imported; it is only exposed because of the
way cabal handles ordering of chs files. way cabal handles ordering of chs files.
-} -}
module Unicorn.Internal.Hook ( module Unicorn.Internal.Hook
-- * Types ( -- * Types
Hook, Hook
HookType(..), , HookType(..)
MemoryHookType(..), , MemoryHookType(..)
MemoryEventHookType(..), , MemoryEventHookType(..)
MemoryAccess(..), , MemoryAccess(..)
-- * Hook callback bindings -- * Hook callback bindings
CodeHook, , CodeHook
InterruptHook, , InterruptHook
BlockHook, , BlockHook
InHook, , InHook
OutHook, , OutHook
SyscallHook, , SyscallHook
MemoryHook, , MemoryHook
MemoryReadHook, , MemoryReadHook
MemoryWriteHook, , MemoryWriteHook
MemoryEventHook, , MemoryEventHook
-- * Hook marshalling -- * Hook marshallin
marshalCodeHook, , marshalCodeHook
marshalInterruptHook, , marshalInterruptHook
marshalBlockHook, , marshalBlockHook
marshalInHook, , marshalInHook
marshalOutHook, , marshalOutHook
marshalSyscallHook, , marshalSyscallHook
marshalMemoryHook, , marshalMemoryHook
marshalMemoryReadHook, , marshalMemoryReadHook
marshalMemoryWriteHook, , marshalMemoryWriteHook
marshalMemoryEventHook, , marshalMemoryEventHook
-- * Hook registration and deletion bindings -- * Hook registration and deletion bindings
ucHookAdd, , ucHookAdd
ucInsnHookAdd, , ucInsnHookAdd
ucHookDel, , ucHookDel
) where ) where
import Control.Monad import Control.Monad
import Foreign import Foreign
import Unicorn.Internal.Util import Unicorn.Internal.Util
{# context lib="unicorn" #}
{# import Unicorn.Internal.Core #} {# import Unicorn.Internal.Core #}
{# import Unicorn.CPU.X86 #} {# import Unicorn.CPU.X86 #}
{# context lib = "unicorn" #}
#include <unicorn/unicorn.h> #include <unicorn/unicorn.h>
#include "unicorn_wrapper.h" #include "unicorn_wrapper.h"
@ -79,7 +79,8 @@ import Unicorn.Internal.Util
foreign import ccall "&uc_close_dummy" foreign import ccall "&uc_close_dummy"
closeDummy :: FunPtr (EnginePtr -> IO ()) closeDummy :: FunPtr (EnginePtr -> IO ())
mkEngineNC :: EnginePtr -> IO Engine mkEngineNC :: EnginePtr
-> IO Engine
mkEngineNC ptr = mkEngineNC ptr =
liftM Engine (newForeignPtr closeDummy ptr) liftM Engine (newForeignPtr closeDummy ptr)
@ -92,47 +93,55 @@ type Hook = {# type uc_hook #}
-- Note that the both valid and invalid memory access hooks are omitted from -- Note that the both valid and invalid memory access hooks are omitted from
-- this enum (and are exposed to the user). -- this enum (and are exposed to the user).
{# enum uc_hook_type as HookType {# enum uc_hook_type as HookType
{underscoreToCase} { underscoreToCase }
omit (UC_HOOK_MEM_READ_UNMAPPED, omit ( UC_HOOK_MEM_READ_UNMAPPED
UC_HOOK_MEM_WRITE_UNMAPPED, , UC_HOOK_MEM_WRITE_UNMAPPED
UC_HOOK_MEM_FETCH_UNMAPPED, , UC_HOOK_MEM_FETCH_UNMAPPED
UC_HOOK_MEM_READ_PROT, , UC_HOOK_MEM_READ_PROT
UC_HOOK_MEM_WRITE_PROT, , UC_HOOK_MEM_WRITE_PROT
UC_HOOK_MEM_FETCH_PROT, , UC_HOOK_MEM_FETCH_PROT
UC_HOOK_MEM_READ, , UC_HOOK_MEM_READ
UC_HOOK_MEM_WRITE, , UC_HOOK_MEM_WRITE
UC_HOOK_MEM_FETCH) , UC_HOOK_MEM_FETCH
with prefix="UC_" , UC_HOOK_MEM_READ_AFTER
deriving (Show, Eq, Bounded) #} )
with prefix = "UC_"
deriving (Show, Eq, Bounded)
#}
-- | Memory hook types (for valid memory accesses). -- | Memory hook types (for valid memory accesses).
{# enum uc_hook_type as MemoryHookType {# enum uc_hook_type as MemoryHookType
{underscoreToCase} { underscoreToCase }
omit (UC_HOOK_INTR, omit ( UC_HOOK_INTR
UC_HOOK_INSN, , UC_HOOK_INSN
UC_HOOK_CODE, , UC_HOOK_CODE
UC_HOOK_BLOCK, , UC_HOOK_BLOCK
UC_HOOK_MEM_READ_UNMAPPED, , UC_HOOK_MEM_READ_UNMAPPED
UC_HOOK_MEM_WRITE_UNMAPPED, , UC_HOOK_MEM_WRITE_UNMAPPED
UC_HOOK_MEM_FETCH_UNMAPPED, , UC_HOOK_MEM_FETCH_UNMAPPED
UC_HOOK_MEM_READ_PROT, , UC_HOOK_MEM_READ_PROT
UC_HOOK_MEM_WRITE_PROT, , UC_HOOK_MEM_WRITE_PROT
UC_HOOK_MEM_FETCH_PROT) , UC_HOOK_MEM_FETCH_PROT
with prefix="UC_" )
deriving (Show, Eq, Bounded) #} with prefix = "UC_"
deriving (Show, Eq, Bounded)
#}
-- | Memory event hook types (for invalid memory accesses). -- | Memory event hook types (for invalid memory accesses).
{# enum uc_hook_type as MemoryEventHookType {# enum uc_hook_type as MemoryEventHookType
{underscoreToCase} { underscoreToCase }
omit (UC_HOOK_INTR, omit ( UC_HOOK_INTR
UC_HOOK_INSN, , UC_HOOK_INSN
UC_HOOK_CODE, , UC_HOOK_CODE
UC_HOOK_BLOCK, , UC_HOOK_BLOCK
UC_HOOK_MEM_READ, , UC_HOOK_MEM_READ
UC_HOOK_MEM_WRITE, , UC_HOOK_MEM_WRITE
UC_HOOK_MEM_FETCH) , UC_HOOK_MEM_FETCH
with prefix="UC_" , UC_HOOK_MEM_READ_AFTER
deriving (Show, Eq, Bounded) #} )
with prefix = "UC_"
deriving (Show, Eq, Bounded)
#}
-- | Unify the hook types with a type class -- | Unify the hook types with a type class
class Enum a => HookTypeC a class Enum a => HookTypeC a
@ -143,9 +152,10 @@ instance HookTypeC MemoryEventHookType
-- | Memory access. -- | Memory access.
{# enum uc_mem_type as MemoryAccess {# enum uc_mem_type as MemoryAccess
{underscoreToCase} { underscoreToCase }
with prefix="UC_" with prefix = "UC_"
deriving (Show, Eq, Bounded) #} deriving (Show, Eq, Bounded)
#}
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Hook callbacks -- Hook callbacks
@ -159,16 +169,18 @@ type CodeHook a = Engine -- ^ 'Unicorn' engine handle
-> a -- ^ User data passed to tracing APIs -> a -- ^ User data passed to tracing APIs
-> IO () -> IO ()
type CCodeHook = EnginePtr -> Word64 -> Word32 -> Ptr () -> IO () type CCodeHook = EnginePtr -> Word64 -> Word32 -> Ptr () -> IO ()
foreign import ccall "wrapper" foreign import ccall "wrapper"
mkCodeHook :: CCodeHook -> IO {# type uc_cb_hookcode_t #} mkCodeHook :: CCodeHook
-> IO {# type uc_cb_hookcode_t #}
marshalCodeHook :: Storable a marshalCodeHook :: Storable a
=> CodeHook a -> IO {# type uc_cb_hookcode_t #} => CodeHook a
-> IO {# type uc_cb_hookcode_t #}
marshalCodeHook codeHook = marshalCodeHook codeHook =
mkCodeHook $ \ucPtr address size userDataPtr -> do mkCodeHook $ \ucPtr address size userDataPtr -> do
uc <- mkEngineNC ucPtr uc <- mkEngineNC ucPtr
userData <- castPtrAndPeek userDataPtr userData <- castPtrAndPeek userDataPtr
let maybeSize = if size == 0 then Nothing let maybeSize = if size == 0 then Nothing
else Just $ fromIntegral size else Just $ fromIntegral size
@ -186,10 +198,11 @@ foreign import ccall "wrapper"
mkInterruptHook :: CInterruptHook -> IO {# type uc_cb_hookintr_t #} mkInterruptHook :: CInterruptHook -> IO {# type uc_cb_hookintr_t #}
marshalInterruptHook :: Storable a marshalInterruptHook :: Storable a
=> InterruptHook a -> IO {# type uc_cb_hookintr_t #} => InterruptHook a
-> IO {# type uc_cb_hookintr_t #}
marshalInterruptHook interruptHook = marshalInterruptHook interruptHook =
mkInterruptHook $ \ucPtr intNo userDataPtr -> do mkInterruptHook $ \ucPtr intNo userDataPtr -> do
uc <- mkEngineNC ucPtr uc <- mkEngineNC ucPtr
userData <- castPtrAndPeek userDataPtr userData <- castPtrAndPeek userDataPtr
interruptHook uc (fromIntegral intNo) userData interruptHook uc (fromIntegral intNo) userData
@ -197,7 +210,8 @@ marshalInterruptHook interruptHook =
type BlockHook a = CodeHook a type BlockHook a = CodeHook a
marshalBlockHook :: Storable a marshalBlockHook :: Storable a
=> BlockHook a -> IO {# type uc_cb_hookcode_t #} => BlockHook a
-> IO {# type uc_cb_hookcode_t #}
marshalBlockHook = marshalBlockHook =
marshalCodeHook marshalCodeHook
@ -214,10 +228,11 @@ foreign import ccall "wrapper"
mkInHook :: CInHook -> IO {# type uc_cb_insn_in_t #} mkInHook :: CInHook -> IO {# type uc_cb_insn_in_t #}
marshalInHook :: Storable a marshalInHook :: Storable a
=> InHook a -> IO {# type uc_cb_insn_in_t #} => InHook a
-> IO {# type uc_cb_insn_in_t #}
marshalInHook inHook = marshalInHook inHook =
mkInHook $ \ucPtr port size userDataPtr -> do mkInHook $ \ucPtr port size userDataPtr -> do
uc <- mkEngineNC ucPtr uc <- mkEngineNC ucPtr
userData <- castPtrAndPeek userDataPtr userData <- castPtrAndPeek userDataPtr
inHook uc (fromIntegral port) (fromIntegral size) userData inHook uc (fromIntegral port) (fromIntegral size) userData
@ -232,13 +247,15 @@ type OutHook a = Engine -- ^ 'Unicorn' engine handle
type COutHook = EnginePtr -> Word32 -> Int32 -> Word32 -> Ptr () -> IO () type COutHook = EnginePtr -> Word32 -> Int32 -> Word32 -> Ptr () -> IO ()
foreign import ccall "wrapper" foreign import ccall "wrapper"
mkOutHook :: COutHook -> IO {# type uc_cb_insn_out_t #} mkOutHook :: COutHook
-> IO {# type uc_cb_insn_out_t #}
marshalOutHook :: Storable a marshalOutHook :: Storable a
=> OutHook a -> IO {# type uc_cb_insn_out_t #} => OutHook a
-> IO {# type uc_cb_insn_out_t #}
marshalOutHook outHook = marshalOutHook outHook =
mkOutHook $ \ucPtr port size value userDataPtr -> do mkOutHook $ \ucPtr port size value userDataPtr -> do
uc <- mkEngineNC ucPtr uc <- mkEngineNC ucPtr
userData <- castPtrAndPeek userDataPtr userData <- castPtrAndPeek userDataPtr
outHook uc (fromIntegral port) (fromIntegral size) (fromIntegral value) outHook uc (fromIntegral port) (fromIntegral size) (fromIntegral value)
userData userData
@ -251,13 +268,15 @@ type SyscallHook a = Engine -- ^ 'Unicorn' engine handle
type CSyscallHook = Ptr () -> Ptr () -> IO () type CSyscallHook = Ptr () -> Ptr () -> IO ()
foreign import ccall "wrapper" foreign import ccall "wrapper"
mkSyscallHook :: CSyscallHook -> IO {# type uc_cb_insn_syscall_t #} mkSyscallHook :: CSyscallHook
-> IO {# type uc_cb_insn_syscall_t #}
marshalSyscallHook :: Storable a marshalSyscallHook :: Storable a
=> SyscallHook a -> IO {# type uc_cb_insn_syscall_t #} => SyscallHook a
-> IO {# type uc_cb_insn_syscall_t #}
marshalSyscallHook syscallHook = marshalSyscallHook syscallHook =
mkSyscallHook $ \ucPtr userDataPtr -> do mkSyscallHook $ \ucPtr userDataPtr -> do
uc <- mkEngineNC $ castPtr ucPtr uc <- mkEngineNC $ castPtr ucPtr
userData <- castPtrAndPeek userDataPtr userData <- castPtrAndPeek userDataPtr
syscallHook uc userData syscallHook uc userData
@ -281,13 +300,15 @@ type CMemoryHook = EnginePtr
-> IO () -> IO ()
foreign import ccall "wrapper" foreign import ccall "wrapper"
mkMemoryHook :: CMemoryHook -> IO {# type uc_cb_hookmem_t #} mkMemoryHook :: CMemoryHook
-> IO {# type uc_cb_hookmem_t #}
marshalMemoryHook :: Storable a marshalMemoryHook :: Storable a
=> MemoryHook a -> IO {# type uc_cb_hookmem_t #} => MemoryHook a
-> IO {# type uc_cb_hookmem_t #}
marshalMemoryHook memoryHook = marshalMemoryHook memoryHook =
mkMemoryHook $ \ucPtr memAccessI address size value userDataPtr -> do mkMemoryHook $ \ucPtr memAccessI address size value userDataPtr -> do
uc <- mkEngineNC ucPtr uc <- mkEngineNC ucPtr
userData <- castPtrAndPeek userDataPtr userData <- castPtrAndPeek userDataPtr
let memAccess = toMemAccess memAccessI let memAccess = toMemAccess memAccessI
maybeValue = case memAccess of maybeValue = case memAccess of
@ -304,10 +325,11 @@ type MemoryReadHook a = Engine -- ^ 'Unicorn' engine handle
-> IO () -> IO ()
marshalMemoryReadHook :: Storable a marshalMemoryReadHook :: Storable a
=> MemoryReadHook a -> IO {# type uc_cb_hookmem_t #} => MemoryReadHook a
-> IO {# type uc_cb_hookmem_t #}
marshalMemoryReadHook memoryReadHook = marshalMemoryReadHook memoryReadHook =
mkMemoryHook $ \ucPtr _ address size _ userDataPtr -> do mkMemoryHook $ \ucPtr _ address size _ userDataPtr -> do
uc <- mkEngineNC ucPtr uc <- mkEngineNC ucPtr
userData <- castPtrAndPeek userDataPtr userData <- castPtrAndPeek userDataPtr
memoryReadHook uc address (fromIntegral size) userData memoryReadHook uc address (fromIntegral size) userData
@ -321,10 +343,11 @@ type MemoryWriteHook a = Engine -- ^ 'Unicorn' engine handle
-> IO () -> IO ()
marshalMemoryWriteHook :: Storable a marshalMemoryWriteHook :: Storable a
=> MemoryWriteHook a -> IO {# type uc_cb_hookmem_t #} => MemoryWriteHook a
-> IO {# type uc_cb_hookmem_t #}
marshalMemoryWriteHook memoryWriteHook = marshalMemoryWriteHook memoryWriteHook =
mkMemoryHook $ \ucPtr _ address size value userDataPtr -> do mkMemoryHook $ \ucPtr _ address size value userDataPtr -> do
uc <- mkEngineNC ucPtr uc <- mkEngineNC ucPtr
userData <- castPtrAndPeek userDataPtr userData <- castPtrAndPeek userDataPtr
memoryWriteHook uc address (fromIntegral size) (fromIntegral value) memoryWriteHook uc address (fromIntegral size) (fromIntegral value)
userData userData
@ -351,15 +374,17 @@ type CMemoryEventHook = EnginePtr
-> IO Int32 -> IO Int32
foreign import ccall "wrapper" foreign import ccall "wrapper"
mkMemoryEventHook :: CMemoryEventHook -> IO {# type uc_cb_eventmem_t #} mkMemoryEventHook :: CMemoryEventHook
-> IO {# type uc_cb_eventmem_t #}
marshalMemoryEventHook :: Storable a marshalMemoryEventHook :: Storable a
=> MemoryEventHook a -> IO {# type uc_cb_eventmem_t #} => MemoryEventHook a
-> IO {# type uc_cb_eventmem_t #}
marshalMemoryEventHook eventMemoryHook = marshalMemoryEventHook eventMemoryHook =
mkMemoryEventHook $ \ucPtr memAccessI address size value userDataPtr -> do mkMemoryEventHook $ \ucPtr memAccessI address size value userDataPtr -> do
uc <- mkEngineNC ucPtr uc <- mkEngineNC ucPtr
userData <- castPtrAndPeek userDataPtr userData <- castPtrAndPeek userDataPtr
let memAccess = toMemAccess memAccessI let memAccess = toMemAccess memAccessI
maybeValue = case memAccess of maybeValue = case memAccess of
MemReadUnmapped -> Nothing MemReadUnmapped -> Nothing
MemReadProt -> Nothing MemReadProt -> Nothing
@ -369,7 +394,7 @@ marshalMemoryEventHook eventMemoryHook =
res <- eventMemoryHook uc memAccess address (fromIntegral size) res <- eventMemoryHook uc memAccess address (fromIntegral size)
maybeValue userData maybeValue userData
return $ boolToInt res return $ boolToInt res
where boolToInt True = 1 where boolToInt True = 1
boolToInt False = 0 boolToInt False = 0
@ -378,38 +403,43 @@ marshalMemoryEventHook eventMemoryHook =
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
{# fun variadic uc_hook_add as ucHookAdd {# fun variadic uc_hook_add as ucHookAdd
`(Storable a, HookTypeC h)' => `HookTypeC h' =>
{`Engine', { `Engine'
alloca- `Hook' peek*, , alloca- `Hook' peek*
enumToNum `h', , enumToNum `h'
castFunPtrToPtr `FunPtr b', , castFunPtrToPtr `FunPtr b'
castPtr `Ptr a', , castPtr `Ptr a'
`Word64', , `Word64'
`Word64'} , `Word64'
-> `Error' #} } -> `Error'
#}
{# fun variadic uc_hook_add[int] as ucInsnHookAdd {# fun variadic uc_hook_add[int] as ucInsnHookAdd
`(Storable a, HookTypeC h)' => `HookTypeC h' =>
{`Engine', { `Engine'
alloca- `Hook' peek*, , alloca- `Hook' peek*
enumToNum `h', , enumToNum `h'
castFunPtrToPtr `FunPtr b', , castFunPtrToPtr `FunPtr b'
castPtr `Ptr a', , castPtr `Ptr a'
`Word64', , `Word64'
`Word64', , `Word64'
enumToNum `Instruction'} , enumToNum `Instruction'
-> `Error' #} } -> `Error'
#}
-- | Unregister (remove) a hook callback. -- | Unregister (remove) a hook callback.
{# fun uc_hook_del as ^ {# fun uc_hook_del as ^
{`Engine', { `Engine'
fromIntegral `Hook'} , fromIntegral `Hook'
-> `Error' #} } -> `Error'
#}
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Helper functions -- Helper functions
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
toMemAccess :: Integral a => a -> MemoryAccess toMemAccess :: Integral a
=> a
-> MemoryAccess
toMemAccess = toMemAccess =
toEnum . fromIntegral toEnum . fromIntegral

View file

@ -12,33 +12,39 @@ Low-level bindings for the Unicorn CPU emulator framework.
This module should not be directly imported; it is only exposed because of the This module should not be directly imported; it is only exposed because of the
way cabal handles ordering of chs files. way cabal handles ordering of chs files.
-} -}
module Unicorn.Internal.Unicorn ( module Unicorn.Internal.Unicorn
-- * Types ( -- * Types
Architecture(..), Architecture(..)
Mode(..), , Mode(..)
MemoryPermission(..), , MemoryPermission(..)
MemoryRegion(..), , MemoryRegion(..)
QueryType(..), , QueryType(..)
, Context
-- * Function bindings -- * Function bindings
ucOpen, , ucOpen
ucQuery, , ucQuery
ucEmuStart, , ucEmuStart
ucEmuStop, , ucEmuStop
ucRegWrite, , ucRegWrite
ucRegRead, , ucRegRead
ucMemWrite, , ucMemWrite
ucMemRead, , ucMemRead
ucMemMap, , ucMemMap
ucMemUnmap, , ucMemUnmap
ucMemProtect, , ucMemProtect
ucMemRegions, , ucMemRegions
ucVersion, , mkContext
ucErrno, , ucContextAlloc
ucStrerror, , ucContextSave
) where , ucContextRestore
, ucVersion
, ucErrno
, ucStrerror
) where
import Control.Applicative import Control.Applicative
import Control.Monad
import Data.ByteString (ByteString, useAsCStringLen) import Data.ByteString (ByteString, useAsCStringLen)
import Foreign import Foreign
import Foreign.C import Foreign.C
@ -46,11 +52,12 @@ import Prelude hiding (until)
import Unicorn.Internal.Util import Unicorn.Internal.Util
{# context lib="unicorn" #}
{# import Unicorn.Internal.Core #} {# import Unicorn.Internal.Core #}
{# context lib = "unicorn" #}
#include <unicorn/unicorn.h> #include <unicorn/unicorn.h>
#include "unicorn_wrapper.h"
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Types -- Types
@ -58,29 +65,33 @@ import Unicorn.Internal.Util
-- | CPU architecture. -- | CPU architecture.
{# enum uc_arch as Architecture {# enum uc_arch as Architecture
{underscoreToCase} { underscoreToCase }
with prefix="UC_" with prefix = "UC_"
deriving (Show, Eq, Bounded) #} deriving (Show, Eq, Bounded)
#}
-- | CPU hardware mode. -- | CPU hardware mode.
{# enum uc_mode as Mode {# enum uc_mode as Mode
{underscoreToCase} { underscoreToCase }
with prefix="UC_" with prefix = "UC_"
deriving (Show, Eq, Bounded) #} deriving (Show, Eq, Bounded)
#}
-- | Memory permissions. -- | Memory permissions.
{# enum uc_prot as MemoryPermission {# enum uc_prot as MemoryPermission
{underscoreToCase} { underscoreToCase }
with prefix="UC_" with prefix = "UC_"
deriving (Show, Eq, Bounded) #} deriving (Show, Eq, Bounded)
#}
-- | Memory region mapped by 'memMap'. Retrieve the list of memory regions with -- | Memory region mapped by 'memMap'. Retrieve the list of memory regions with
-- 'memRegions'. -- 'memRegions'.
data MemoryRegion = MemoryRegion { data MemoryRegion = MemoryRegion
mrBegin :: Word64, -- ^ Begin address of the region (inclusive) {
mrEnd :: Word64, -- ^ End address of the region (inclusive) mrBegin :: Word64 -- ^ Begin address of the region (inclusive)
mrPerms :: [MemoryPermission] -- ^ Memory permissions of the region , mrEnd :: Word64 -- ^ End address of the region (inclusive)
} , mrPerms :: [MemoryPermission] -- ^ Memory permissions of the region
}
instance Storable MemoryRegion where instance Storable MemoryRegion where
sizeOf _ = {# sizeof uc_mem_region #} sizeOf _ = {# sizeof uc_mem_region #}
@ -99,121 +110,174 @@ instance Storable MemoryRegion where
-- | Query types for the 'query' API. -- | Query types for the 'query' API.
{# enum uc_query_type as QueryType {# enum uc_query_type as QueryType
{underscoreToCase} { underscoreToCase }
with prefix="UC_" with prefix = "UC_"
deriving (Show, Eq, Bounded) #} deriving (Show, Eq, Bounded)
#}
-- | Opaque storage for CPU context, used with the context functions.
{# pointer *uc_context as Context
foreign finalizer uc_context_free_wrapper as contextFree
newtype
#}
-- | A pointer to a CPU context.
{# pointer *uc_context as ContextPtr -> Context #}
-- | Make a CPU context out of a context pointer. The returned CPU context will
-- automatically call 'uc_context_free' when it goes out of scope.
mkContext :: ContextPtr
-> IO Context
mkContext ptr =
liftM Context (newForeignPtr contextFree ptr)
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Emulator control -- Emulator control
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
{# fun uc_open as ^ {# fun uc_open as ^
{`Architecture', { `Architecture'
combineEnums `[Mode]', , combineEnums `[Mode]'
alloca- `EnginePtr' peek*} , alloca- `EnginePtr' peek*
-> `Error' #} } -> `Error'
#}
{# fun uc_query as ^ {# fun uc_query as ^
{`Engine', { `Engine'
`QueryType', , `QueryType'
alloca- `Int' castPtrAndPeek*} , alloca- `Int' castPtrAndPeek*
-> `Error' #} } -> `Error'
#}
{# fun uc_emu_start as ^ {# fun uc_emu_start as ^
{`Engine', { `Engine'
`Word64', , `Word64'
`Word64', , `Word64'
`Int', , `Int'
`Int'} , `Int'} -> `Error'
-> `Error' #} #}
{# fun uc_emu_stop as ^ {# fun uc_emu_stop as ^
{`Engine'} { `Engine'
-> `Error' #} } -> `Error'
#}
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Register operations -- Register operations
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
{# fun uc_reg_write as ^ {# fun uc_reg_write as ^
`Reg r' => `Reg r' =>
{`Engine', { `Engine'
enumToNum `r', , enumToNum `r'
castPtr `Ptr Int64'} , castPtr `Ptr Int64'
-> `Error' #} } -> `Error'
#}
{# fun uc_reg_read as ^ {# fun uc_reg_read as ^
`Reg r' => `Reg r' =>
{`Engine', { `Engine'
enumToNum `r', , enumToNum `r'
allocaInt64ToVoid- `Int64' castPtrAndPeek*} , allocaInt64ToVoid- `Int64' castPtrAndPeek*
-> `Error' #} } -> `Error'
#}
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Memory operations -- Memory operations
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
{# fun uc_mem_write as ^ {# fun uc_mem_write as ^
{`Engine', { `Engine'
`Word64', , `Word64'
withByteStringLen* `ByteString'&} , withByteStringLen* `ByteString'&
-> `Error' #} } -> `Error'
#}
{# fun uc_mem_read as ^ {# fun uc_mem_read as ^
{`Engine', { `Engine'
`Word64', , `Word64'
castPtr `Ptr Word8', , castPtr `Ptr Word8'
`Int'} , `Int'} -> `Error'
-> `Error' #} #}
{# fun uc_mem_map as ^ {# fun uc_mem_map as ^
{`Engine', { `Engine'
`Word64', , `Word64'
`Int', , `Int'
combineEnums `[MemoryPermission]'} , combineEnums `[MemoryPermission]'
-> `Error' #} } -> `Error' #}
{# fun uc_mem_unmap as ^ {# fun uc_mem_unmap as ^
{`Engine', { `Engine'
`Word64', , `Word64'
`Int'} , `Int'
-> `Error' #} } -> `Error'
#}
{# fun uc_mem_protect as ^ {# fun uc_mem_protect as ^
{`Engine', { `Engine'
`Word64', , `Word64'
`Int', , `Int'
combineEnums `[MemoryPermission]'} , combineEnums `[MemoryPermission]'
-> `Error' #} } -> `Error'
#}
{# fun uc_mem_regions as ^ {# fun uc_mem_regions as ^
{`Engine', { `Engine'
alloca- `MemoryRegionPtr' peek*, , alloca- `MemoryRegionPtr' peek*
alloca- `Int' castPtrAndPeek*} , alloca- `Int' castPtrAndPeek*
-> `Error' #} } -> `Error'
#}
-------------------------------------------------------------------------------
-- Context
-------------------------------------------------------------------------------
{# fun uc_context_alloc as ^
{ `Engine'
, alloca- `ContextPtr' peek*
} -> `Error'
#}
{# fun uc_context_save as ^
{ `Engine'
, `Context'
} -> `Error'
#}
{# fun uc_context_restore as ^
{ `Engine'
, `Context'
} -> `Error'
#}
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Misc. -- Misc.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
{# fun pure unsafe uc_version as ^ {# fun pure unsafe uc_version as ^
{id `Ptr CUInt', { id `Ptr CUInt'
id `Ptr CUInt'} , id `Ptr CUInt'
-> `Int' #} } -> `Int'
#}
{# fun unsafe uc_errno as ^ {# fun unsafe uc_errno as ^
{`Engine'} { `Engine'
-> `Error' #} } -> `Error'
#}
{# fun pure unsafe uc_strerror as ^ {# fun pure unsafe uc_strerror as ^
{`Error'} { `Error'
-> `String' #} } -> `String'
#}
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Helper functions -- Helper functions
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
expandMemPerms :: (Integral a, Bits a) => a -> [MemoryPermission] expandMemPerms :: (Integral a, Bits a)
=> a
-> [MemoryPermission]
expandMemPerms perms = expandMemPerms perms =
-- Only interested in the 3 least-significant bits -- Only interested in the 3 least-significant bits
let maskedPerms = fromIntegral $ perms .&. 0x7 in let maskedPerms = fromIntegral $ perms .&. 0x7 in
@ -232,10 +296,13 @@ expandMemPerms perms =
checkRWE _ [] = checkRWE _ [] =
[] []
allocaInt64ToVoid :: (Ptr () -> IO b) -> IO b allocaInt64ToVoid :: (Ptr () -> IO b)
-> IO b
allocaInt64ToVoid f = allocaInt64ToVoid f =
alloca $ \(ptr :: Ptr Int64) -> poke ptr 0 >> f (castPtr ptr) alloca $ \(ptr :: Ptr Int64) -> poke ptr 0 >> f (castPtr ptr)
withByteStringLen :: ByteString -> ((Ptr (), CULong) -> IO a) -> IO a withByteStringLen :: ByteString
-> ((Ptr (), CULong) -> IO a)
-> IO a
withByteStringLen bs f = withByteStringLen bs f =
useAsCStringLen bs $ \(ptr, len) -> f (castPtr ptr, fromIntegral len) useAsCStringLen bs $ \(ptr, len) -> f (castPtr ptr, fromIntegral len)

View file

@ -10,16 +10,22 @@ import Data.Bits
import Foreign import Foreign
-- | Combine a list of Enums by performing a bitwise-OR. -- | Combine a list of Enums by performing a bitwise-OR.
combineEnums :: (Enum a, Num b, Bits b) => [a] -> b combineEnums :: (Enum a, Num b, Bits b)
=> [a]
-> b
combineEnums = combineEnums =
foldr ((.|.) <$> enumToNum) 0 foldr ((.|.) <$> enumToNum) 0
-- | Cast a pointer and then peek inside it. -- | Cast a pointer and then peek inside it.
castPtrAndPeek :: Storable a => Ptr b -> IO a castPtrAndPeek :: Storable a
=> Ptr b
-> IO a
castPtrAndPeek = castPtrAndPeek =
peek . castPtr peek . castPtr
-- | Convert an 'Eum' to a 'Num'. -- | Convert an 'Eum' to a 'Num'.
enumToNum :: (Enum a, Num b) => a -> b enumToNum :: (Enum a, Num b)
=> a
-> b
enumToNum = enumToNum =
fromIntegral . fromEnum fromIntegral . fromEnum

View file

@ -6,3 +6,7 @@ void uc_close_wrapper(uc_engine *uc) {
void uc_close_dummy(uc_engine *uc) { void uc_close_dummy(uc_engine *uc) {
} }
void uc_context_free_wrapper(uc_context *context) {
uc_context_free(context);
}

View file

@ -13,4 +13,9 @@ void uc_close_wrapper(uc_engine *uc);
*/ */
void uc_close_dummy(uc_engine *uc); void uc_close_dummy(uc_engine *uc);
/*
* Wrap Unicorn's uc_context_free function and ignore the returned error code.
*/
void uc_context_free_wrapper(uc_context *context);
#endif #endif

View file

@ -13,8 +13,9 @@ copyright: (c) 2016, Adrian Herrera
category: System category: System
build-type: Simple build-type: Simple
stability: experimental stability: experimental
cabal-version: >=1.10 cabal-version: >= 1.10
extra-source-files: cbits/, include/ extra-source-files: cbits/
, include/
library library
exposed-modules: Unicorn.Internal.Core exposed-modules: Unicorn.Internal.Core
@ -29,10 +30,10 @@ library
Unicorn.Hook Unicorn.Hook
Unicorn Unicorn
other-modules: Unicorn.Internal.Util other-modules: Unicorn.Internal.Util
build-depends: base >=4 && <5, build-depends: base >=4 && <5
bytestring >= 0.9.1, , bytestring >= 0.9.1
transformers < 0.6, , transformers < 0.6
either >= 4.4 , either >= 4.4
hs-source-dirs: src hs-source-dirs: src
c-sources: src/cbits/unicorn_wrapper.c c-sources: src/cbits/unicorn_wrapper.c
include-dirs: src/include include-dirs: src/include

View file

@ -21,7 +21,7 @@ def hook_block(uc, address, size, user_data):
# callback for tracing instructions # callback for tracing instructions
def hook_code(uc, address, size, user_data): def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size)) print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
# Test ARM # Test ARM
@ -46,7 +46,7 @@ def test_arm():
mu.hook_add(UC_HOOK_BLOCK, hook_block) mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions with customized callback # tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code) mu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=ADDRESS)
# emulate machine code in infinite time # emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(ARM_CODE)) mu.emu_start(ADDRESS, ADDRESS + len(ARM_CODE))
@ -100,5 +100,5 @@ def test_thumb():
if __name__ == '__main__': if __name__ == '__main__':
test_arm() test_arm()
print("=" * 20) print("=" * 26)
test_thumb() test_thumb()

View file

@ -21,7 +21,7 @@ def hook_block(uc, address, size, user_data):
# callback for tracing instructions # callback for tracing instructions
def hook_code(uc, address, size, user_data): def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size)) print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
# Test ARM64 # Test ARM64

View file

@ -20,7 +20,7 @@ def hook_block(uc, address, size, user_data):
# callback for tracing instructions # callback for tracing instructions
def hook_code(uc, address, size, user_data): def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size)) print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
# Test ARM # Test ARM
@ -51,8 +51,34 @@ def test_m68k():
# now print out some registers # now print out some registers
print(">>> Emulation done. Below is the CPU context") print(">>> Emulation done. Below is the CPU context")
a0 = mu.reg_read(UC_M68K_REG_A0)
a1 = mu.reg_read(UC_M68K_REG_A1)
a2 = mu.reg_read(UC_M68K_REG_A2)
a3 = mu.reg_read(UC_M68K_REG_A3)
a4 = mu.reg_read(UC_M68K_REG_A4)
a5 = mu.reg_read(UC_M68K_REG_A5)
a6 = mu.reg_read(UC_M68K_REG_A6)
a7 = mu.reg_read(UC_M68K_REG_A7)
d0 = mu.reg_read(UC_M68K_REG_D0)
d1 = mu.reg_read(UC_M68K_REG_D1)
d2 = mu.reg_read(UC_M68K_REG_D2)
d3 = mu.reg_read(UC_M68K_REG_D3) d3 = mu.reg_read(UC_M68K_REG_D3)
print(">>> D3 = 0x%x" %d3) d4 = mu.reg_read(UC_M68K_REG_D4)
d5 = mu.reg_read(UC_M68K_REG_D5)
d6 = mu.reg_read(UC_M68K_REG_D6)
d7 = mu.reg_read(UC_M68K_REG_D7)
pc = mu.reg_read(UC_M68K_REG_PC)
sr = mu.reg_read(UC_M68K_REG_SR)
print(">>> A0 = 0x%x\t\t>>> D0 = 0x%x" % (a0, d0))
print(">>> A1 = 0x%x\t\t>>> D1 = 0x%x" % (a1, d1))
print(">>> A2 = 0x%x\t\t>>> D2 = 0x%x" % (a2, d2))
print(">>> A3 = 0x%x\t\t>>> D3 = 0x%x" % (a3, d3))
print(">>> A4 = 0x%x\t\t>>> D4 = 0x%x" % (a4, d4))
print(">>> A5 = 0x%x\t\t>>> D5 = 0x%x" % (a5, d5))
print(">>> A6 = 0x%x\t\t>>> D6 = 0x%x" % (a6, d6))
print(">>> A7 = 0x%x\t\t>>> D7 = 0x%x" % (a7, d7))
print(">>> PC = 0x%x" % pc)
print(">>> SR = 0x%x" % sr)
except UcError as e: except UcError as e:
print("ERROR: %s" % e) print("ERROR: %s" % e)

View file

@ -22,7 +22,7 @@ def hook_block(uc, address, size, user_data):
# callback for tracing instructions # callback for tracing instructions
def hook_code(uc, address, size, user_data): def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size)) print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
# Test MIPS EB # Test MIPS EB
@ -54,7 +54,7 @@ def test_mips_eb():
print(">>> Emulation done. Below is the CPU context") print(">>> Emulation done. Below is the CPU context")
r1 = mu.reg_read(UC_MIPS_REG_1) r1 = mu.reg_read(UC_MIPS_REG_1)
print(">>> r1 = 0x%x" %r1) print(">>> R1 = 0x%x" %r1)
except UcError as e: except UcError as e:
print("ERROR: %s" % e) print("ERROR: %s" % e)
@ -89,7 +89,7 @@ def test_mips_el():
print(">>> Emulation done. Below is the CPU context") print(">>> Emulation done. Below is the CPU context")
r1 = mu.reg_read(UC_MIPS_REG_1) r1 = mu.reg_read(UC_MIPS_REG_1)
print(">>> r1 = 0x%x" %r1) print(">>> R1 = 0x%x" %r1)
except UcError as e: except UcError as e:
print("ERROR: %s" % e) print("ERROR: %s" % e)
@ -97,5 +97,5 @@ def test_mips_el():
if __name__ == '__main__': if __name__ == '__main__':
test_mips_eb() test_mips_eb()
print("=" * 20) print("=" * 27)
test_mips_el() test_mips_el()

View file

@ -20,7 +20,7 @@ def hook_block(uc, address, size, user_data):
# callback for tracing instructions # callback for tracing instructions
def hook_code(uc, address, size, user_data): def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size)) print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
# Test SPARC # Test SPARC

View file

@ -8,6 +8,8 @@ from unicorn.x86_const import *
X86_CODE32 = b"\x41\x4a\x66\x0f\xef\xc1" # INC ecx; DEC edx; PXOR xmm0, xmm1 X86_CODE32 = b"\x41\x4a\x66\x0f\xef\xc1" # INC ecx; DEC edx; PXOR xmm0, xmm1
X86_CODE32_LOOP = b"\x41\x4a\xeb\xfe" # INC ecx; DEC edx; JMP self-loop X86_CODE32_LOOP = b"\x41\x4a\xeb\xfe" # INC ecx; DEC edx; JMP self-loop
X86_CODE32_JUMP = b"\xeb\x02\x90\x90\x90\x90\x90\x90" # jmp 4; nop; nop; nop; nop; nop; nop
X86_CODE32_JMP_INVALID = b"\xe9\xe9\xee\xee\xee\x41\x4a" # JMP outside; INC ecx; DEC edx
X86_CODE32_MEM_READ = b"\x8B\x0D\xAA\xAA\xAA\xAA\x41\x4a" # mov ecx,[0xaaaaaaaa]; INC ecx; DEC edx X86_CODE32_MEM_READ = b"\x8B\x0D\xAA\xAA\xAA\xAA\x41\x4a" # mov ecx,[0xaaaaaaaa]; INC ecx; DEC edx
X86_CODE32_MEM_WRITE = b"\x89\x0D\xAA\xAA\xAA\xAA\x41\x4a" # mov [0xaaaaaaaa], ecx; INC ecx; DEC edx X86_CODE32_MEM_WRITE = b"\x89\x0D\xAA\xAA\xAA\xAA\x41\x4a" # mov [0xaaaaaaaa], ecx; INC ecx; DEC edx
X86_CODE64 = b"\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_CODE64 = b"\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"
@ -26,9 +28,14 @@ def hook_block(uc, address, size, user_data):
# callback for tracing instructions # callback for tracing instructions
def hook_code(uc, address, size, user_data): def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size)) print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
#eip = uc.reg_read(UC_X86_REG_EIP) eip = uc.reg_read(UC_X86_REG_EFLAGS)
#print(">>> EIP = 0x%x" %(eip)) print(">>> --- EFLAGS is 0x%x" %(eip))
def hook_code64(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
rip = uc.reg_read(UC_X86_REG_RIP)
print(">>> RIP is 0x%x" %rip);
# callback for tracing invalid memory access (READ or WRITE) # callback for tracing invalid memory access (READ or WRITE)
@ -128,21 +135,21 @@ def test_i386():
r_xmm0 = mu.reg_read(UC_X86_REG_XMM0) r_xmm0 = mu.reg_read(UC_X86_REG_XMM0)
print(">>> ECX = 0x%x" %r_ecx) print(">>> ECX = 0x%x" %r_ecx)
print(">>> EDX = 0x%x" %r_edx) print(">>> EDX = 0x%x" %r_edx)
print(">>> XMM0 = 0x%x" %r_xmm0) print(">>> XMM0 = 0x%.32x" %r_xmm0)
# read from memory # read from memory
tmp = mu.mem_read(ADDRESS, 2) tmp = mu.mem_read(ADDRESS, 4)
print(">>> Read 2 bytes from [0x%x] =" %(ADDRESS), end="") print(">>> Read 4 bytes from [0x%x] = 0x" %(ADDRESS), end="")
for i in tmp: for i in reversed(tmp):
print(" 0x%x" %i, end="") print("%x" %(i), end="")
print("") print("")
except UcError as e: except UcError as e:
print("ERROR: %s" % e) print("ERROR: %s" % e)
def test_i386_loop(): def test_i386_map_ptr():
print("Emulate i386 code with infinite loop - wait for 2 seconds then stop emulation") print("Emulate i386 code - use uc_mem_map_ptr()")
try: try:
# Initialize emulator in X86-32bit mode # Initialize emulator in X86-32bit mode
mu = Uc(UC_ARCH_X86, UC_MODE_32) mu = Uc(UC_ARCH_X86, UC_MODE_32)
@ -151,14 +158,20 @@ def test_i386_loop():
mu.mem_map(ADDRESS, 2 * 1024 * 1024) mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory # write machine code to be emulated to memory
mu.mem_write(ADDRESS, X86_CODE32_LOOP) mu.mem_write(ADDRESS, X86_CODE32)
# initialize machine registers # initialize machine registers
mu.reg_write(UC_X86_REG_ECX, 0x1234) mu.reg_write(UC_X86_REG_ECX, 0x1234)
mu.reg_write(UC_X86_REG_EDX, 0x7890) 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)
# emulate machine code in infinite time # emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_LOOP), 2 * UC_SECOND_SCALE) mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32), 2 * UC_SECOND_SCALE)
# now print out some registers # now print out some registers
print(">>> Emulation done. Below is the CPU context") print(">>> Emulation done. Below is the CPU context")
@ -168,6 +181,13 @@ def test_i386_loop():
print(">>> ECX = 0x%x" %r_ecx) print(">>> ECX = 0x%x" %r_ecx)
print(">>> EDX = 0x%x" %r_edx) print(">>> EDX = 0x%x" %r_edx)
# read from memory
tmp = mu.mem_read(ADDRESS, 4)
print(">>> Read 4 bytes from [0x%x] = 0x" %(ADDRESS), end="")
for i in reversed(tmp):
print("%x" %(i), end="")
print("")
except UcError as e: except UcError as e:
print("ERROR: %s" % e) print("ERROR: %s" % e)
@ -198,7 +218,7 @@ def test_i386_invalid_mem_read():
# emulate machine code in infinite time # emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_MEM_READ)) mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_MEM_READ))
except UcError as e: except UcError as e:
print("ERROR: %s" % e) print("Failed on uc_emu_start() with error returned 6: %s" % e)
# now print out some registers # now print out some registers
print(">>> Emulation done. Below is the CPU context") print(">>> Emulation done. Below is the CPU context")
@ -211,6 +231,35 @@ def test_i386_invalid_mem_read():
except UcError as e: except UcError as e:
print("ERROR: %s" % e) print("ERROR: %s" % e)
def test_i386_jump():
print("Emulate i386 code with jump")
try:
# Initialize emulator in X86-32bit mode
mu = Uc(UC_ARCH_X86, UC_MODE_32)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, X86_CODE32_JUMP)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, hook_block, begin=ADDRESS, end=ADDRESS)
# tracing all instructions with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=ADDRESS)
try:
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_JUMP))
except UcError as e:
print("ERROR: %s" % e)
print(">>> Emulation done. Below is the CPU context")
except UcError as e:
print("ERROR: %s" % e)
def test_i386_invalid_mem_write(): def test_i386_invalid_mem_write():
print("Emulate i386 code that write to invalid memory") print("Emulate i386 code that write to invalid memory")
@ -229,10 +278,10 @@ def test_i386_invalid_mem_write():
mu.reg_write(UC_X86_REG_EDX, 0x7890) mu.reg_write(UC_X86_REG_EDX, 0x7890)
# tracing all basic blocks with customized callback # tracing all basic blocks with customized callback
#mu.hook_add(UC_HOOK_BLOCK, hook_block) mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions with customized callback # tracing all instructions with customized callback
#mu.hook_add(UC_HOOK_CODE, hook_code) mu.hook_add(UC_HOOK_CODE, hook_code)
# intercept invalid memory events # intercept invalid memory events
mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, hook_mem_invalid) mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, hook_mem_invalid)
@ -251,25 +300,92 @@ def test_i386_invalid_mem_write():
print(">>> ECX = 0x%x" %r_ecx) print(">>> ECX = 0x%x" %r_ecx)
print(">>> EDX = 0x%x" %r_edx) print(">>> EDX = 0x%x" %r_edx)
# read from memory
print(">>> Read 4 bytes from [0x%x] = 0x" %(0xaaaaaaaa), end="")
tmp = mu.mem_read(0xaaaaaaaa, 4)
for i in reversed(tmp):
if i != 0:
print("%x" %i, end="")
print("")
try: try:
# read from memory tmp = mu.mem_read(0xffffffaa, 4)
print(">>> Read 4 bytes from [0x%x] = " %(0xaaaaaaaa), end="") print(">>> Read 4 bytes from [0x%x] = 0x" %(0xffffffaa), end="")
tmp = mu.mem_read(0xaaaaaaaa, 4) for i in reversed(tmp):
for i in tmp: print("%x" %i, end="")
print(" 0x%x" %i, end="")
print("") print("")
print(">>> Read 4 bytes from [0x%x] = " %(0xffffffaa), end="")
tmp = mu.mem_read(0xffffffaa, 4)
for i in tmp:
print(" 0x%x" %i, end="")
print("")
except UcError as e: except UcError as e:
print("ERROR: %s" % e) print(">>> Failed to read 4 bytes from [0xffffffaa]")
except UcError as e: except UcError as e:
print("ERROR: %s" % e) print("ERROR: %s" % e)
def test_i386_jump_invalid():
print("Emulate i386 code that jumps to invalid memory")
try:
# Initialize emulator in X86-32bit mode
mu = Uc(UC_ARCH_X86, UC_MODE_32)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, X86_CODE32_JMP_INVALID)
# 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)
try:
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_JMP_INVALID))
except UcError as e:
print("Failed on uc_emu_start() with error returned 8: %s" %e)
print(">>> Emulation done. Below is the CPU context")
r_ecx = mu.reg_read(UC_X86_REG_ECX)
r_edx = mu.reg_read(UC_X86_REG_EDX)
print(">>> ECX = 0x%x" %r_ecx)
print(">>> EDX = 0x%x" %r_edx)
except UcError as e:
print("ERROR %s" % e)
def test_i386_loop():
print("Emulate i386 code that loop forever")
try:
# Initialize emulator in X86-32bit mode
mu = Uc(UC_ARCH_X86, UC_MODE_32)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, X86_CODE32_LOOP)
# initialize machine registers
mu.reg_write(UC_X86_REG_ECX, 0x1234)
mu.reg_write(UC_X86_REG_EDX, 0x7890)
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_LOOP), timeout=2*UC_SECOND_SCALE)
print(">>> Emulation done. Below is the CPU context")
r_ecx = mu.reg_read(UC_X86_REG_ECX)
r_edx = mu.reg_read(UC_X86_REG_EDX)
print(">>> ECX = 0x%x" %r_ecx)
print(">>> EDX = 0x%x" %r_edx)
except UcError as e:
print("ERROR: %s" % e)
# Test X86 32 bit with IN/OUT instruction # Test X86 32 bit with IN/OUT instruction
def test_i386_inout(): def test_i386_inout():
@ -397,7 +513,7 @@ def test_x86_64():
mu.hook_add(UC_HOOK_BLOCK, hook_block) mu.hook_add(UC_HOOK_BLOCK, hook_block)
# tracing all instructions in range [ADDRESS, ADDRESS+20] # tracing all instructions in range [ADDRESS, ADDRESS+20]
mu.hook_add(UC_HOOK_CODE, hook_code, None, ADDRESS, ADDRESS+20) mu.hook_add(UC_HOOK_CODE, hook_code64, None, ADDRESS, ADDRESS+20)
# tracing all memory READ & WRITE access # tracing all memory READ & WRITE access
mu.hook_add(UC_HOOK_MEM_WRITE, hook_mem_access) mu.hook_add(UC_HOOK_MEM_WRITE, hook_mem_access)
@ -429,23 +545,21 @@ def test_x86_64():
r14 = mu.reg_read(UC_X86_REG_R14) r14 = mu.reg_read(UC_X86_REG_R14)
r15 = mu.reg_read(UC_X86_REG_R15) r15 = mu.reg_read(UC_X86_REG_R15)
print(">>> RAX = %x" %rax) print(">>> RAX = 0x%x" %rax)
print(">>> RBX = %x" %rbx) print(">>> RBX = 0x%x" %rbx)
print(">>> RCX = %x" %rcx) print(">>> RCX = 0x%x" %rcx)
print(">>> RDX = %x" %rdx) print(">>> RDX = 0x%x" %rdx)
print(">>> RSI = %x" %rsi) print(">>> RSI = 0x%x" %rsi)
print(">>> RDI = %x" %rdi) print(">>> RDI = 0x%x" %rdi)
print(">>> R8 = %x" %r8) print(">>> R8 = 0x%x" %r8)
print(">>> R9 = %x" %r9) print(">>> R9 = 0x%x" %r9)
print(">>> R10 = %x" %r10) print(">>> R10 = 0x%x" %r10)
print(">>> R11 = %x" %r11) print(">>> R11 = 0x%x" %r11)
print(">>> R12 = %x" %r12) print(">>> R12 = 0x%x" %r12)
print(">>> R13 = %x" %r13) print(">>> R13 = 0x%x" %r13)
print(">>> R14 = %x" %r14) print(">>> R14 = 0x%x" %r14)
print(">>> R15 = %x" %r15) print(">>> R15 = 0x%x" %r15)
#BUG
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE64))
except UcError as e: except UcError as e:
print("ERROR: %s" % e) print("ERROR: %s" % e)
@ -516,27 +630,29 @@ def test_x86_16():
print(">>> Emulation done. Below is the CPU context") print(">>> Emulation done. Below is the CPU context")
tmp = mu.mem_read(11, 1) tmp = mu.mem_read(11, 1)
print("[0x%x] = 0x%x" %(11, tmp[0])) print(">>> Read 1 bytes from [0x%x] = 0x%x" %(11, tmp[0]))
except UcError as e: except UcError as e:
print("ERROR: %s" % e) print("ERROR: %s" % e)
if __name__ == '__main__': if __name__ == '__main__':
test_i386()
print("=" * 20)
test_i386_loop()
print("=" * 20)
test_i386_invalid_mem_read()
print("=" * 20)
test_i386_invalid_mem_write()
print("=" * 20)
test_i386_inout()
print("=" * 20)
test_i386_context_save()
print("=" * 20)
test_x86_64()
print("=" * 20)
test_x86_64_syscall()
print("=" * 20)
test_x86_16() test_x86_16()
test_i386()
print("=" * 35)
test_i386_map_ptr()
print("=" * 35)
test_i386_inout()
print("=" * 35)
test_i386_jump()
print("=" * 35)
test_i386_loop()
print("=" * 35)
test_i386_invalid_mem_read()
print("=" * 35)
test_i386_invalid_mem_write()
print("=" * 35)
test_i386_jump_invalid()
test_x86_64()
print("=" * 35)
test_x86_64_syscall()

View file

@ -23,7 +23,7 @@ if os.path.exists(PATH_LIB64) and os.path.exists(PATH_LIB32):
PKG_NAME = 'unicorn-windows' PKG_NAME = 'unicorn-windows'
SYSTEM = sys.platform SYSTEM = sys.platform
VERSION = '1.0' VERSION = '1.0.0'
# adapted from commit e504b81 of Nguyen Tan Cong # adapted from commit e504b81 of Nguyen Tan Cong
# Reference: https://docs.python.org/2/library/platform.html#cross-platform # Reference: https://docs.python.org/2/library/platform.html#cross-platform

View file

@ -63,7 +63,8 @@ _path_list = [pkg_resources.resource_filename(__name__, 'lib'),
os.path.join(os.path.split(__file__)[0], 'lib'), os.path.join(os.path.split(__file__)[0], 'lib'),
'', '',
distutils.sysconfig.get_python_lib(), distutils.sysconfig.get_python_lib(),
"/usr/local/lib/" if sys.platform == 'darwin' else '/usr/lib64'] "/usr/local/lib/" if sys.platform == 'darwin' else '/usr/lib64',
os.environ['PATH']]
for _path in _path_list: for _path in _path_list:
_uc = _load_lib(_path) _uc = _load_lib(_path)
@ -105,7 +106,6 @@ _setup_prototype(_uc, "uc_context_alloc", ucerr, uc_engine, ctypes.POINTER(uc_co
_setup_prototype(_uc, "uc_context_free", ucerr, uc_context) _setup_prototype(_uc, "uc_context_free", ucerr, uc_context)
_setup_prototype(_uc, "uc_context_save", ucerr, uc_engine, uc_context) _setup_prototype(_uc, "uc_context_save", ucerr, uc_engine, uc_context)
_setup_prototype(_uc, "uc_context_restore", ucerr, uc_engine, uc_context) _setup_prototype(_uc, "uc_context_restore", ucerr, uc_engine, uc_context)
_setup_prototype(_uc, "free", None, ctypes.c_voidp)
# 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 = _uc.uc_hook_add _uc.uc_hook_add = _uc.uc_hook_add

View file

@ -73,11 +73,11 @@ VALUE m_uc_emu_start(int argc, VALUE* argv, VALUE self){
Data_Get_Struct(rb_iv_get(self,"@uch"), uc_engine, _uc); Data_Get_Struct(rb_iv_get(self,"@uch"), uc_engine, _uc);
rb_scan_args(argc, argv, "22",&begin, &until, &timeout, &count); rb_scan_args(argc, argv, "22",&begin, &until, &timeout, &count);
if (NIL_P(timeout)) if (NIL_P(timeout))
timeout = INT2NUM(0); timeout = INT2NUM(0);
if (NIL_P(count)) if (NIL_P(count))
count = INT2NUM(0); count = INT2NUM(0);
err = uc_emu_start(_uc, NUM2ULL(begin), NUM2ULL(until), NUM2INT(timeout), NUM2INT(count)); err = uc_emu_start(_uc, NUM2ULL(begin), NUM2ULL(until), NUM2INT(timeout), NUM2INT(count));
if (err != UC_ERR_OK) { if (err != UC_ERR_OK) {
@ -133,7 +133,7 @@ VALUE m_uc_reg_read(VALUE self, VALUE reg_id){
if (err != UC_ERR_OK) { if (err != UC_ERR_OK) {
rb_raise(UcError, "%s", uc_strerror(err)); rb_raise(UcError, "%s", uc_strerror(err));
} }
return LL2NUM(reg_value); return ULL2NUM(reg_value);
} }
} }
@ -165,7 +165,7 @@ VALUE m_uc_reg_write(VALUE self, VALUE reg_id, VALUE reg_value){
err = uc_reg_write(_uc, NUM2INT(reg_id), &tmp); err = uc_reg_write(_uc, NUM2INT(reg_id), &tmp);
break; break;
} }
if (err != UC_ERR_OK) { if (err != UC_ERR_OK) {
rb_raise(UcError, "%s", uc_strerror(err)); rb_raise(UcError, "%s", uc_strerror(err));
} }
@ -205,8 +205,8 @@ VALUE m_uc_mem_map(int argc, VALUE* argv, VALUE self){
uc_engine *_uc; uc_engine *_uc;
Data_Get_Struct(rb_iv_get(self,"@uch"), uc_engine, _uc); Data_Get_Struct(rb_iv_get(self,"@uch"), uc_engine, _uc);
rb_scan_args(argc, argv, "21",&address, &size, &perms); rb_scan_args(argc, argv, "21",&address, &size, &perms);
if (NIL_P(perms)) if (NIL_P(perms))
perms = INT2NUM(UC_PROT_ALL); perms = INT2NUM(UC_PROT_ALL);
err = uc_mem_map(_uc, NUM2ULL(address), NUM2UINT(size), NUM2UINT(perms)); err = uc_mem_map(_uc, NUM2ULL(address), NUM2UINT(size), NUM2UINT(perms));
if (err != UC_ERR_OK) { if (err != UC_ERR_OK) {
@ -332,14 +332,14 @@ VALUE m_uc_hook_add(int argc, VALUE* argv, VALUE self){
uc_engine *_uc; uc_engine *_uc;
Data_Get_Struct(rb_iv_get(self,"@uch"), 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); rb_scan_args(argc, argv, "24",&hook_type, &callback, &user_data, &begin, &end, &arg1);
if (NIL_P(begin)) if (NIL_P(begin))
begin = ULL2NUM(1); begin = ULL2NUM(1);
if (NIL_P(end)) if (NIL_P(end))
end = ULL2NUM(0); end = ULL2NUM(0);
if (NIL_P(arg1)) if (NIL_P(arg1))
arg1 = INT2NUM(0); arg1 = INT2NUM(0);
VALUE passthrough; VALUE passthrough;
uc_hook trace; uc_hook trace;
@ -374,12 +374,12 @@ VALUE m_uc_hook_add(int argc, VALUE* argv, VALUE self){
else if(htype == UC_HOOK_CODE || htype == UC_HOOK_BLOCK){ 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)); err = uc_hook_add(_uc, &trace, htype, cb_hook_code,(void *)passthrough, NUM2ULL(begin), NUM2ULL(end));
} }
else if (htype & UC_HOOK_MEM_READ_UNMAPPED else if (htype & UC_HOOK_MEM_READ_UNMAPPED
|| htype & UC_HOOK_MEM_WRITE_UNMAPPED || htype & UC_HOOK_MEM_WRITE_UNMAPPED
|| htype & UC_HOOK_MEM_FETCH_UNMAPPED || htype & UC_HOOK_MEM_FETCH_UNMAPPED
|| htype & UC_HOOK_MEM_READ_PROT || htype & UC_HOOK_MEM_READ_PROT
|| htype & UC_HOOK_MEM_WRITE_PROT || htype & UC_HOOK_MEM_WRITE_PROT
|| htype & UC_HOOK_MEM_FETCH_PROT || htype & UC_HOOK_MEM_FETCH_PROT
|| htype & UC_HOOK_MEM_READ_INVALID || htype & UC_HOOK_MEM_READ_INVALID
|| htype & UC_HOOK_MEM_WRITE_INVALID || htype & UC_HOOK_MEM_WRITE_INVALID
|| htype & UC_HOOK_MEM_FETCH_INVALID || htype & UC_HOOK_MEM_FETCH_INVALID
@ -387,7 +387,7 @@ VALUE m_uc_hook_add(int argc, VALUE* argv, VALUE self){
|| htype & UC_HOOK_MEM_PROT || htype & UC_HOOK_MEM_PROT
|| htype & UC_HOOK_MEM_INVALID) { || htype & UC_HOOK_MEM_INVALID) {
err = uc_hook_add(_uc, &trace, htype, cb_hook_mem_invalid,(void *)passthrough, NUM2ULL(begin), NUM2ULL(end)); err = uc_hook_add(_uc, &trace, htype, cb_hook_mem_invalid,(void *)passthrough, NUM2ULL(begin), NUM2ULL(end));
} }
else{ else{
err = uc_hook_add(_uc, &trace, htype, cb_hook_mem_access,(void *)passthrough, NUM2ULL(begin), NUM2ULL(end)); err = uc_hook_add(_uc, &trace, htype, cb_hook_mem_access,(void *)passthrough, NUM2ULL(begin), NUM2ULL(end));
} }

20
make.sh
View file

@ -36,17 +36,6 @@ build_iOS() {
${MAKE} ${MAKE}
} }
build() {
[ "$UNAME" = Darwin ] && LIBARCHS="i386 x86_64"
${MAKE}
}
build_macos_universal() {
[ "$UNAME" = Darwin ] && LIBARCHS="i386 x86_64"
MACOS_UNIVERSAL=yes \
${MAKE}
}
build_cross() { build_cross() {
[ "$UNAME" = Darwin ] && LIBARCHS="i386 x86_64" [ "$UNAME" = Darwin ] && LIBARCHS="i386 x86_64"
CROSS=$1 CROSS=$1
@ -112,17 +101,14 @@ fi
export CC INSTALL_BIN PREFIX PKGCFGDIR LIBDIRARCH LIBARCHS CFLAGS LDFLAGS export CC INSTALL_BIN PREFIX PKGCFGDIR LIBDIRARCH LIBARCHS CFLAGS LDFLAGS
case "$1" in case "$1" in
"" ) build;; "" ) ${MAKE};;
"macos-universal" ) build_macos_universal;;
"asan" ) asan;; "asan" ) asan;;
"default" ) build;;
"install" ) install;; "install" ) install;;
"uninstall" ) uninstall;; "uninstall" ) uninstall;;
"macos-universal" ) MACOS_UNIVERSAL=yes ${MAKE};;
"cross-win32" ) build_cross i686-w64-mingw32;; "cross-win32" ) build_cross i686-w64-mingw32;;
"cross-win64" ) build_cross x86_64-w64-mingw32;; "cross-win64" ) build_cross x86_64-w64-mingw32;;
"cross-android" ) CROSS=arm-linux-androideabi build;; "cross-android" ) CROSS=arm-linux-androideabi ${MAKE};;
"clang" ) CC=clang build;;
"gcc" ) CC=gcc build;;
"ios" ) build_iOS;; "ios" ) build_iOS;;
"ios_armv7" ) build_iOS armv7;; "ios_armv7" ) build_iOS armv7;;
"ios_armv7s" ) build_iOS armv7s;; "ios_armv7s" ) build_iOS armv7s;;

View file

@ -3,7 +3,6 @@
include ../config.mk include ../config.mk
LIBNAME = unicorn
UNAME_S := $(shell uname -s) UNAME_S := $(shell uname -s)
# Find GLIB # Find GLIB
@ -11,70 +10,34 @@ ifndef GLIB
GLIB = $(shell pkg-config --libs glib-2.0) GLIB = $(shell pkg-config --libs glib-2.0)
endif endif
UNICORN_DEP_LIBS_STATIC += -lpthread -lm $(GLIB)
# Verbose output? # Verbose output?
V ?= 0 V ?= 0
INCDIR = ../include CFLAGS += -Wall -Werror -I../include
SAMPLEDIR = . LDFLAGS += -L..
OBJDIR = . LDLIBS += -lpthread -lunicorn -lm $(GLIB)
LIBDIR = ..
CFLAGS += -Wall -Werror -I$(INCDIR) ifneq ($(CROSS),)
LDFLAGS += -lpthread -L$(LIBDIR) -l$(LIBNAME)
LDFLAGS_STATIC += $(UNICORN_DEP_LIBS_STATIC)
ifeq ($(CROSS),)
CC ?= cc
LDFLAGS += -lm $(GLIB)
else
CC = $(CROSS)gcc CC = $(CROSS)gcc
endif endif
ifeq ($(UNICORN_ASAN),yes) ifeq ($(UNICORN_ASAN),yes)
CC = clang -fsanitize=address -fno-omit-frame-pointer CC = clang
CXX = clang++ -fsanitize=address -fno-omit-frame-pointer CXX = clang++
AR = llvm-ar AR = llvm-ar
LDFLAGS := -fsanitize=address ${LDFLAGS} CFLAGS += -fsanitize=address -fno-omit-frame-pointer
endif endif
#CFLAGS += $(foreach arch,$(LIBARCHS),-arch $(arch))
#LDFLAGS += $(foreach arch,$(LIBARCHS),-arch $(arch))
BIN_EXT =
AR_EXT = a
# Cygwin? # Cygwin?
ifneq ($(filter CYGWIN%,$(UNAME_S)),) ifneq ($(filter CYGWIN%,$(UNAME_S)),)
CFLAGS := $(CFLAGS:-fPIC=) CFLAGS := $(CFLAGS:-fPIC=)
LDFLAGS += -lssp LDLIBS += -lssp
LDFLAGS_STATIC += -lssp
BIN_EXT = .exe
AR_EXT = a
# mingw? # mingw?
else ifneq ($(filter MINGW%,$(UNAME_S)),) else ifneq ($(filter MINGW%,$(UNAME_S)),)
CFLAGS := $(CFLAGS:-fPIC=) CFLAGS := $(CFLAGS:-fPIC=)
BIN_EXT = .exe
AR_EXT = lib
endif endif
.PHONY: all clean
ifeq ($(UNICORN_STATIC),yes)
ifneq ($(filter MINGW%,$(UNAME_S)),)
ARCHIVE = $(LIBDIR)/$(LIBNAME).$(AR_EXT)
else ifneq ($(filter CYGWIN%,$(UNAME_S)),)
ARCHIVE = $(LIBDIR)/lib$(LIBNAME).$(AR_EXT)
else
ARCHIVE = $(LIBDIR)/lib$(LIBNAME).$(AR_EXT)
#ARCHIVE_X86 = $(LIBDIR)/lib$(LIBNAME)_x86.$(AR_EXT)
#ARCHIVE_ARM = $(LIBDIR)/lib$(LIBNAME)_arm.$(AR_EXT)
#ARCHIVE_ARM64 = $(LIBDIR)/lib$(LIBNAME)_arm64.$(AR_EXT)
endif
endif
.PHONY: all clean clean_bins clean_libs
UNICORN_ARCHS := $(shell if [ -e ../config.log ]; then cat ../config.log;\ UNICORN_ARCHS := $(shell if [ -e ../config.log ]; then cat ../config.log;\
else printf "$(UNICORN_ARCHS)"; fi) else printf "$(UNICORN_ARCHS)"; fi)
@ -89,9 +52,9 @@ endif
ifneq (,$(findstring mips,$(UNICORN_ARCHS))) ifneq (,$(findstring mips,$(UNICORN_ARCHS)))
SOURCES += sample_mips.c SOURCES += sample_mips.c
endif endif
ifneq (,$(findstring ppc,$(UNICORN_ARCHS))) #ifneq (,$(findstring ppc,$(UNICORN_ARCHS)))
#SOURCES += sample_ppc.c #SOURCES += sample_ppc.c
endif #endif
ifneq (,$(findstring sparc,$(UNICORN_ARCHS))) ifneq (,$(findstring sparc,$(UNICORN_ARCHS)))
SOURCES += sample_sparc.c SOURCES += sample_sparc.c
endif endif
@ -106,73 +69,9 @@ ifneq (,$(findstring m68k,$(UNICORN_ARCHS)))
SOURCES += sample_m68k.c SOURCES += sample_m68k.c
endif endif
OBJS = $(addprefix $(OBJDIR)/,$(SOURCES:.c=.o)) BINS = $(SOURCES:.c=)
OBJS_ELF = $(addprefix $(OBJDIR)/,$(SOURCES:.c=))
BINARY = $(addprefix $(SAMPLEDIR)/,$(SOURCES:.c=$(BIN_EXT)))
all: $(BINARY) all: $(BINS)
clean_bins: clean:
rm -rf *.o $(OBJS_ELF) $(BINARY) $(SAMPLEDIR)/*.exe $(SAMPLEDIR)/*.static $(OBJDIR)/lib$(LIBNAME)* $(OBJDIR)/$(LIBNAME)* rm -rf *.o $(BINS)
rm -rf sample_x86 sample_arm sample_arm64 sample_mips sample_sparc sample_ppc sample_m68k shellcode mem_apis sample_x86_32_gdt_and_seg_regs sample_batch_reg
clean_libs:
rm -rf libunicorn*.so libunicorn*.lib libunicorn*.dylib unicorn*.dll unicorn*.lib
clean: clean_bins clean_libs
$(BINARY): $(OBJS)
$(SAMPLEDIR)/%$(BIN_EXT): $(OBJDIR)/%.o
@mkdir -p $(@D)
ifeq ($(V),0)
ifeq ($(UNICORN_SHARED),yes)
$(call log,LINK,$(notdir $@))
@$(link-dynamic)
endif
ifeq ($(UNICORN_STATIC),yes)
ifneq ($(filter MINGW%,$(UNAME_S)),)
$(call log,LINK,$(notdir $(call staticname,$@)))
@$(link-static)
endif
endif
else
ifeq ($(UNICORN_SHARED),yes)
$(link-dynamic)
endif
ifeq ($(UNICORN_STATIC),yes)
ifneq ($(filter MINGW%,$(UNAME_S)),)
$(link-static)
endif
endif
endif
$(OBJDIR)/%.o: %.c
@mkdir -p $(@D)
ifeq ($(V),0)
$(call log,CC,$(@:$(OBJDIR)/%=%))
@$(compile)
else
$(compile)
endif
define link-dynamic
$(CC) $< $(LDFLAGS) -o $@
endef
define link-static
$(CC) $< $(ARCHIVE) $(LDFLAGS_STATIC) -o $(call staticname,$@)
endef
staticname = $(subst $(BIN_EXT),,$(1)).static$(BIN_EXT)
define log
@printf " %-7s %s\n" "$(1)" "$(2)"
endef
define compile
${CC} ${CFLAGS} -c $< -o $@
endef

View file

@ -41,6 +41,7 @@
#define X86_CODE32_JMP_INVALID "\xe9\xe9\xee\xee\xee\x41\x4a" // JMP outside; INC ecx; DEC edx #define X86_CODE32_JMP_INVALID "\xe9\xe9\xee\xee\xee\x41\x4a" // JMP outside; INC ecx; DEC edx
#define X86_CODE32_INOUT "\x41\xE4\x3F\x4a\xE6\x46\x43" // INC ecx; IN AL, 0x3f; DEC edx; OUT 0x46, AL; INC ebx #define X86_CODE32_INOUT "\x41\xE4\x3F\x4a\xE6\x46\x43" // INC ecx; IN AL, 0x3f; DEC edx; OUT 0x46, AL; INC ebx
#define X86_CODE32_INC "\x40" // INC eax
//#define 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" // <== still crash //#define 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" // <== still crash
//#define 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" //#define 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"
@ -668,6 +669,105 @@ static void test_i386_inout(void)
uc_close(uc); uc_close(uc);
} }
// emulate code and save/restore the CPU context
static void test_i386_context_save(void)
{
uc_engine *uc;
uc_context *context;
uc_err err;
int r_eax = 0x1; // EAX register
printf("===================================\n");
printf("Save/restore CPU context in opaque blob\n");
// initialize emulator in X86-32bit mode
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
if (err) {
printf("Failed on uc_open() with error returned: %u\n", err);
return;
}
// map 8KB memory for this emulation
uc_mem_map(uc, ADDRESS, 8 * 1024, UC_PROT_ALL);
// write machine code to be emulated to memory
if (uc_mem_write(uc, ADDRESS, X86_CODE32_INC, sizeof(X86_CODE32_INC) - 1)) {
printf("Failed to write emulation code to memory, quit!\n");
return;
}
// initialize machine registers
uc_reg_write(uc, UC_X86_REG_EAX, &r_eax);
// emulate machine code in infinite time
printf(">>> Running emulation for the first time\n");
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_INC) - 1, 0, 0);
if (err) {
printf("Failed on uc_emu_start() with error returned %u: %s\n",
err, uc_strerror(err));
}
// now print out some registers
printf(">>> Emulation done. Below is the CPU context\n");
uc_reg_read(uc, UC_X86_REG_EAX, &r_eax);
printf(">>> EAX = 0x%x\n", r_eax);
// allocate and save the CPU context
printf(">>> Saving CPU context\n");
err = uc_context_alloc(uc, &context);
if (err) {
printf("Failed on uc_context_alloc() with error returned: %u\n", err);
return;
}
err = uc_context_save(uc, context);
if (err) {
printf("Failed on uc_context_save() with error returned: %u\n", err);
return;
}
// emulate machine code again
printf(">>> Running emulation for the second time\n");
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_INC) - 1, 0, 0);
if (err) {
printf("Failed on uc_emu_start() with error returned %u: %s\n",
err, uc_strerror(err));
}
// now print out some registers
printf(">>> Emulation done. Below is the CPU context\n");
uc_reg_read(uc, UC_X86_REG_EAX, &r_eax);
printf(">>> EAX = 0x%x\n", r_eax);
// restore CPU context
err = uc_context_restore(uc, context);
if (err) {
printf("Failed on uc_context_restore() with error returned: %u\n", err);
return;
}
// now print out some registers
printf(">>> CPU context restored. Below is the CPU context\n");
uc_reg_read(uc, UC_X86_REG_EAX, &r_eax);
printf(">>> EAX = 0x%x\n", r_eax);
// free the CPU context
err = uc_context_free(context);
if (err) {
printf("Failed on uc_context_free() with error returned: %u\n", err);
return;
}
uc_close(uc);
}
static void test_x86_64(void) static void test_x86_64(void)
{ {
uc_engine *uc; uc_engine *uc;
@ -906,6 +1006,7 @@ int main(int argc, char **argv, char **envp)
test_i386(); test_i386();
test_i386_map_ptr(); test_i386_map_ptr();
test_i386_inout(); test_i386_inout();
test_i386_context_save();
test_i386_jump(); test_i386_jump();
test_i386_loop(); test_i386_loop();
test_i386_invalid_mem_read(); test_i386_invalid_mem_read();

View file

@ -269,7 +269,7 @@ static void gdt_demo()
int i; int i;
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
fprintf(stderr, "%02hhx", buf[i]); fprintf(stderr, "%02x", buf[i]);
} }
fprintf(stderr, "\n"); fprintf(stderr, "\n");

View file

@ -1,11 +1,9 @@
CFLAGS += -Wall -Werror -Wno-unused-function -g CFLAGS += -Wall -Werror -Wno-unused-function -g
CFLAGS += -L ../../ CFLAGS += -L ../../ -I ../../include
CFLAGS += -I ../../include
CFLAGS += -L ../../cmocka/src -I ../../cmocka/include CFLAGS += -L ../../cmocka/src -I ../../cmocka/include
EXECUTE_VARS = LD_LIBRARY_PATH=../../cmocka/src:../../ DYLD_LIBRARY_PATH=../../ LDLIBS += -lcmocka -lunicorn
LIBS += -lcmocka -lunicorn EXECUTE_VARS = LD_LIBRARY_PATH=../../cmocka/src:../../ DYLD_LIBRARY_PATH=../../
ifeq ($(UNICORN_ASAN),yes) ifeq ($(UNICORN_ASAN),yes)
CC = clang -fsanitize=address -fno-omit-frame-pointer CC = clang -fsanitize=address -fno-omit-frame-pointer
@ -14,9 +12,8 @@ AR = llvm-ar
LDFLAGS := -fsanitize=address ${LDFLAGS} LDFLAGS := -fsanitize=address ${LDFLAGS}
endif endif
ALL_TESTS = test_sanity test_x86 test_mem_map test_mem_high test_mem_map_ptr \ ALL_TESTS_SOURCES = $(wildcard *.c)
test_tb_x86 test_multihook test_pc_change test_x86_soft_paging \ ALL_TESTS = $(ALL_TESTS_SOURCES:%.c=%)
test_hookcounts test_hang test_x86_shl_enter_leave test_x86_rip_bug
.PHONY: all .PHONY: all
all: ${ALL_TESTS} all: ${ALL_TESTS}
@ -32,28 +29,11 @@ test: ${ALL_TESTS}
${EXECUTE_VARS} ./test_mem_map ${EXECUTE_VARS} ./test_mem_map
${EXECUTE_VARS} ./test_mem_map_ptr ${EXECUTE_VARS} ./test_mem_map_ptr
${EXECUTE_VARS} ./test_mem_high ${EXECUTE_VARS} ./test_mem_high
echo "skipping test_tb_x86" #${EXECUTE_VARS} ./test_tb_x86
${EXECUTE_VARS} ./test_multihook ${EXECUTE_VARS} ./test_multihook
${EXECUTE_VARS} ./test_pc_change ${EXECUTE_VARS} ./test_pc_change
echo "skipping test_x86_soft_paging" #${EXECUTE_VARS} ./test_x86_soft_paging
${EXECUTE_VARS} ./test_hookcounts ${EXECUTE_VARS} ./test_hookcounts
echo "skipping test_hang" #${EXECUTE_VARS} ./test_hang echo "skipping test_tb_x86"
echo "skipping test_x86_sh1_enter_leave" #${EXECUTE_VARS} ./test_x86_shl_enter_leave echo "skipping test_x86_soft_paging"
echo "skipping test_x86_rip_bug" #${EXECUTE_VARS} ./test_x86_rip_bug echo "skipping test_hang"
echo "skipping test_x86_sh1_enter_leave"
test_sanity: test_sanity.c echo "skipping test_x86_rip_bug"
test_x86: test_x86.c
test_mem_map: test_mem_map.c
test_mem_map_ptr: test_mem_map_ptr.c
test_mem_high: test_mem_high.c
test_tb_x86: test_tb_x86.c
test_multihook: test_multihook.c
test_pc_change: test_pc_change.c
test_x86_soft_paging: test_x86_soft_paging.c
test_hookcounts: test_hookcounts.c
test_hang: test_hang.c
test_x86_shl_enter_leave: test_x86_shl_enter_leave.c
test_x86_rip_bug: test_x86_rip_bug.c
${ALL_TESTS}:
${CC} ${CFLAGS} -o $@ $^ ${LIBS}

15
uc.c
View file

@ -1,9 +1,6 @@
/* Unicorn Emulator Engine */ /* Unicorn Emulator Engine */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2015 */ /* By Nguyen Anh Quynh <aquynh@gmail.com>, 2015 */
#if defined (WIN32) || defined (WIN64) || defined (_WIN32) || defined (_WIN64)
#pragma warning(disable:4996)
#endif
#if defined(UNICORN_HAS_OSXKERNEL) #if defined(UNICORN_HAS_OSXKERNEL)
#include <libkern/libkern.h> #include <libkern/libkern.h>
#else #else
@ -1162,12 +1159,24 @@ static size_t cpu_context_size(uc_arch arch, uc_mode mode)
// tbl_table is the first entry in the CPU_COMMON macro, so it marks the end // tbl_table is the first entry in the CPU_COMMON macro, so it marks the end
// of the interesting CPU registers // of the interesting CPU registers
switch (arch) { switch (arch) {
#ifdef UNICORN_HAS_M68K
case UC_ARCH_M68K: return M68K_REGS_STORAGE_SIZE; case UC_ARCH_M68K: return M68K_REGS_STORAGE_SIZE;
#endif
#ifdef UNICORN_HAS_X86
case UC_ARCH_X86: return X86_REGS_STORAGE_SIZE; case UC_ARCH_X86: return X86_REGS_STORAGE_SIZE;
#endif
#ifdef UNICORN_HAS_ARM
case UC_ARCH_ARM: return ARM_REGS_STORAGE_SIZE; case UC_ARCH_ARM: return ARM_REGS_STORAGE_SIZE;
#endif
#ifdef UNICORN_HAS_ARM64
case UC_ARCH_ARM64: return ARM64_REGS_STORAGE_SIZE; case UC_ARCH_ARM64: return ARM64_REGS_STORAGE_SIZE;
#endif
#ifdef UNICORN_HAS_MIPS
case UC_ARCH_MIPS: return mode & UC_MODE_MIPS64 ? MIPS64_REGS_STORAGE_SIZE : MIPS_REGS_STORAGE_SIZE; case UC_ARCH_MIPS: return mode & UC_MODE_MIPS64 ? MIPS64_REGS_STORAGE_SIZE : MIPS_REGS_STORAGE_SIZE;
#endif
#ifdef UNICORN_HAS_SPARC
case UC_ARCH_SPARC: return mode & UC_MODE_SPARC64 ? SPARC64_REGS_STORAGE_SIZE : SPARC_REGS_STORAGE_SIZE; case UC_ARCH_SPARC: return mode & UC_MODE_SPARC64 ? SPARC64_REGS_STORAGE_SIZE : SPARC_REGS_STORAGE_SIZE;
#endif
default: return 0; default: return 0;
} }
} }