From 186540e160c4805a3472bfa31f03e24b47a206ef Mon Sep 17 00:00:00 2001 From: Stephen Date: Fri, 28 Oct 2016 16:31:52 -0700 Subject: [PATCH] make cleanup --- .appveyor.yml | 27 +- .travis.yml | 36 +-- Makefile | 98 ++---- bindings/Makefile | 17 +- bindings/go/unicorn/context.go | 28 ++ bindings/go/unicorn/context_test.go | 26 ++ bindings/go/unicorn/unicorn.go | 3 + bindings/haskell/samples/SampleX86.hs | 69 +++++ bindings/haskell/src/Unicorn.hs | 116 +++++--- bindings/haskell/src/Unicorn/CPU/Arm.chs | 21 +- bindings/haskell/src/Unicorn/CPU/Arm64.chs | 21 +- bindings/haskell/src/Unicorn/CPU/M68k.chs | 21 +- bindings/haskell/src/Unicorn/CPU/Mips.chs | 86 +++--- bindings/haskell/src/Unicorn/CPU/Sparc.chs | 21 +- bindings/haskell/src/Unicorn/CPU/X86.chs | 49 +-- bindings/haskell/src/Unicorn/Hook.hs | 59 ++-- .../haskell/src/Unicorn/Internal/Core.chs | 17 +- .../haskell/src/Unicorn/Internal/Hook.chs | 280 ++++++++++-------- .../haskell/src/Unicorn/Internal/Unicorn.chs | 279 ++++++++++------- bindings/haskell/src/Unicorn/Internal/Util.hs | 12 +- bindings/haskell/src/cbits/unicorn_wrapper.c | 4 + .../haskell/src/include/unicorn_wrapper.h | 5 + bindings/haskell/unicorn.cabal | 13 +- bindings/python/sample_arm.py | 6 +- bindings/python/sample_arm64.py | 2 +- bindings/python/sample_m68k.py | 30 +- bindings/python/sample_mips.py | 8 +- bindings/python/sample_sparc.py | 2 +- bindings/python/sample_x86.py | 236 +++++++++++---- bindings/python/setup.py | 2 +- bindings/python/unicorn/unicorn.py | 4 +- bindings/ruby/unicorn_gem/ext/unicorn.c | 42 +-- make.sh | 20 +- samples/Makefile | 131 +------- samples/sample_x86.c | 101 +++++++ samples/sample_x86_32_gdt_and_seg_regs.c | 2 +- tests/unit/Makefile | 40 +-- uc.c | 15 +- 38 files changed, 1149 insertions(+), 800 deletions(-) create mode 100644 bindings/go/unicorn/context.go create mode 100644 bindings/go/unicorn/context_test.go diff --git a/.appveyor.yml b/.appveyor.yml index 96d9d7fa..03d5d411 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -7,26 +7,15 @@ environment: CYG_CACHE: C:\cygwin64\var\cache\setup CYG_BASH: C:\cygwin64\bin\bash 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_SETUP: setup-x86.exe CYG_MIRROR: http://cygwin.mirror.constant.com CYG_CACHE: C:\cygwin\var\cache\setup CYG_BASH: C:\cygwin\bin\bash CC: gcc -# TODO: Uncomment -# - CYG_ROOT: C:\cygwin -# CYG_SETUP: setup-x86.exe -# CYG_MIRROR: http://cygwin.mirror.constant.com -# CYG_CACHE: C:\cygwin\var\cache\setup -# CYG_BASH: C:\cygwin\bin\bash -# CC: clang +# - MSYS_ROOT: C:\msys64 +# MSYS_BASH: C:\msys64\mingw64\bin\sh +# CC: x86_64-w64-mingw32-gcc # Cache Cygwin files to speed up build cache: @@ -42,13 +31,11 @@ init: # Install needed build dependencies install: - 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' - - '%CYG_BASH% -lc "cygcheck -dc cygwin"' - + - 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) + - if defined MSYS_ROOT (%MSYS_BASH% -lc "pacman -S --noconfirm mingw-w64-x86_64-glib2") build_script: - # TODO: uncomment and enable tests - - '%CYG_BASH% -lc "export CYGWIN=winsymlinks:native; cd $APPVEYOR_BUILD_FOLDER; ./install-cmocka-linux.sh; ./make.sh; export PATH=$PATH:../../:../../cmocka/src; make test"' - #- '%CYG_BASH% -lc "export CYGWIN=winsymlinks:native; cd $APPVEYOR_BUILD_FOLDER; ./install-cmocka-linux.sh; ./make.sh"' + - 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") + - 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") #- 'cd %APPVEYOR_BUILD_FOLDER% && cd bindings\dotnet && msbuild UnicornDotNet.sln' # Allows RDP #on_finish: diff --git a/.travis.yml b/.travis.yml index 3f12f302..75139721 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,42 +2,30 @@ language: c sudo: false before_install: - 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 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 -# - ./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 compiler: - clang - gcc -# TODO update mingw32 to gcc 4.7+ for compilation -# - i686-w64-mingw32-gcc -# - x86_64-w64-mingw32-gcc os: - linux - osx -#matrix: -# exclude: -# - os: osx -# compiler: i686-w64-mingw32-gcc +matrix: + include: + - os: osx + compiler: clang + env: MACOS_UNIVERSAL=yes + - os: osx + compiler: gcc + env: MACOS_UNIVERSAL=yes # - os: osx # compiler: x86_64-w64-mingw32-gcc addons: apt: packages: - - mingw-w64 - - 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 + # mingw-w64 packages too old in precise diff --git a/Makefile b/Makefile index 75f51228..2f828d35 100644 --- a/Makefile +++ b/Makefile @@ -63,6 +63,7 @@ ifeq ($(UNICORN_DEBUG),yes) CFLAGS += -g else CFLAGS += -O3 +UNICORN_QEMU_FLAGS += --disable-debug-info endif ifeq ($(UNICORN_ASAN),yes) @@ -97,9 +98,6 @@ PKG_VERSION = $(PKG_MAJOR).$(PKG_MINOR).$(PKG_EXTRA) endif 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? ifeq ($(UNAME_S),Darwin) @@ -127,6 +125,8 @@ else ifneq ($(filter MINGW%,$(UNAME_S)),) EXT = dll AR_EXT = lib BIN_EXT = .exe +UNICORN_QEMU_FLAGS += --disable-stack-protector +UNICORN_CFLAGS := $(UNICORN_CFLAGS:-fPIC=) # Linux, Darwin else @@ -139,27 +139,25 @@ endif ifeq ($(UNICORN_SHARED),yes) ifneq ($(filter MINGW%,$(UNAME_S)),) -LIBRARY = $(BLDIR)/$(LIBNAME).$(EXT) +LIBRARY = $(LIBNAME).$(EXT) else ifneq ($(filter CYGWIN%,$(UNAME_S)),) -LIBRARY = $(BLDIR)/cyg$(LIBNAME).$(EXT) -LIBRARY_DLLA = $(BLDIR)/lib$(LIBNAME).$(EXT).$(AR_EXT) +LIBRARY = cyg$(LIBNAME).$(EXT) +LIBRARY_DLLA = lib$(LIBNAME).$(EXT).$(AR_EXT) $(LIBNAME)_LDFLAGS += -Wl,--out-implib=$(LIBRARY_DLLA) $(LIBNAME)_LDFLAGS += -lssp # Linux, Darwin else -LIBRARY = $(BLDIR)/lib$(LIBNAME).$(VERSION_EXT) -LIBRARY_SYMLINK = $(BLDIR)/lib$(LIBNAME).$(EXT) +LIBRARY = lib$(LIBNAME).$(VERSION_EXT) +LIBRARY_SYMLINK = lib$(LIBNAME).$(EXT) endif endif ifeq ($(UNICORN_STATIC),yes) ifneq ($(filter MINGW%,$(UNAME_S)),) -ARCHIVE = $(BLDIR)/$(LIBNAME).$(AR_EXT) -else ifneq ($(filter CYGWIN%,$(UNAME_S)),) -ARCHIVE = $(BLDIR)/lib$(LIBNAME).$(AR_EXT) -# Linux, Darwin +ARCHIVE = $(LIBNAME).$(AR_EXT) +# Cygwin, Linux, Darwin else -ARCHIVE = $(BLDIR)/lib$(LIBNAME).$(AR_EXT) +ARCHIVE = lib$(LIBNAME).$(AR_EXT) endif endif @@ -169,8 +167,6 @@ INSTALL_LIB ?= $(INSTALL_BIN) -m0755 PKGCFGF = $(LIBNAME).pc PREFIX ?= /usr DESTDIR ?= -BLDIR = . -OBJDIR = . LIBDIRARCH ?= lib # Uncomment the below line to installs x86_64 libs to lib64/ directory. @@ -200,75 +196,39 @@ else PKGCFGDIR ?= $(LIBDATADIR)/pkgconfig endif -all: compile_lib -ifeq (,$(findstring yes,$(UNICORN_BUILD_CORE_ONLY))) -ifeq ($(UNICORN_SHARED),yes) -ifeq ($(V),0) - @$(INSTALL_LIB) $(LIBRARY) $(BLDIR)/samples/ -else - $(INSTALL_LIB) $(LIBRARY) $(BLDIR)/samples/ -endif -endif - - @cd samples && $(MAKE) -endif +.PHONY: all +all: unicorn + $(MAKE) -C samples config: if [ "$(UNICORN_ARCHS)" != "`cat config.log`" ]; then $(MAKE) clean; fi qemu/config-host.h-timestamp: -ifeq ($(UNICORN_DEBUG),yes) cd qemu && \ ./configure --cc="${CC}" --extra-cflags="$(UNICORN_CFLAGS)" --target-list="$(UNICORN_TARGETS)" ${UNICORN_QEMU_FLAGS} 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 - cd qemu && $(MAKE) -j 4 - $(MAKE) unicorn + $(MAKE) -C qemu -j 4 -unicorn: $(LIBRARY) $(ARCHIVE) +unicorn: compile_lib $(LIBRARY) $(ARCHIVE) -$(LIBRARY): $(UC_TARGET_OBJ) uc.o list.o -ifeq ($(UNICORN_SHARED),yes) -ifeq ($(V),0) - $(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 +$(LIBRARY): $(UC_TARGET_OBJ) + $(CC) $(CFLAGS) -shared $(GENOBJ) uc.o list.o -o $(LIBRARY) $(GLIB) -lm $($(LIBNAME)_LDFLAGS) + ln -sf $(LIBRARY) $(LIBRARY_SYMLINK) $(ARCHIVE): $(UC_TARGET_OBJ) uc.o list.o -ifeq ($(UNICORN_STATIC),yes) -ifeq ($(V),0) - $(call log,GEN,$(ARCHIVE)) - @$(create-archive) -else - $(create-archive) -endif -endif + $(AR) q $(ARCHIVE) $^ + $(RANLIB) $(ARCHIVE) $(PKGCFGF): -ifeq ($(V),0) - $(call log,GEN,$(@:$(BLDIR)/%=%)) - @$(generate-pkgcfg) -else $(generate-pkgcfg) -endif - .PHONY: test test: all $(MAKE) -C tests/unit test + $(MAKE) -C bindings test install: compile_lib $(PKGCFGF) mkdir -p $(DESTDIR)$(LIBDIR) @@ -309,7 +269,7 @@ dist: 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\ powerpc sparc sparc64 x86_64) $(foreach var,$(TARGETS),\ @@ -328,10 +288,7 @@ clean: $(MAKE) -C qemu clean rm -rf *.d *.o rm -rf lib$(LIBNAME)* $(LIBNAME)*.lib $(LIBNAME)*.dll cyg$(LIBNAME)*.dll -ifeq (,$(findstring yes,$(UNICORN_BUILD_CORE_ONLY))) - cd samples && $(MAKE) clean - rm -f $(BLDIR)/samples/lib$(LIBNAME).$(EXT) -endif + $(MAKE) -C samples clean $(MAKE) -C tests/unit clean @@ -351,10 +308,3 @@ define log @printf " %-7s %s\n" "$(1)" "$(2)" endef - -define create-archive - $(AR) q $(ARCHIVE) $^ - $(RANLIB) $(ARCHIVE) -endef - -FORCE: diff --git a/bindings/Makefile b/bindings/Makefile index dee75187..7ded2e10 100644 --- a/bindings/Makefile +++ b/bindings/Makefile @@ -13,7 +13,7 @@ SAMPLE_X86 = $(TMP_DIR)/sample_x86 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: $(MAKE) -C python gen_const @@ -26,9 +26,7 @@ install: build $(MAKE) -C python install $(MAKE) -C java install -samples: expected python - -sample_python: expected python +test: expected python sample_diff expected: $(MAKE) -C ../samples @@ -38,9 +36,11 @@ expected: $(ENV_VARS) ../samples/sample_mips > $(SAMPLE_MIPS)_e $(ENV_VARS) ../samples/sample_sparc > $(SAMPLE_SPARC)_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 $(ENV_VARS) python python/sample_arm.py > $(SAMPLE_ARM)_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_m68k.py > $(SAMPLE_M68K)_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_ARM64)_e $(SAMPLE_ARM64)_o $(DIFF) $(SAMPLE_MIPS)_e $(SAMPLE_MIPS)_o @@ -65,5 +64,3 @@ clean: check: make -C python check - -FORCE: diff --git a/bindings/go/unicorn/context.go b/bindings/go/unicorn/context.go new file mode 100644 index 00000000..bf5ced9b --- /dev/null +++ b/bindings/go/unicorn/context.go @@ -0,0 +1,28 @@ +package unicorn + +import ( + "runtime" +) + +// #include +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)) +} diff --git a/bindings/go/unicorn/context_test.go b/bindings/go/unicorn/context_test.go new file mode 100644 index 00000000..3231ef43 --- /dev/null +++ b/bindings/go/unicorn/context_test.go @@ -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") + } +} diff --git a/bindings/go/unicorn/unicorn.go b/bindings/go/unicorn/unicorn.go index 59ad50e5..58e28939 100644 --- a/bindings/go/unicorn/unicorn.go +++ b/bindings/go/unicorn/unicorn.go @@ -55,6 +55,9 @@ type Unicorn interface { HookDel(hook Hook) error Query(queryType int) (uint64, error) Close() error + + ContextSave(reuse Context) (Context, error) + ContextRestore(Context) error } type uc struct { diff --git a/bindings/haskell/samples/SampleX86.hs b/bindings/haskell/samples/SampleX86.hs index fad686af..e395ed75 100644 --- a/bindings/haskell/samples/SampleX86.hs +++ b/bindings/haskell/samples/SampleX86.hs @@ -40,6 +40,10 @@ x86Code32JmpInvalid = BS.pack [0xe9, 0xe9, 0xee, 0xee, 0xee, 0x41, 0x4a] x86Code32InOut :: BS.ByteString x86Code32InOut = BS.pack [0x41, 0xe4, 0x3f, 0x4a, 0xe6, 0x46, 0x43] +-- inc eax +x86Code32Inc :: BS.ByteString +x86Code32Inc = BS.pack [0x40] + x86Code64 :: BS.ByteString x86Code64 = BS.pack [0x41, 0xbc, 0x3b, 0xb0, 0x28, 0x2a, 0x49, 0x0f, 0xc9, 0x90, 0x4d, 0x0f, 0xad, 0xcf, 0x49, 0x87, 0xfd, 0x90, @@ -494,6 +498,70 @@ testI386InOut = do Left err -> putStrLn $ "Failed with error " ++ show 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 = do putStrLn "Emulate x86_64 code" @@ -660,6 +728,7 @@ main = do ["-32"] -> do testI386 testI386InOut + testI386ContextSave testI386Jump testI386Loop testI386InvalidMemRead diff --git a/bindings/haskell/src/Unicorn.hs b/bindings/haskell/src/Unicorn.hs index 7f05eca4..19c07f8c 100644 --- a/bindings/haskell/src/Unicorn.hs +++ b/bindings/haskell/src/Unicorn.hs @@ -9,41 +9,47 @@ framework based on QEMU. Further information is available at . -} -module Unicorn ( - -- * Emulator control - Emulator, - Engine, - Architecture(..), - Mode(..), - QueryType(..), - runEmulator, - open, - query, - start, - stop, +module Unicorn + ( -- * Emulator control + Emulator + , Engine + , Architecture(..) + , Mode(..) + , QueryType(..) + , runEmulator + , open + , query + , start + , stop - -- * Register operations - regWrite, - regRead, + -- * Register operations + , regWrite + , regRead - -- * Memory operations - MemoryPermission(..), - MemoryRegion(..), - memWrite, - memRead, - memMap, - memUnmap, - memProtect, - memRegions, + -- * Memory operations + , MemoryPermission(..) + , MemoryRegion(..) + , memWrite + , memRead + , memMap + , memUnmap + , memProtect + , memRegions - -- * Error handling - Error(..), - errno, - strerror, + -- * Context operations + , Context + , contextAllocate + , contextSave + , contextRestore - -- * Misc. - version, -) where + -- * Error handling + , Error(..) + , errno + , strerror + + -- * Misc. + , version + ) where import Control.Monad (liftM) import Control.Monad.Trans.Class (lift) @@ -132,8 +138,8 @@ stop uc = do ------------------------------------------------------------------------------- -- | Write to register. -regWrite :: Reg r => - Engine -- ^ 'Unicorn' engine handle +regWrite :: Reg r + => Engine -- ^ 'Unicorn' engine handle -> r -- ^ Register ID to write to -> Int64 -- ^ Value to write to register -> Emulator () -- ^ An 'Error' on failure @@ -147,8 +153,8 @@ regWrite uc regId value = do left err -- | Read register value. -regRead :: Reg r => - Engine -- ^ 'Unicorn' engine handle +regRead :: Reg r + => Engine -- ^ 'Unicorn' engine handle -> r -- ^ Register ID to read from -> Emulator Int64 -- ^ The value read from the register on success, -- or an 'Error' on failure @@ -259,6 +265,46 @@ memRegions uc = do else 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. ------------------------------------------------------------------------------- diff --git a/bindings/haskell/src/Unicorn/CPU/Arm.chs b/bindings/haskell/src/Unicorn/CPU/Arm.chs index fbc3294c..138bf179 100644 --- a/bindings/haskell/src/Unicorn/CPU/Arm.chs +++ b/bindings/haskell/src/Unicorn/CPU/Arm.chs @@ -8,22 +8,25 @@ License : GPL-2 Definitions for the ARM architecture. -} -module Unicorn.CPU.Arm ( - Register(..), -) where +module Unicorn.CPU.Arm + ( + Register(..) + ) where import Unicorn.Internal.Core (Reg) -{# context lib="unicorn" #} +{# context lib = "unicorn" #} #include -- | ARM registers. {# enum uc_arm_reg as Register - {underscoreToCase} - omit (UC_ARM_REG_INVALID, - UC_ARM_REG_ENDING) - with prefix="UC_ARM_REG_" - deriving (Show, Eq, Bounded) #} + { underscoreToCase } + omit ( UC_ARM_REG_INVALID + , UC_ARM_REG_ENDING + ) + with prefix = "UC_ARM_REG_" + deriving (Show, Eq, Bounded) +#} instance Reg Register diff --git a/bindings/haskell/src/Unicorn/CPU/Arm64.chs b/bindings/haskell/src/Unicorn/CPU/Arm64.chs index 6174ef89..f4f1dec3 100644 --- a/bindings/haskell/src/Unicorn/CPU/Arm64.chs +++ b/bindings/haskell/src/Unicorn/CPU/Arm64.chs @@ -8,22 +8,25 @@ License : GPL-2 Definitions for the ARM64 (ARMv8) architecture. -} -module Unicorn.CPU.Arm64 ( - Register(..), -) where +module Unicorn.CPU.Arm64 + ( + Register(..) + ) where import Unicorn.Internal.Core (Reg) -{# context lib="unicorn" #} +{# context lib = "unicorn" #} #include -- | ARM64 registers. {# enum uc_arm64_reg as Register - {underscoreToCase} - omit (UC_ARM64_REG_INVALID, - UC_ARM64_REG_ENDING) - with prefix="UC_ARM64_REG_" - deriving (Show, Eq, Bounded) #} + { underscoreToCase } + omit ( UC_ARM64_REG_INVALID + , UC_ARM64_REG_ENDING + ) + with prefix = "UC_ARM64_REG_" + deriving (Show, Eq, Bounded) +#} instance Reg Register diff --git a/bindings/haskell/src/Unicorn/CPU/M68k.chs b/bindings/haskell/src/Unicorn/CPU/M68k.chs index 25753aa4..b06ffb30 100644 --- a/bindings/haskell/src/Unicorn/CPU/M68k.chs +++ b/bindings/haskell/src/Unicorn/CPU/M68k.chs @@ -8,22 +8,25 @@ License : GPL-2 Definitions for the MK68K architecture. -} -module Unicorn.CPU.M68k ( - Register(..), -) where +module Unicorn.CPU.M68k + ( + Register(..) + ) where import Unicorn.Internal.Core (Reg) -{# context lib="unicorn" #} +{# context lib = "unicorn" #} #include -- | M68K registers. {# enum uc_m68k_reg as Register - {underscoreToCase} - omit (UC_M68K_REG_INVALID, - UC_M68K_REG_ENDING) - with prefix="UC_M68K_REG_" - deriving (Show, Eq, Bounded) #} + { underscoreToCase } + omit ( UC_M68K_REG_INVALID + , UC_M68K_REG_ENDING + ) + with prefix = "UC_M68K_REG_" + deriving (Show, Eq, Bounded) +#} instance Reg Register diff --git a/bindings/haskell/src/Unicorn/CPU/Mips.chs b/bindings/haskell/src/Unicorn/CPU/Mips.chs index b234ba72..8ec5db4d 100644 --- a/bindings/haskell/src/Unicorn/CPU/Mips.chs +++ b/bindings/haskell/src/Unicorn/CPU/Mips.chs @@ -8,54 +8,58 @@ License : GPL-2 Definitions for the MIPS architecture. -} -module Unicorn.CPU.Mips ( - Register(..), -) where +module Unicorn.CPU.Mips + ( + Register(..) + ) where import Unicorn.Internal.Core (Reg) -{# context lib="unicorn" #} +{# context lib = "unicorn" #} #include -- | MIPS registers. {# enum UC_MIPS_REG as Register - {underscoreToCase, - UC_MIPS_REG_0 as Reg0, - UC_MIPS_REG_1 as Reg1, - UC_MIPS_REG_2 as Reg2, - UC_MIPS_REG_3 as Reg3, - UC_MIPS_REG_4 as Reg4, - UC_MIPS_REG_5 as Reg5, - UC_MIPS_REG_6 as Reg6, - UC_MIPS_REG_7 as Reg7, - UC_MIPS_REG_8 as Reg8, - UC_MIPS_REG_9 as Reg9, - UC_MIPS_REG_10 as Reg10, - UC_MIPS_REG_11 as Reg11, - UC_MIPS_REG_12 as Reg12, - UC_MIPS_REG_13 as Reg13, - UC_MIPS_REG_14 as Reg14, - UC_MIPS_REG_15 as Reg15, - UC_MIPS_REG_16 as Reg16, - UC_MIPS_REG_17 as Reg17, - UC_MIPS_REG_18 as Reg18, - UC_MIPS_REG_19 as Reg19, - UC_MIPS_REG_20 as Reg20, - UC_MIPS_REG_21 as Reg21, - UC_MIPS_REG_22 as Reg22, - UC_MIPS_REG_23 as Reg23, - UC_MIPS_REG_24 as Reg24, - UC_MIPS_REG_25 as Reg25, - UC_MIPS_REG_26 as Reg26, - UC_MIPS_REG_27 as Reg27, - UC_MIPS_REG_28 as Reg28, - UC_MIPS_REG_29 as Reg29, - UC_MIPS_REG_30 as Reg30, - UC_MIPS_REG_31 as Reg31} - omit (UC_MIPS_REG_INVALID, - UC_MIPS_REG_ENDING) - with prefix="UC_MIPS_REG_" - deriving (Show, Eq, Bounded) #} + { underscoreToCase + , UC_MIPS_REG_0 as Reg0g + , UC_MIPS_REG_1 as Reg1g + , UC_MIPS_REG_2 as Reg2g + , UC_MIPS_REG_3 as Reg3g + , UC_MIPS_REG_4 as Reg4g + , UC_MIPS_REG_5 as Reg5g + , UC_MIPS_REG_6 as Reg6g + , UC_MIPS_REG_7 as Reg7g + , UC_MIPS_REG_8 as Reg8g + , UC_MIPS_REG_9 as Reg9g + , UC_MIPS_REG_10 as Reg10g + , UC_MIPS_REG_11 as Reg11g + , UC_MIPS_REG_12 as Reg12g + , UC_MIPS_REG_13 as Reg13g + , UC_MIPS_REG_14 as Reg14g + , UC_MIPS_REG_15 as Reg15g + , UC_MIPS_REG_16 as Reg16g + , UC_MIPS_REG_17 as Reg17g + , UC_MIPS_REG_18 as Reg18g + , UC_MIPS_REG_19 as Reg19g + , UC_MIPS_REG_20 as Reg20g + , UC_MIPS_REG_21 as Reg21g + , UC_MIPS_REG_22 as Reg22g + , UC_MIPS_REG_23 as Reg23g + , UC_MIPS_REG_24 as Reg24g + , UC_MIPS_REG_25 as Reg25g + , UC_MIPS_REG_26 as Reg26g + , UC_MIPS_REG_27 as Reg27g + , UC_MIPS_REG_28 as Reg28g + , UC_MIPS_REG_29 as Reg29g + , UC_MIPS_REG_30 as Reg30g + , UC_MIPS_REG_31 as Reg31 + } + omit ( UC_MIPS_REG_INVALID + , UC_MIPS_REG_ENDING + ) + with prefix = "UC_MIPS_REG_" + deriving (Show, Eq, Bounded) +#} instance Reg Register diff --git a/bindings/haskell/src/Unicorn/CPU/Sparc.chs b/bindings/haskell/src/Unicorn/CPU/Sparc.chs index a94c1b22..e54262bd 100644 --- a/bindings/haskell/src/Unicorn/CPU/Sparc.chs +++ b/bindings/haskell/src/Unicorn/CPU/Sparc.chs @@ -8,22 +8,25 @@ License : GPL-2 Definitions for the SPARC architecture. -} -module Unicorn.CPU.Sparc ( - Register(..), -) where +module Unicorn.CPU.Sparc + ( + Register(..) + ) where import Unicorn.Internal.Core (Reg) -{# context lib="unicorn" #} +{# context lib = "unicorn" #} #include -- | SPARC registers. {# enum uc_sparc_reg as Register - {underscoreToCase} - omit (UC_SPARC_REG_INVALID, - UC_SPARC_REG_ENDING) - with prefix="UC_SPARC_REG_" - deriving (Show, Eq, Bounded) #} + { underscoreToCase } + omit (UC_SPARC_REG_INVALID + , UC_SPARC_REG_ENDING + ) + with prefix = "UC_SPARC_REG_" + deriving (Show, Eq, Bounded) +#} instance Reg Register diff --git a/bindings/haskell/src/Unicorn/CPU/X86.chs b/bindings/haskell/src/Unicorn/CPU/X86.chs index eb99c978..56608c17 100644 --- a/bindings/haskell/src/Unicorn/CPU/X86.chs +++ b/bindings/haskell/src/Unicorn/CPU/X86.chs @@ -8,11 +8,12 @@ License : GPL-2 Definitions for the X86 architecture. -} -module Unicorn.CPU.X86 ( - Mmr(..), - Register(..), - Instruction(..), -) where +module Unicorn.CPU.X86 + ( + Mmr(..) + , Register(..) + , Instruction(..) + ) where import Control.Applicative import Data.Word @@ -20,18 +21,18 @@ import Foreign import Unicorn.Internal.Core (Reg) -{# context lib="unicorn" #} +{# context lib = "unicorn" #} #include -- | Memory-managemen Register for instructions IDTR, GDTR, LDTR, TR. -- Borrow from SegmentCache in qemu/target-i386/cpu.h -data Mmr = Mmr { - mmrSelector :: Word16, -- ^ Not used by GDTR and IDTR - mmrBase :: Word64, -- ^ Handle 32 or 64 bit CPUs - mmrLimit :: Word32, - mmrFlags :: Word32 -- ^ Not used by GDTR and IDTR -} +data Mmr = Mmr + { mmrSelector :: Word16 -- ^ Not used by GDTR and IDTR + , mmrBase :: Word64 -- ^ Handle 32 or 64 bit CPUs + , mmrLimit :: Word32 + , mmrFlags :: Word32 -- ^ Not used by GDTR and IDTR + } instance Storable Mmr where sizeOf _ = {# sizeof uc_x86_mmr #} @@ -48,18 +49,22 @@ instance Storable Mmr where -- | X86 registers. {# enum uc_x86_reg as Register - {underscoreToCase} - omit (UC_X86_REG_INVALID, - UC_X86_REG_ENDING) - with prefix="UC_X86_REG_" - deriving (Show, Eq, Bounded) #} + { underscoreToCase } + omit ( UC_X86_REG_INVALID + , UC_X86_REG_ENDING + ) + with prefix = "UC_X86_REG_" + deriving (Show, Eq, Bounded) +#} instance Reg Register -- | X86 instructions. {# enum uc_x86_insn as Instruction - {underscoreToCase} - omit (UC_X86_INS_INVALID, - UC_X86_INS_ENDING) - with prefix="UC_X86_INS_" - deriving (Show, Eq, Bounded) #} + { underscoreToCase } + omit ( UC_X86_INS_INVALID + , UC_X86_INS_ENDING + ) + with prefix = "UC_X86_INS_" + deriving (Show, Eq, Bounded) +#} diff --git a/bindings/haskell/src/Unicorn/Hook.hs b/bindings/haskell/src/Unicorn/Hook.hs index 2e13ebc4..9595a140 100644 --- a/bindings/haskell/src/Unicorn/Hook.hs +++ b/bindings/haskell/src/Unicorn/Hook.hs @@ -6,36 +6,36 @@ License : GPL-2 Insert hook points into the Unicorn emulator engine. -} -module Unicorn.Hook ( - -- * Hook types - Hook, - MemoryHookType(..), - MemoryEventHookType(..), - MemoryAccess(..), +module Unicorn.Hook + ( -- * Hook types + Hook + , MemoryHookType(..) + , MemoryEventHookType(..) + , MemoryAccess(..) - -- * Hook callbacks - CodeHook, - InterruptHook, - BlockHook, - InHook, - OutHook, - SyscallHook, - MemoryHook, - MemoryReadHook, - MemoryWriteHook, - MemoryEventHook, + -- * Hook callbacks + , CodeHook + , InterruptHook + , BlockHook + , InHook + , OutHook + , SyscallHook + , MemoryHook + , MemoryReadHook + , MemoryWriteHook + , MemoryEventHook - -- * Hook callback management - codeHookAdd, - interruptHookAdd, - blockHookAdd, - inHookAdd, - outHookAdd, - syscallHookAdd, - memoryHookAdd, - memoryEventHookAdd, - hookDel, -) where + -- * Hook callback management + , codeHookAdd + , interruptHookAdd + , blockHookAdd + , inHookAdd + , outHookAdd + , syscallHookAdd + , memoryHookAdd + , memoryEventHookAdd + , hookDel + ) where import Control.Monad import Control.Monad.Trans.Class @@ -213,7 +213,8 @@ hookDel uc hook = do -- Takes the tuple returned by `ucHookAdd`, an IO (Error, Hook), and -- returns either a `Right Hook` if no error occurred or a `Left Error` if an -- error occurred -getResult :: IO (Error, Hook) -> IO (Either Error Hook) +getResult :: IO (Error, Hook) + -> IO (Either Error Hook) getResult = liftM (uncurry checkResult) where checkResult err hook = diff --git a/bindings/haskell/src/Unicorn/Internal/Core.chs b/bindings/haskell/src/Unicorn/Internal/Core.chs index a69f51c3..dcc6a7fc 100644 --- a/bindings/haskell/src/Unicorn/Internal/Core.chs +++ b/bindings/haskell/src/Unicorn/Internal/Core.chs @@ -17,31 +17,34 @@ import Control.Monad import Control.Monad.Trans.Either (EitherT) import Foreign -{# context lib="unicorn" #} +{# context lib = "unicorn" #} #include #include "unicorn_wrapper.h" -- | The Unicorn engine. {# pointer *uc_engine as Engine - foreign finalizer uc_close_wrapper as close - newtype #} + foreign finalizer uc_close_wrapper as close + newtype +#} -- | A pointer to a Unicorn engine. {# pointer *uc_engine as EnginePtr -> Engine #} -- | Make a new Unicorn engine out of an engine pointer. The returned Unicorn -- engine will automatically call 'uc_close_wrapper' when it goes out of scope. -mkEngine :: EnginePtr -> IO Engine +mkEngine :: EnginePtr + -> IO Engine mkEngine ptr = liftM Engine (newForeignPtr close ptr) -- | Errors encountered by the Unicorn API. These values are returned by -- 'errno'. {# enum uc_err as Error - {underscoreToCase} - with prefix="UC_" - deriving (Show, Eq, Bounded) #} + { underscoreToCase } + with prefix = "UC_" + deriving (Show, Eq, Bounded) +#} -- | The emulator runs in the IO monad and allows for the handling of errors -- "under the hood". diff --git a/bindings/haskell/src/Unicorn/Internal/Hook.chs b/bindings/haskell/src/Unicorn/Internal/Hook.chs index 00a8a123..affed872 100644 --- a/bindings/haskell/src/Unicorn/Internal/Hook.chs +++ b/bindings/haskell/src/Unicorn/Internal/Hook.chs @@ -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 way cabal handles ordering of chs files. -} -module Unicorn.Internal.Hook ( - -- * Types - Hook, - HookType(..), - MemoryHookType(..), - MemoryEventHookType(..), - MemoryAccess(..), +module Unicorn.Internal.Hook + ( -- * Types + Hook + , HookType(..) + , MemoryHookType(..) + , MemoryEventHookType(..) + , MemoryAccess(..) - -- * Hook callback bindings - CodeHook, - InterruptHook, - BlockHook, - InHook, - OutHook, - SyscallHook, - MemoryHook, - MemoryReadHook, - MemoryWriteHook, - MemoryEventHook, + -- * Hook callback bindings + , CodeHook + , InterruptHook + , BlockHook + , InHook + , OutHook + , SyscallHook + , MemoryHook + , MemoryReadHook + , MemoryWriteHook + , MemoryEventHook - -- * Hook marshalling - marshalCodeHook, - marshalInterruptHook, - marshalBlockHook, - marshalInHook, - marshalOutHook, - marshalSyscallHook, - marshalMemoryHook, - marshalMemoryReadHook, - marshalMemoryWriteHook, - marshalMemoryEventHook, + -- * Hook marshallin + , marshalCodeHook + , marshalInterruptHook + , marshalBlockHook + , marshalInHook + , marshalOutHook + , marshalSyscallHook + , marshalMemoryHook + , marshalMemoryReadHook + , marshalMemoryWriteHook + , marshalMemoryEventHook - -- * Hook registration and deletion bindings - ucHookAdd, - ucInsnHookAdd, - ucHookDel, -) where + -- * Hook registration and deletion bindings + , ucHookAdd + , ucInsnHookAdd + , ucHookDel + ) where import Control.Monad import Foreign import Unicorn.Internal.Util -{# context lib="unicorn" #} - {# import Unicorn.Internal.Core #} {# import Unicorn.CPU.X86 #} +{# context lib = "unicorn" #} + #include #include "unicorn_wrapper.h" @@ -79,7 +79,8 @@ import Unicorn.Internal.Util foreign import ccall "&uc_close_dummy" closeDummy :: FunPtr (EnginePtr -> IO ()) -mkEngineNC :: EnginePtr -> IO Engine +mkEngineNC :: EnginePtr + -> IO Engine mkEngineNC 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 -- this enum (and are exposed to the user). {# enum uc_hook_type as HookType - {underscoreToCase} - omit (UC_HOOK_MEM_READ_UNMAPPED, - UC_HOOK_MEM_WRITE_UNMAPPED, - UC_HOOK_MEM_FETCH_UNMAPPED, - UC_HOOK_MEM_READ_PROT, - UC_HOOK_MEM_WRITE_PROT, - UC_HOOK_MEM_FETCH_PROT, - UC_HOOK_MEM_READ, - UC_HOOK_MEM_WRITE, - UC_HOOK_MEM_FETCH) - with prefix="UC_" - deriving (Show, Eq, Bounded) #} + { underscoreToCase } + omit ( UC_HOOK_MEM_READ_UNMAPPED + , UC_HOOK_MEM_WRITE_UNMAPPED + , UC_HOOK_MEM_FETCH_UNMAPPED + , UC_HOOK_MEM_READ_PROT + , UC_HOOK_MEM_WRITE_PROT + , UC_HOOK_MEM_FETCH_PROT + , UC_HOOK_MEM_READ + , UC_HOOK_MEM_WRITE + , UC_HOOK_MEM_FETCH + , UC_HOOK_MEM_READ_AFTER + ) + with prefix = "UC_" + deriving (Show, Eq, Bounded) +#} -- | Memory hook types (for valid memory accesses). {# enum uc_hook_type as MemoryHookType - {underscoreToCase} - omit (UC_HOOK_INTR, - UC_HOOK_INSN, - UC_HOOK_CODE, - UC_HOOK_BLOCK, - UC_HOOK_MEM_READ_UNMAPPED, - UC_HOOK_MEM_WRITE_UNMAPPED, - UC_HOOK_MEM_FETCH_UNMAPPED, - UC_HOOK_MEM_READ_PROT, - UC_HOOK_MEM_WRITE_PROT, - UC_HOOK_MEM_FETCH_PROT) - with prefix="UC_" - deriving (Show, Eq, Bounded) #} + { underscoreToCase } + omit ( UC_HOOK_INTR + , UC_HOOK_INSN + , UC_HOOK_CODE + , UC_HOOK_BLOCK + , UC_HOOK_MEM_READ_UNMAPPED + , UC_HOOK_MEM_WRITE_UNMAPPED + , UC_HOOK_MEM_FETCH_UNMAPPED + , UC_HOOK_MEM_READ_PROT + , UC_HOOK_MEM_WRITE_PROT + , UC_HOOK_MEM_FETCH_PROT + ) + with prefix = "UC_" + deriving (Show, Eq, Bounded) +#} -- | Memory event hook types (for invalid memory accesses). {# enum uc_hook_type as MemoryEventHookType - {underscoreToCase} - omit (UC_HOOK_INTR, - UC_HOOK_INSN, - UC_HOOK_CODE, - UC_HOOK_BLOCK, - UC_HOOK_MEM_READ, - UC_HOOK_MEM_WRITE, - UC_HOOK_MEM_FETCH) - with prefix="UC_" - deriving (Show, Eq, Bounded) #} + { underscoreToCase } + omit ( UC_HOOK_INTR + , UC_HOOK_INSN + , UC_HOOK_CODE + , UC_HOOK_BLOCK + , UC_HOOK_MEM_READ + , UC_HOOK_MEM_WRITE + , UC_HOOK_MEM_FETCH + , UC_HOOK_MEM_READ_AFTER + ) + with prefix = "UC_" + deriving (Show, Eq, Bounded) +#} -- | Unify the hook types with a type class class Enum a => HookTypeC a @@ -143,9 +152,10 @@ instance HookTypeC MemoryEventHookType -- | Memory access. {# enum uc_mem_type as MemoryAccess - {underscoreToCase} - with prefix="UC_" - deriving (Show, Eq, Bounded) #} + { underscoreToCase } + with prefix = "UC_" + deriving (Show, Eq, Bounded) +#} ------------------------------------------------------------------------------- -- Hook callbacks @@ -159,16 +169,18 @@ type CodeHook a = Engine -- ^ 'Unicorn' engine handle -> a -- ^ User data passed to tracing APIs -> IO () -type CCodeHook = EnginePtr -> Word64 -> Word32 -> Ptr () -> IO () +type CCodeHook = EnginePtr -> Word64 -> Word32 -> Ptr () -> IO () foreign import ccall "wrapper" - mkCodeHook :: CCodeHook -> IO {# type uc_cb_hookcode_t #} + mkCodeHook :: CCodeHook + -> IO {# type uc_cb_hookcode_t #} marshalCodeHook :: Storable a - => CodeHook a -> IO {# type uc_cb_hookcode_t #} + => CodeHook a + -> IO {# type uc_cb_hookcode_t #} marshalCodeHook codeHook = mkCodeHook $ \ucPtr address size userDataPtr -> do - uc <- mkEngineNC ucPtr + uc <- mkEngineNC ucPtr userData <- castPtrAndPeek userDataPtr let maybeSize = if size == 0 then Nothing else Just $ fromIntegral size @@ -186,10 +198,11 @@ foreign import ccall "wrapper" mkInterruptHook :: CInterruptHook -> IO {# type uc_cb_hookintr_t #} marshalInterruptHook :: Storable a - => InterruptHook a -> IO {# type uc_cb_hookintr_t #} + => InterruptHook a + -> IO {# type uc_cb_hookintr_t #} marshalInterruptHook interruptHook = mkInterruptHook $ \ucPtr intNo userDataPtr -> do - uc <- mkEngineNC ucPtr + uc <- mkEngineNC ucPtr userData <- castPtrAndPeek userDataPtr interruptHook uc (fromIntegral intNo) userData @@ -197,7 +210,8 @@ marshalInterruptHook interruptHook = type BlockHook a = CodeHook a marshalBlockHook :: Storable a - => BlockHook a -> IO {# type uc_cb_hookcode_t #} + => BlockHook a + -> IO {# type uc_cb_hookcode_t #} marshalBlockHook = marshalCodeHook @@ -214,10 +228,11 @@ foreign import ccall "wrapper" mkInHook :: CInHook -> IO {# type uc_cb_insn_in_t #} marshalInHook :: Storable a - => InHook a -> IO {# type uc_cb_insn_in_t #} + => InHook a + -> IO {# type uc_cb_insn_in_t #} marshalInHook inHook = mkInHook $ \ucPtr port size userDataPtr -> do - uc <- mkEngineNC ucPtr + uc <- mkEngineNC ucPtr userData <- castPtrAndPeek userDataPtr 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 () 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 - => OutHook a -> IO {# type uc_cb_insn_out_t #} + => OutHook a + -> IO {# type uc_cb_insn_out_t #} marshalOutHook outHook = mkOutHook $ \ucPtr port size value userDataPtr -> do - uc <- mkEngineNC ucPtr + uc <- mkEngineNC ucPtr userData <- castPtrAndPeek userDataPtr outHook uc (fromIntegral port) (fromIntegral size) (fromIntegral value) userData @@ -251,13 +268,15 @@ type SyscallHook a = Engine -- ^ 'Unicorn' engine handle type CSyscallHook = Ptr () -> Ptr () -> IO () 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 - => SyscallHook a -> IO {# type uc_cb_insn_syscall_t #} + => SyscallHook a + -> IO {# type uc_cb_insn_syscall_t #} marshalSyscallHook syscallHook = mkSyscallHook $ \ucPtr userDataPtr -> do - uc <- mkEngineNC $ castPtr ucPtr + uc <- mkEngineNC $ castPtr ucPtr userData <- castPtrAndPeek userDataPtr syscallHook uc userData @@ -281,13 +300,15 @@ type CMemoryHook = EnginePtr -> IO () foreign import ccall "wrapper" - mkMemoryHook :: CMemoryHook -> IO {# type uc_cb_hookmem_t #} + mkMemoryHook :: CMemoryHook + -> IO {# type uc_cb_hookmem_t #} marshalMemoryHook :: Storable a - => MemoryHook a -> IO {# type uc_cb_hookmem_t #} + => MemoryHook a + -> IO {# type uc_cb_hookmem_t #} marshalMemoryHook memoryHook = mkMemoryHook $ \ucPtr memAccessI address size value userDataPtr -> do - uc <- mkEngineNC ucPtr + uc <- mkEngineNC ucPtr userData <- castPtrAndPeek userDataPtr let memAccess = toMemAccess memAccessI maybeValue = case memAccess of @@ -304,10 +325,11 @@ type MemoryReadHook a = Engine -- ^ 'Unicorn' engine handle -> IO () marshalMemoryReadHook :: Storable a - => MemoryReadHook a -> IO {# type uc_cb_hookmem_t #} + => MemoryReadHook a + -> IO {# type uc_cb_hookmem_t #} marshalMemoryReadHook memoryReadHook = mkMemoryHook $ \ucPtr _ address size _ userDataPtr -> do - uc <- mkEngineNC ucPtr + uc <- mkEngineNC ucPtr userData <- castPtrAndPeek userDataPtr memoryReadHook uc address (fromIntegral size) userData @@ -321,10 +343,11 @@ type MemoryWriteHook a = Engine -- ^ 'Unicorn' engine handle -> IO () marshalMemoryWriteHook :: Storable a - => MemoryWriteHook a -> IO {# type uc_cb_hookmem_t #} + => MemoryWriteHook a + -> IO {# type uc_cb_hookmem_t #} marshalMemoryWriteHook memoryWriteHook = mkMemoryHook $ \ucPtr _ address size value userDataPtr -> do - uc <- mkEngineNC ucPtr + uc <- mkEngineNC ucPtr userData <- castPtrAndPeek userDataPtr memoryWriteHook uc address (fromIntegral size) (fromIntegral value) userData @@ -351,15 +374,17 @@ type CMemoryEventHook = EnginePtr -> IO Int32 foreign import ccall "wrapper" - mkMemoryEventHook :: CMemoryEventHook -> IO {# type uc_cb_eventmem_t #} + mkMemoryEventHook :: CMemoryEventHook + -> IO {# type uc_cb_eventmem_t #} marshalMemoryEventHook :: Storable a - => MemoryEventHook a -> IO {# type uc_cb_eventmem_t #} + => MemoryEventHook a + -> IO {# type uc_cb_eventmem_t #} marshalMemoryEventHook eventMemoryHook = mkMemoryEventHook $ \ucPtr memAccessI address size value userDataPtr -> do - uc <- mkEngineNC ucPtr + uc <- mkEngineNC ucPtr userData <- castPtrAndPeek userDataPtr - let memAccess = toMemAccess memAccessI + let memAccess = toMemAccess memAccessI maybeValue = case memAccess of MemReadUnmapped -> Nothing MemReadProt -> Nothing @@ -369,7 +394,7 @@ marshalMemoryEventHook eventMemoryHook = res <- eventMemoryHook uc memAccess address (fromIntegral size) maybeValue userData return $ boolToInt res - where boolToInt True = 1 + where boolToInt True = 1 boolToInt False = 0 @@ -378,38 +403,43 @@ marshalMemoryEventHook eventMemoryHook = ------------------------------------------------------------------------------- {# fun variadic uc_hook_add as ucHookAdd - `(Storable a, HookTypeC h)' => - {`Engine', - alloca- `Hook' peek*, - enumToNum `h', - castFunPtrToPtr `FunPtr b', - castPtr `Ptr a', - `Word64', - `Word64'} - -> `Error' #} + `HookTypeC h' => + { `Engine' + , alloca- `Hook' peek* + , enumToNum `h' + , castFunPtrToPtr `FunPtr b' + , castPtr `Ptr a' + , `Word64' + , `Word64' + } -> `Error' +#} {# fun variadic uc_hook_add[int] as ucInsnHookAdd - `(Storable a, HookTypeC h)' => - {`Engine', - alloca- `Hook' peek*, - enumToNum `h', - castFunPtrToPtr `FunPtr b', - castPtr `Ptr a', - `Word64', - `Word64', - enumToNum `Instruction'} - -> `Error' #} + `HookTypeC h' => + { `Engine' + , alloca- `Hook' peek* + , enumToNum `h' + , castFunPtrToPtr `FunPtr b' + , castPtr `Ptr a' + , `Word64' + , `Word64' + , enumToNum `Instruction' + } -> `Error' +#} -- | Unregister (remove) a hook callback. {# fun uc_hook_del as ^ - {`Engine', - fromIntegral `Hook'} - -> `Error' #} + { `Engine' + , fromIntegral `Hook' + } -> `Error' +#} ------------------------------------------------------------------------------- -- Helper functions ------------------------------------------------------------------------------- -toMemAccess :: Integral a => a -> MemoryAccess +toMemAccess :: Integral a + => a + -> MemoryAccess toMemAccess = toEnum . fromIntegral diff --git a/bindings/haskell/src/Unicorn/Internal/Unicorn.chs b/bindings/haskell/src/Unicorn/Internal/Unicorn.chs index 53bf82f5..30605282 100644 --- a/bindings/haskell/src/Unicorn/Internal/Unicorn.chs +++ b/bindings/haskell/src/Unicorn/Internal/Unicorn.chs @@ -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 way cabal handles ordering of chs files. -} -module Unicorn.Internal.Unicorn ( - -- * Types - Architecture(..), - Mode(..), - MemoryPermission(..), - MemoryRegion(..), - QueryType(..), +module Unicorn.Internal.Unicorn + ( -- * Types + Architecture(..) + , Mode(..) + , MemoryPermission(..) + , MemoryRegion(..) + , QueryType(..) + , Context - -- * Function bindings - ucOpen, - ucQuery, - ucEmuStart, - ucEmuStop, - ucRegWrite, - ucRegRead, - ucMemWrite, - ucMemRead, - ucMemMap, - ucMemUnmap, - ucMemProtect, - ucMemRegions, - ucVersion, - ucErrno, - ucStrerror, -) where + -- * Function bindings + , ucOpen + , ucQuery + , ucEmuStart + , ucEmuStop + , ucRegWrite + , ucRegRead + , ucMemWrite + , ucMemRead + , ucMemMap + , ucMemUnmap + , ucMemProtect + , ucMemRegions + , mkContext + , ucContextAlloc + , ucContextSave + , ucContextRestore + , ucVersion + , ucErrno + , ucStrerror + ) where import Control.Applicative +import Control.Monad import Data.ByteString (ByteString, useAsCStringLen) import Foreign import Foreign.C @@ -46,11 +52,12 @@ import Prelude hiding (until) import Unicorn.Internal.Util -{# context lib="unicorn" #} - {# import Unicorn.Internal.Core #} +{# context lib = "unicorn" #} + #include +#include "unicorn_wrapper.h" ------------------------------------------------------------------------------- -- Types @@ -58,29 +65,33 @@ import Unicorn.Internal.Util -- | CPU architecture. {# enum uc_arch as Architecture - {underscoreToCase} - with prefix="UC_" - deriving (Show, Eq, Bounded) #} + { underscoreToCase } + with prefix = "UC_" + deriving (Show, Eq, Bounded) +#} -- | CPU hardware mode. {# enum uc_mode as Mode - {underscoreToCase} - with prefix="UC_" - deriving (Show, Eq, Bounded) #} + { underscoreToCase } + with prefix = "UC_" + deriving (Show, Eq, Bounded) +#} -- | Memory permissions. {# enum uc_prot as MemoryPermission - {underscoreToCase} - with prefix="UC_" - deriving (Show, Eq, Bounded) #} + { underscoreToCase } + with prefix = "UC_" + deriving (Show, Eq, Bounded) +#} -- | Memory region mapped by 'memMap'. Retrieve the list of memory regions with -- 'memRegions'. -data MemoryRegion = MemoryRegion { - mrBegin :: Word64, -- ^ Begin address of the region (inclusive) - mrEnd :: Word64, -- ^ End address of the region (inclusive) - mrPerms :: [MemoryPermission] -- ^ Memory permissions of the region -} +data MemoryRegion = MemoryRegion + { + mrBegin :: Word64 -- ^ Begin address of the region (inclusive) + , mrEnd :: Word64 -- ^ End address of the region (inclusive) + , mrPerms :: [MemoryPermission] -- ^ Memory permissions of the region + } instance Storable MemoryRegion where sizeOf _ = {# sizeof uc_mem_region #} @@ -99,121 +110,174 @@ instance Storable MemoryRegion where -- | Query types for the 'query' API. {# enum uc_query_type as QueryType - {underscoreToCase} - with prefix="UC_" - deriving (Show, Eq, Bounded) #} + { underscoreToCase } + with prefix = "UC_" + 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 ------------------------------------------------------------------------------- {# fun uc_open as ^ - {`Architecture', - combineEnums `[Mode]', - alloca- `EnginePtr' peek*} - -> `Error' #} + { `Architecture' + , combineEnums `[Mode]' + , alloca- `EnginePtr' peek* + } -> `Error' +#} {# fun uc_query as ^ - {`Engine', - `QueryType', - alloca- `Int' castPtrAndPeek*} - -> `Error' #} + { `Engine' + , `QueryType' + , alloca- `Int' castPtrAndPeek* + } -> `Error' +#} {# fun uc_emu_start as ^ - {`Engine', - `Word64', - `Word64', - `Int', - `Int'} - -> `Error' #} + { `Engine' + , `Word64' + , `Word64' + , `Int' + , `Int'} -> `Error' +#} {# fun uc_emu_stop as ^ - {`Engine'} - -> `Error' #} + { `Engine' + } -> `Error' +#} ------------------------------------------------------------------------------- -- Register operations ------------------------------------------------------------------------------- {# fun uc_reg_write as ^ - `Reg r' => - {`Engine', - enumToNum `r', - castPtr `Ptr Int64'} - -> `Error' #} + `Reg r' => + { `Engine' + , enumToNum `r' + , castPtr `Ptr Int64' + } -> `Error' +#} {# fun uc_reg_read as ^ - `Reg r' => - {`Engine', - enumToNum `r', - allocaInt64ToVoid- `Int64' castPtrAndPeek*} - -> `Error' #} + `Reg r' => + { `Engine' + , enumToNum `r' + , allocaInt64ToVoid- `Int64' castPtrAndPeek* + } -> `Error' +#} ------------------------------------------------------------------------------- -- Memory operations ------------------------------------------------------------------------------- {# fun uc_mem_write as ^ - {`Engine', - `Word64', - withByteStringLen* `ByteString'&} - -> `Error' #} + { `Engine' + , `Word64' + , withByteStringLen* `ByteString'& + } -> `Error' +#} {# fun uc_mem_read as ^ - {`Engine', - `Word64', - castPtr `Ptr Word8', - `Int'} - -> `Error' #} + { `Engine' + , `Word64' + , castPtr `Ptr Word8' + , `Int'} -> `Error' +#} {# fun uc_mem_map as ^ - {`Engine', - `Word64', - `Int', - combineEnums `[MemoryPermission]'} - -> `Error' #} + { `Engine' + , `Word64' + , `Int' + , combineEnums `[MemoryPermission]' + } -> `Error' #} {# fun uc_mem_unmap as ^ - {`Engine', - `Word64', - `Int'} - -> `Error' #} + { `Engine' + , `Word64' + , `Int' + } -> `Error' +#} {# fun uc_mem_protect as ^ - {`Engine', - `Word64', - `Int', - combineEnums `[MemoryPermission]'} - -> `Error' #} + { `Engine' + , `Word64' + , `Int' + , combineEnums `[MemoryPermission]' + } -> `Error' +#} {# fun uc_mem_regions as ^ - {`Engine', - alloca- `MemoryRegionPtr' peek*, - alloca- `Int' castPtrAndPeek*} - -> `Error' #} + { `Engine' + , alloca- `MemoryRegionPtr' peek* + , alloca- `Int' castPtrAndPeek* + } -> `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. ------------------------------------------------------------------------------- {# fun pure unsafe uc_version as ^ - {id `Ptr CUInt', - id `Ptr CUInt'} - -> `Int' #} + { id `Ptr CUInt' + , id `Ptr CUInt' + } -> `Int' +#} {# fun unsafe uc_errno as ^ - {`Engine'} - -> `Error' #} + { `Engine' + } -> `Error' +#} {# fun pure unsafe uc_strerror as ^ - {`Error'} - -> `String' #} + { `Error' + } -> `String' +#} ------------------------------------------------------------------------------- -- Helper functions ------------------------------------------------------------------------------- -expandMemPerms :: (Integral a, Bits a) => a -> [MemoryPermission] +expandMemPerms :: (Integral a, Bits a) + => a + -> [MemoryPermission] expandMemPerms perms = -- Only interested in the 3 least-significant bits let maskedPerms = fromIntegral $ perms .&. 0x7 in @@ -232,10 +296,13 @@ expandMemPerms perms = checkRWE _ [] = [] -allocaInt64ToVoid :: (Ptr () -> IO b) -> IO b +allocaInt64ToVoid :: (Ptr () -> IO b) + -> IO b allocaInt64ToVoid f = alloca $ \(ptr :: Ptr Int64) -> poke ptr 0 >> f (castPtr ptr) -withByteStringLen :: ByteString -> ((Ptr (), CULong) -> IO a) -> IO a +withByteStringLen :: ByteString + -> ((Ptr (), CULong) -> IO a) + -> IO a withByteStringLen bs f = useAsCStringLen bs $ \(ptr, len) -> f (castPtr ptr, fromIntegral len) diff --git a/bindings/haskell/src/Unicorn/Internal/Util.hs b/bindings/haskell/src/Unicorn/Internal/Util.hs index 3af6a513..edaf3430 100644 --- a/bindings/haskell/src/Unicorn/Internal/Util.hs +++ b/bindings/haskell/src/Unicorn/Internal/Util.hs @@ -10,16 +10,22 @@ import Data.Bits import Foreign -- | Combine a list of Enums by performing a bitwise-OR. -combineEnums :: (Enum a, Num b, Bits b) => [a] -> b +combineEnums :: (Enum a, Num b, Bits b) + => [a] + -> b combineEnums = foldr ((.|.) <$> enumToNum) 0 -- | Cast a pointer and then peek inside it. -castPtrAndPeek :: Storable a => Ptr b -> IO a +castPtrAndPeek :: Storable a + => Ptr b + -> IO a castPtrAndPeek = peek . castPtr -- | Convert an 'Eum' to a 'Num'. -enumToNum :: (Enum a, Num b) => a -> b +enumToNum :: (Enum a, Num b) + => a + -> b enumToNum = fromIntegral . fromEnum diff --git a/bindings/haskell/src/cbits/unicorn_wrapper.c b/bindings/haskell/src/cbits/unicorn_wrapper.c index 1ee60706..fdbbe6d8 100644 --- a/bindings/haskell/src/cbits/unicorn_wrapper.c +++ b/bindings/haskell/src/cbits/unicorn_wrapper.c @@ -6,3 +6,7 @@ void uc_close_wrapper(uc_engine *uc) { void uc_close_dummy(uc_engine *uc) { } + +void uc_context_free_wrapper(uc_context *context) { + uc_context_free(context); +} diff --git a/bindings/haskell/src/include/unicorn_wrapper.h b/bindings/haskell/src/include/unicorn_wrapper.h index 76d414aa..e717c408 100644 --- a/bindings/haskell/src/include/unicorn_wrapper.h +++ b/bindings/haskell/src/include/unicorn_wrapper.h @@ -13,4 +13,9 @@ void uc_close_wrapper(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 diff --git a/bindings/haskell/unicorn.cabal b/bindings/haskell/unicorn.cabal index 027e09d2..d050c538 100644 --- a/bindings/haskell/unicorn.cabal +++ b/bindings/haskell/unicorn.cabal @@ -13,8 +13,9 @@ copyright: (c) 2016, Adrian Herrera category: System build-type: Simple stability: experimental -cabal-version: >=1.10 -extra-source-files: cbits/, include/ +cabal-version: >= 1.10 +extra-source-files: cbits/ + , include/ library exposed-modules: Unicorn.Internal.Core @@ -29,10 +30,10 @@ library Unicorn.Hook Unicorn other-modules: Unicorn.Internal.Util - build-depends: base >=4 && <5, - bytestring >= 0.9.1, - transformers < 0.6, - either >= 4.4 + build-depends: base >=4 && <5 + , bytestring >= 0.9.1 + , transformers < 0.6 + , either >= 4.4 hs-source-dirs: src c-sources: src/cbits/unicorn_wrapper.c include-dirs: src/include diff --git a/bindings/python/sample_arm.py b/bindings/python/sample_arm.py index e910defc..13292320 100755 --- a/bindings/python/sample_arm.py +++ b/bindings/python/sample_arm.py @@ -21,7 +21,7 @@ def hook_block(uc, address, size, user_data): # callback for tracing instructions 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 @@ -46,7 +46,7 @@ def test_arm(): mu.hook_add(UC_HOOK_BLOCK, hook_block) # tracing all instructions with customized callback - mu.hook_add(UC_HOOK_CODE, hook_code) + mu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=ADDRESS) # emulate machine code in infinite time mu.emu_start(ADDRESS, ADDRESS + len(ARM_CODE)) @@ -100,5 +100,5 @@ def test_thumb(): if __name__ == '__main__': test_arm() - print("=" * 20) + print("=" * 26) test_thumb() diff --git a/bindings/python/sample_arm64.py b/bindings/python/sample_arm64.py index 2afee8c4..0270a401 100755 --- a/bindings/python/sample_arm64.py +++ b/bindings/python/sample_arm64.py @@ -21,7 +21,7 @@ def hook_block(uc, address, size, user_data): # callback for tracing instructions 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 diff --git a/bindings/python/sample_m68k.py b/bindings/python/sample_m68k.py index d9579f66..7e35cf59 100755 --- a/bindings/python/sample_m68k.py +++ b/bindings/python/sample_m68k.py @@ -20,7 +20,7 @@ def hook_block(uc, address, size, user_data): # callback for tracing instructions 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 @@ -51,8 +51,34 @@ def test_m68k(): # now print out some registers 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) - 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: print("ERROR: %s" % e) diff --git a/bindings/python/sample_mips.py b/bindings/python/sample_mips.py index 9b9f9e8f..7199b631 100755 --- a/bindings/python/sample_mips.py +++ b/bindings/python/sample_mips.py @@ -22,7 +22,7 @@ def hook_block(uc, address, size, user_data): # callback for tracing instructions 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 @@ -54,7 +54,7 @@ def test_mips_eb(): print(">>> Emulation done. Below is the CPU context") r1 = mu.reg_read(UC_MIPS_REG_1) - print(">>> r1 = 0x%x" %r1) + print(">>> R1 = 0x%x" %r1) except UcError as e: print("ERROR: %s" % e) @@ -89,7 +89,7 @@ def test_mips_el(): print(">>> Emulation done. Below is the CPU context") r1 = mu.reg_read(UC_MIPS_REG_1) - print(">>> r1 = 0x%x" %r1) + print(">>> R1 = 0x%x" %r1) except UcError as e: print("ERROR: %s" % e) @@ -97,5 +97,5 @@ def test_mips_el(): if __name__ == '__main__': test_mips_eb() - print("=" * 20) + print("=" * 27) test_mips_el() diff --git a/bindings/python/sample_sparc.py b/bindings/python/sample_sparc.py index f6f154d6..5dbe746e 100755 --- a/bindings/python/sample_sparc.py +++ b/bindings/python/sample_sparc.py @@ -20,7 +20,7 @@ def hook_block(uc, address, size, user_data): # callback for tracing instructions 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 diff --git a/bindings/python/sample_x86.py b/bindings/python/sample_x86.py index 81027b2a..981a6d5e 100755 --- a/bindings/python/sample_x86.py +++ b/bindings/python/sample_x86.py @@ -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_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_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" @@ -26,9 +28,14 @@ def hook_block(uc, address, size, user_data): # callback for tracing instructions def hook_code(uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size)) - #eip = uc.reg_read(UC_X86_REG_EIP) - #print(">>> EIP = 0x%x" %(eip)) + print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size)) + eip = uc.reg_read(UC_X86_REG_EFLAGS) + 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) @@ -128,21 +135,21 @@ def test_i386(): r_xmm0 = mu.reg_read(UC_X86_REG_XMM0) print(">>> ECX = 0x%x" %r_ecx) print(">>> EDX = 0x%x" %r_edx) - print(">>> XMM0 = 0x%x" %r_xmm0) + print(">>> XMM0 = 0x%.32x" %r_xmm0) # read from memory - tmp = mu.mem_read(ADDRESS, 2) - print(">>> Read 2 bytes from [0x%x] =" %(ADDRESS), end="") - for i in tmp: - print(" 0x%x" %i, end="") + 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: print("ERROR: %s" % e) -def test_i386_loop(): - print("Emulate i386 code with infinite loop - wait for 2 seconds then stop emulation") +def test_i386_map_ptr(): + print("Emulate i386 code - use uc_mem_map_ptr()") try: # Initialize emulator in X86-32bit mode mu = Uc(UC_ARCH_X86, UC_MODE_32) @@ -151,14 +158,20 @@ def test_i386_loop(): mu.mem_map(ADDRESS, 2 * 1024 * 1024) # write machine code to be emulated to memory - mu.mem_write(ADDRESS, X86_CODE32_LOOP) + mu.mem_write(ADDRESS, X86_CODE32) # initialize machine registers mu.reg_write(UC_X86_REG_ECX, 0x1234) mu.reg_write(UC_X86_REG_EDX, 0x7890) + # tracing all basic blocks with customized callback + mu.hook_add(UC_HOOK_BLOCK, hook_block) + + # tracing all instructions with customized callback + mu.hook_add(UC_HOOK_CODE, hook_code) + # 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 print(">>> Emulation done. Below is the CPU context") @@ -168,6 +181,13 @@ def test_i386_loop(): print(">>> ECX = 0x%x" %r_ecx) 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: print("ERROR: %s" % e) @@ -198,7 +218,7 @@ def test_i386_invalid_mem_read(): # emulate machine code in infinite time mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_MEM_READ)) 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 print(">>> Emulation done. Below is the CPU context") @@ -211,6 +231,35 @@ def test_i386_invalid_mem_read(): except UcError as 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(): 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) # 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 - #mu.hook_add(UC_HOOK_CODE, hook_code) + mu.hook_add(UC_HOOK_CODE, hook_code) # intercept invalid memory events mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, hook_mem_invalid) @@ -251,25 +300,92 @@ def test_i386_invalid_mem_write(): print(">>> ECX = 0x%x" %r_ecx) 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: - # read from memory - print(">>> Read 4 bytes from [0x%x] = " %(0xaaaaaaaa), end="") - tmp = mu.mem_read(0xaaaaaaaa, 4) - for i in tmp: - print(" 0x%x" %i, end="") + tmp = mu.mem_read(0xffffffaa, 4) + print(">>> Read 4 bytes from [0x%x] = 0x" %(0xffffffaa), end="") + for i in reversed(tmp): + print("%x" %i, end="") 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: - print("ERROR: %s" % e) + print(">>> Failed to read 4 bytes from [0xffffffaa]") except UcError as 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 def test_i386_inout(): @@ -397,7 +513,7 @@ def test_x86_64(): mu.hook_add(UC_HOOK_BLOCK, hook_block) # 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 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) r15 = mu.reg_read(UC_X86_REG_R15) - print(">>> RAX = %x" %rax) - print(">>> RBX = %x" %rbx) - print(">>> RCX = %x" %rcx) - print(">>> RDX = %x" %rdx) - print(">>> RSI = %x" %rsi) - print(">>> RDI = %x" %rdi) - print(">>> R8 = %x" %r8) - print(">>> R9 = %x" %r9) - print(">>> R10 = %x" %r10) - print(">>> R11 = %x" %r11) - print(">>> R12 = %x" %r12) - print(">>> R13 = %x" %r13) - print(">>> R14 = %x" %r14) - print(">>> R15 = %x" %r15) + print(">>> RAX = 0x%x" %rax) + print(">>> RBX = 0x%x" %rbx) + print(">>> RCX = 0x%x" %rcx) + print(">>> RDX = 0x%x" %rdx) + print(">>> RSI = 0x%x" %rsi) + print(">>> RDI = 0x%x" %rdi) + print(">>> R8 = 0x%x" %r8) + print(">>> R9 = 0x%x" %r9) + print(">>> R10 = 0x%x" %r10) + print(">>> R11 = 0x%x" %r11) + print(">>> R12 = 0x%x" %r12) + print(">>> R13 = 0x%x" %r13) + print(">>> R14 = 0x%x" %r14) + print(">>> R15 = 0x%x" %r15) - #BUG - mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE64)) except UcError as e: print("ERROR: %s" % e) @@ -516,27 +630,29 @@ def test_x86_16(): print(">>> Emulation done. Below is the CPU context") 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: print("ERROR: %s" % e) 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_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() diff --git a/bindings/python/setup.py b/bindings/python/setup.py index 155b524a..aa7acb5a 100755 --- a/bindings/python/setup.py +++ b/bindings/python/setup.py @@ -23,7 +23,7 @@ if os.path.exists(PATH_LIB64) and os.path.exists(PATH_LIB32): PKG_NAME = 'unicorn-windows' SYSTEM = sys.platform -VERSION = '1.0' +VERSION = '1.0.0' # adapted from commit e504b81 of Nguyen Tan Cong # Reference: https://docs.python.org/2/library/platform.html#cross-platform diff --git a/bindings/python/unicorn/unicorn.py b/bindings/python/unicorn/unicorn.py index 1d3f584f..1bad25fa 100644 --- a/bindings/python/unicorn/unicorn.py +++ b/bindings/python/unicorn/unicorn.py @@ -63,7 +63,8 @@ _path_list = [pkg_resources.resource_filename(__name__, 'lib'), os.path.join(os.path.split(__file__)[0], '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: _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_save", 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.uc_hook_add = _uc.uc_hook_add diff --git a/bindings/ruby/unicorn_gem/ext/unicorn.c b/bindings/ruby/unicorn_gem/ext/unicorn.c index c7979343..bd23086c 100644 --- a/bindings/ruby/unicorn_gem/ext/unicorn.c +++ b/bindings/ruby/unicorn_gem/ext/unicorn.c @@ -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); rb_scan_args(argc, argv, "22",&begin, &until, &timeout, &count); - if (NIL_P(timeout)) - timeout = INT2NUM(0); + if (NIL_P(timeout)) + timeout = INT2NUM(0); - if (NIL_P(count)) - count = INT2NUM(0); + if (NIL_P(count)) + count = INT2NUM(0); err = uc_emu_start(_uc, NUM2ULL(begin), NUM2ULL(until), NUM2INT(timeout), NUM2INT(count)); if (err != UC_ERR_OK) { @@ -133,7 +133,7 @@ VALUE m_uc_reg_read(VALUE self, VALUE reg_id){ if (err != UC_ERR_OK) { 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); break; } - + if (err != UC_ERR_OK) { 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; Data_Get_Struct(rb_iv_get(self,"@uch"), uc_engine, _uc); rb_scan_args(argc, argv, "21",&address, &size, &perms); - if (NIL_P(perms)) - perms = INT2NUM(UC_PROT_ALL); + if (NIL_P(perms)) + perms = INT2NUM(UC_PROT_ALL); err = uc_mem_map(_uc, NUM2ULL(address), NUM2UINT(size), NUM2UINT(perms)); if (err != UC_ERR_OK) { @@ -332,14 +332,14 @@ VALUE m_uc_hook_add(int argc, VALUE* argv, VALUE self){ uc_engine *_uc; Data_Get_Struct(rb_iv_get(self,"@uch"), uc_engine, _uc); rb_scan_args(argc, argv, "24",&hook_type, &callback, &user_data, &begin, &end, &arg1); - if (NIL_P(begin)) - begin = ULL2NUM(1); + if (NIL_P(begin)) + begin = ULL2NUM(1); - if (NIL_P(end)) - end = ULL2NUM(0); + if (NIL_P(end)) + end = ULL2NUM(0); - if (NIL_P(arg1)) - arg1 = INT2NUM(0); + if (NIL_P(arg1)) + arg1 = INT2NUM(0); VALUE passthrough; 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){ err = uc_hook_add(_uc, &trace, htype, cb_hook_code,(void *)passthrough, NUM2ULL(begin), NUM2ULL(end)); } - else if (htype & UC_HOOK_MEM_READ_UNMAPPED - || htype & UC_HOOK_MEM_WRITE_UNMAPPED - || htype & UC_HOOK_MEM_FETCH_UNMAPPED - || htype & UC_HOOK_MEM_READ_PROT - || htype & UC_HOOK_MEM_WRITE_PROT - || htype & UC_HOOK_MEM_FETCH_PROT + else if (htype & UC_HOOK_MEM_READ_UNMAPPED + || htype & UC_HOOK_MEM_WRITE_UNMAPPED + || htype & UC_HOOK_MEM_FETCH_UNMAPPED + || htype & UC_HOOK_MEM_READ_PROT + || htype & UC_HOOK_MEM_WRITE_PROT + || htype & UC_HOOK_MEM_FETCH_PROT || htype & UC_HOOK_MEM_READ_INVALID || htype & UC_HOOK_MEM_WRITE_INVALID || htype & UC_HOOK_MEM_FETCH_INVALID @@ -387,7 +387,7 @@ VALUE m_uc_hook_add(int argc, VALUE* argv, VALUE self){ || htype & UC_HOOK_MEM_PROT || htype & UC_HOOK_MEM_INVALID) { err = uc_hook_add(_uc, &trace, htype, cb_hook_mem_invalid,(void *)passthrough, NUM2ULL(begin), NUM2ULL(end)); - } + } else{ err = uc_hook_add(_uc, &trace, htype, cb_hook_mem_access,(void *)passthrough, NUM2ULL(begin), NUM2ULL(end)); } diff --git a/make.sh b/make.sh index a41a7c8a..048df624 100755 --- a/make.sh +++ b/make.sh @@ -36,17 +36,6 @@ build_iOS() { ${MAKE} } -build() { - [ "$UNAME" = Darwin ] && LIBARCHS="i386 x86_64" - ${MAKE} -} - -build_macos_universal() { - [ "$UNAME" = Darwin ] && LIBARCHS="i386 x86_64" - MACOS_UNIVERSAL=yes \ - ${MAKE} -} - build_cross() { [ "$UNAME" = Darwin ] && LIBARCHS="i386 x86_64" CROSS=$1 @@ -112,17 +101,14 @@ fi export CC INSTALL_BIN PREFIX PKGCFGDIR LIBDIRARCH LIBARCHS CFLAGS LDFLAGS case "$1" in - "" ) build;; - "macos-universal" ) build_macos_universal;; + "" ) ${MAKE};; "asan" ) asan;; - "default" ) build;; "install" ) install;; "uninstall" ) uninstall;; + "macos-universal" ) MACOS_UNIVERSAL=yes ${MAKE};; "cross-win32" ) build_cross i686-w64-mingw32;; "cross-win64" ) build_cross x86_64-w64-mingw32;; - "cross-android" ) CROSS=arm-linux-androideabi build;; - "clang" ) CC=clang build;; - "gcc" ) CC=gcc build;; + "cross-android" ) CROSS=arm-linux-androideabi ${MAKE};; "ios" ) build_iOS;; "ios_armv7" ) build_iOS armv7;; "ios_armv7s" ) build_iOS armv7s;; diff --git a/samples/Makefile b/samples/Makefile index e32afc93..830cd68b 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -3,7 +3,6 @@ include ../config.mk -LIBNAME = unicorn UNAME_S := $(shell uname -s) # Find GLIB @@ -11,70 +10,34 @@ ifndef GLIB GLIB = $(shell pkg-config --libs glib-2.0) endif -UNICORN_DEP_LIBS_STATIC += -lpthread -lm $(GLIB) - # Verbose output? V ?= 0 -INCDIR = ../include -SAMPLEDIR = . -OBJDIR = . -LIBDIR = .. +CFLAGS += -Wall -Werror -I../include +LDFLAGS += -L.. +LDLIBS += -lpthread -lunicorn -lm $(GLIB) -CFLAGS += -Wall -Werror -I$(INCDIR) -LDFLAGS += -lpthread -L$(LIBDIR) -l$(LIBNAME) -LDFLAGS_STATIC += $(UNICORN_DEP_LIBS_STATIC) - -ifeq ($(CROSS),) -CC ?= cc -LDFLAGS += -lm $(GLIB) -else +ifneq ($(CROSS),) CC = $(CROSS)gcc endif ifeq ($(UNICORN_ASAN),yes) -CC = clang -fsanitize=address -fno-omit-frame-pointer -CXX = clang++ -fsanitize=address -fno-omit-frame-pointer +CC = clang +CXX = clang++ AR = llvm-ar -LDFLAGS := -fsanitize=address ${LDFLAGS} +CFLAGS += -fsanitize=address -fno-omit-frame-pointer endif - -#CFLAGS += $(foreach arch,$(LIBARCHS),-arch $(arch)) -#LDFLAGS += $(foreach arch,$(LIBARCHS),-arch $(arch)) - -BIN_EXT = -AR_EXT = a - # Cygwin? ifneq ($(filter CYGWIN%,$(UNAME_S)),) CFLAGS := $(CFLAGS:-fPIC=) -LDFLAGS += -lssp -LDFLAGS_STATIC += -lssp -BIN_EXT = .exe -AR_EXT = a +LDLIBS += -lssp # mingw? else ifneq ($(filter MINGW%,$(UNAME_S)),) CFLAGS := $(CFLAGS:-fPIC=) -BIN_EXT = .exe -AR_EXT = lib endif - -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 +.PHONY: all clean UNICORN_ARCHS := $(shell if [ -e ../config.log ]; then cat ../config.log;\ else printf "$(UNICORN_ARCHS)"; fi) @@ -89,9 +52,9 @@ endif ifneq (,$(findstring mips,$(UNICORN_ARCHS))) SOURCES += sample_mips.c endif -ifneq (,$(findstring ppc,$(UNICORN_ARCHS))) +#ifneq (,$(findstring ppc,$(UNICORN_ARCHS))) #SOURCES += sample_ppc.c -endif +#endif ifneq (,$(findstring sparc,$(UNICORN_ARCHS))) SOURCES += sample_sparc.c endif @@ -106,73 +69,9 @@ ifneq (,$(findstring m68k,$(UNICORN_ARCHS))) SOURCES += sample_m68k.c endif -OBJS = $(addprefix $(OBJDIR)/,$(SOURCES:.c=.o)) -OBJS_ELF = $(addprefix $(OBJDIR)/,$(SOURCES:.c=)) -BINARY = $(addprefix $(SAMPLEDIR)/,$(SOURCES:.c=$(BIN_EXT))) +BINS = $(SOURCES:.c=) -all: $(BINARY) +all: $(BINS) -clean_bins: - rm -rf *.o $(OBJS_ELF) $(BINARY) $(SAMPLEDIR)/*.exe $(SAMPLEDIR)/*.static $(OBJDIR)/lib$(LIBNAME)* $(OBJDIR)/$(LIBNAME)* - 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 +clean: + rm -rf *.o $(BINS) diff --git a/samples/sample_x86.c b/samples/sample_x86.c index 2e1d73ec..4c5da3e3 100644 --- a/samples/sample_x86.c +++ b/samples/sample_x86.c @@ -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_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" @@ -668,6 +669,105 @@ static void test_i386_inout(void) 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) { uc_engine *uc; @@ -906,6 +1006,7 @@ int main(int argc, char **argv, char **envp) test_i386(); test_i386_map_ptr(); test_i386_inout(); + test_i386_context_save(); test_i386_jump(); test_i386_loop(); test_i386_invalid_mem_read(); diff --git a/samples/sample_x86_32_gdt_and_seg_regs.c b/samples/sample_x86_32_gdt_and_seg_regs.c index c3531619..7e1e260b 100644 --- a/samples/sample_x86_32_gdt_and_seg_regs.c +++ b/samples/sample_x86_32_gdt_and_seg_regs.c @@ -269,7 +269,7 @@ static void gdt_demo() int i; for (i = 0; i < 8; i++) { - fprintf(stderr, "%02hhx", buf[i]); + fprintf(stderr, "%02x", buf[i]); } fprintf(stderr, "\n"); diff --git a/tests/unit/Makefile b/tests/unit/Makefile index ebf8eb30..0768210a 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -1,11 +1,9 @@ - CFLAGS += -Wall -Werror -Wno-unused-function -g -CFLAGS += -L ../../ -CFLAGS += -I ../../include +CFLAGS += -L ../../ -I ../../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) CC = clang -fsanitize=address -fno-omit-frame-pointer @@ -14,9 +12,8 @@ AR = llvm-ar LDFLAGS := -fsanitize=address ${LDFLAGS} endif -ALL_TESTS = test_sanity test_x86 test_mem_map test_mem_high test_mem_map_ptr \ - test_tb_x86 test_multihook test_pc_change test_x86_soft_paging \ - test_hookcounts test_hang test_x86_shl_enter_leave test_x86_rip_bug +ALL_TESTS_SOURCES = $(wildcard *.c) +ALL_TESTS = $(ALL_TESTS_SOURCES:%.c=%) .PHONY: all all: ${ALL_TESTS} @@ -32,28 +29,11 @@ test: ${ALL_TESTS} ${EXECUTE_VARS} ./test_mem_map ${EXECUTE_VARS} ./test_mem_map_ptr ${EXECUTE_VARS} ./test_mem_high - echo "skipping test_tb_x86" #${EXECUTE_VARS} ./test_tb_x86 ${EXECUTE_VARS} ./test_multihook ${EXECUTE_VARS} ./test_pc_change - echo "skipping test_x86_soft_paging" #${EXECUTE_VARS} ./test_x86_soft_paging ${EXECUTE_VARS} ./test_hookcounts - echo "skipping test_hang" #${EXECUTE_VARS} ./test_hang - echo "skipping test_x86_sh1_enter_leave" #${EXECUTE_VARS} ./test_x86_shl_enter_leave - echo "skipping test_x86_rip_bug" #${EXECUTE_VARS} ./test_x86_rip_bug - -test_sanity: test_sanity.c -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} + echo "skipping test_tb_x86" + echo "skipping test_x86_soft_paging" + echo "skipping test_hang" + echo "skipping test_x86_sh1_enter_leave" + echo "skipping test_x86_rip_bug" diff --git a/uc.c b/uc.c index 23f672da..10ebdb6a 100644 --- a/uc.c +++ b/uc.c @@ -1,9 +1,6 @@ /* Unicorn Emulator Engine */ /* By Nguyen Anh Quynh , 2015 */ -#if defined (WIN32) || defined (WIN64) || defined (_WIN32) || defined (_WIN64) -#pragma warning(disable:4996) -#endif #if defined(UNICORN_HAS_OSXKERNEL) #include #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 // of the interesting CPU registers switch (arch) { +#ifdef UNICORN_HAS_M68K case UC_ARCH_M68K: return M68K_REGS_STORAGE_SIZE; +#endif +#ifdef UNICORN_HAS_X86 case UC_ARCH_X86: return X86_REGS_STORAGE_SIZE; +#endif +#ifdef UNICORN_HAS_ARM case UC_ARCH_ARM: return ARM_REGS_STORAGE_SIZE; +#endif +#ifdef UNICORN_HAS_ARM64 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; +#endif +#ifdef UNICORN_HAS_SPARC case UC_ARCH_SPARC: return mode & UC_MODE_SPARC64 ? SPARC64_REGS_STORAGE_SIZE : SPARC_REGS_STORAGE_SIZE; +#endif default: return 0; } }