diff --git a/.gitignore b/.gitignore index 38d5bb7e..ed7c3e25 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.d *.o *.a +*.dSYM qemu/config-all-devices.mak @@ -93,12 +94,12 @@ tmp/ bindings/python/build/ config.log -regress/map_crash -regress/sigill -regress/sigill2 -regress/block_test -regress/map_write -regress/ro_mem_test -regress/nr_mem_test -regress/timeout_segfault -regress/rep_movsb +map_crash +sigill +sigill2 +block_test +map_write +ro_mem_test +nr_mem_test +timeout_segfault +rep_movsb diff --git a/COMPILE.TXT b/COMPILE.TXT index 53d6885f..9001cbfd 100644 --- a/COMPILE.TXT +++ b/COMPILE.TXT @@ -35,11 +35,11 @@ Unicorn requires few dependent packages as followings [1] Tailor Unicorn to your need. - Out of 8 archtitectures supported by Unicorn (Arm, Arm64, Mips, PPC, Sparc, - SystemZ, XCore & X86), if you just need several selected archs, choose which - ones you want to compile in by editing "config.mk" before going to next steps. + Out of 6 archtitectures supported by Unicorn (Arm, Arm64, M68K, Mips, Sparc, + & X86), if you just need several selected archs, choose which ones you want + to compile in by editing "config.mk" before going to next steps. - By default, all 8 architectures are compiled. + By default, all 6 architectures are compiled. The other way of customize Unicorn without having to edit config.mk is to pass the desired options on the commandline to ./make.sh. Currently, @@ -246,3 +246,12 @@ Unicorn requires few dependent packages as followings So far, only Python is supported by bindings in the main code. Look for the bindings under directory bindings/, and refer to README file of corresponding languages. + + +[11] Unit tests + + Automated unit tests use the cmocka unit testing framework (https://cmocka.org/). + It can be installed in most Linux distros using the package manager, e.g. + `sudo yum install libcmocka libcmocka-devel`, or you can easily build and install it from source. + + You can run the tests by running `make test` in the project directory. diff --git a/Makefile b/Makefile index 148baa73..06be3fae 100644 --- a/Makefile +++ b/Makefile @@ -249,6 +249,11 @@ else endif +.PHONY: test +test: all + $(MAKE) -C tests/unit test + + install: all $(PKGCFGF) mkdir -p $(LIBDIR) ifeq ($(UNICORN_SHARED),yes) @@ -302,6 +307,7 @@ ifeq (,$(findstring yes,$(UNICORN_BUILD_CORE_ONLY))) cd samples && $(MAKE) clean rm -f $(BLDIR)/samples/lib$(LIBNAME).$(EXT) endif + $(MAKE) -C tests/unit clean ifdef BUILDDIR rm -rf $(BUILDDIR) diff --git a/README b/README deleted file mode 100644 index a2eb9d70..00000000 --- a/README +++ /dev/null @@ -1,24 +0,0 @@ -Unicorn is a lightweight multi-platform, multi-architecture CPU emulator framework. - -Unicorn offers some unparalleled features: - -- Multi-architectures: Arm, Arm64 (Armv8), M68K, Mips, Sparc, & X86 (include X86_64). -- Clean/simple/lightweight/intuitive architecture-neutral API. -- Implemented in pure C language, with bindings for Python available. -- Native support for Windows & *nix (with Mac OSX, Linux, *BSD & Solaris confirmed). -- High performace by using Just-In-Time compiler technique. -- Support fine-grained instrumentation at various levels. -- Thread-safe by design. -- Distributed under open source license GPL. - -Further information is available at http://www.unicorn-engine.org - - -[Compile] - -See COMPILE.TXT file for how to compile and install Unicorn. - - -[License] - -This project is released under the GPL license. diff --git a/README.md b/README.md new file mode 100644 index 00000000..fd7a5639 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +Unicorn Engine +============== + +Unicorn is a lightweight, multi-platform, multi-architecture CPU emulator framework +based on [QEMU](http://qemu.org). + +Unicorn offers some unparalleled features: + +- Multi-architecture: ARM, AMM64 (ARMv8), M68K, MIPS, SPARC, and X86 (16, 32, 64-bit) +- Clean/simple/lightweight/intuitive architecture-neutral API +- Implemented in pure C language, with bindings for Python, Java, and Go +- Native support for Windows & *nix (with Mac OSX, Linux, *BSD & Solaris confirmed) +- High performace via Just-In-Time compilation +- Support for fine-grained instrumentation at various levels +- Thread-safety by design +- Distributed under open source license GPL + +Further information is available at http://www.unicorn-engine.org + + +Compilation +----------- + +See [COMPILE.TXT](COMPILE.TXT) file for how to compile and install Unicorn. + + +License +------- + +This project is released under the GPL license. diff --git a/bindings/Makefile b/bindings/Makefile index 5fa8e472..d004735a 100644 --- a/bindings/Makefile +++ b/bindings/Makefile @@ -16,6 +16,7 @@ SAMPLE_X86 = $(TMPDIR)/sample_x86 all: cd python && $(MAKE) gen_const cd go && $(MAKE) gen_const + cd java && $(MAKE) gen_const samples: expected python diff --git a/bindings/go/unicorn/hook.go b/bindings/go/unicorn/hook.go index bfa72c80..c286f08f 100644 --- a/bindings/go/unicorn/hook.go +++ b/bindings/go/unicorn/hook.go @@ -71,10 +71,7 @@ func (u *uc) HookAdd(htype int, cb interface{}, extra ...uint64) (Hook, error) { case HOOK_BLOCK, HOOK_CODE: rangeMode = true callback = C.hookCode_cgo - case HOOK_MEM_INVALID: - rangeMode = true - callback = C.hookMemInvalid_cgo - case HOOK_MEM_READ, HOOK_MEM_WRITE, HOOK_MEM_READ_WRITE: + case HOOK_MEM_READ, HOOK_MEM_WRITE, HOOK_MEM_READ | HOOK_MEM_WRITE: rangeMode = true callback = C.hookMemAccess_cgo case HOOK_INTR: @@ -92,7 +89,14 @@ func (u *uc) HookAdd(htype int, cb interface{}, extra ...uint64) (Hook, error) { return 0, errors.New("Unknown instruction type.") } default: - return 0, errors.New("Unknown hook type.") + // special case for mask + if htype&(HOOK_MEM_READ_INVALID|HOOK_MEM_WRITE_INVALID|HOOK_MEM_FETCH_INVALID| + HOOK_MEM_READ_PROT|HOOK_MEM_WRITE_PROT|HOOK_MEM_FETCH_PROT) != 0 { + rangeMode = true + callback = C.hookMemInvalid_cgo + } else { + return 0, errors.New("Unknown hook type.") + } } var h2 C.uc_hook data := &HookData{u, cb} diff --git a/bindings/go/unicorn/sparc_const.go b/bindings/go/unicorn/sparc_const.go index 51e209f2..afd94f45 100644 --- a/bindings/go/unicorn/sparc_const.go +++ b/bindings/go/unicorn/sparc_const.go @@ -57,21 +57,21 @@ const ( SPARC_REG_FCC1 = 50 SPARC_REG_FCC2 = 51 SPARC_REG_FCC3 = 52 - SPARC_REG_FP = 53 - SPARC_REG_G0 = 54 - SPARC_REG_G1 = 55 - SPARC_REG_G2 = 56 - SPARC_REG_G3 = 57 - SPARC_REG_G4 = 58 - SPARC_REG_G5 = 59 - SPARC_REG_G6 = 60 - SPARC_REG_G7 = 61 - SPARC_REG_I0 = 62 - SPARC_REG_I1 = 63 - SPARC_REG_I2 = 64 - SPARC_REG_I3 = 65 - SPARC_REG_I4 = 66 - SPARC_REG_I5 = 67 + SPARC_REG_G0 = 53 + SPARC_REG_G1 = 54 + SPARC_REG_G2 = 55 + SPARC_REG_G3 = 56 + SPARC_REG_G4 = 57 + SPARC_REG_G5 = 58 + SPARC_REG_G6 = 59 + SPARC_REG_G7 = 60 + SPARC_REG_I0 = 61 + SPARC_REG_I1 = 62 + SPARC_REG_I2 = 63 + SPARC_REG_I3 = 64 + SPARC_REG_I4 = 65 + SPARC_REG_I5 = 66 + SPARC_REG_FP = 67 SPARC_REG_I7 = 68 SPARC_REG_ICC = 69 SPARC_REG_L0 = 70 @@ -88,12 +88,12 @@ const ( SPARC_REG_O3 = 81 SPARC_REG_O4 = 82 SPARC_REG_O5 = 83 - SPARC_REG_O7 = 84 - SPARC_REG_SP = 85 + SPARC_REG_SP = 84 + SPARC_REG_O7 = 85 SPARC_REG_Y = 86 SPARC_REG_XCC = 87 SPARC_REG_PC = 88 SPARC_REG_ENDING = 89 - SPARC_REG_O6 = 85 - SPARC_REG_I6 = 53 + SPARC_REG_O6 = 84 + SPARC_REG_I6 = 67 ) \ No newline at end of file diff --git a/bindings/go/unicorn/unicorn_const.go b/bindings/go/unicorn/unicorn_const.go index 20c42bc9..8bd35349 100644 --- a/bindings/go/unicorn/unicorn_const.go +++ b/bindings/go/unicorn/unicorn_const.go @@ -48,26 +48,33 @@ const ( ERR_MAP = 12 ERR_WRITE_PROT = 13 ERR_READ_PROT = 14 - ERR_EXEC_PROT = 15 + ERR_FETCH_PROT = 15 ERR_ARG = 16 ERR_READ_UNALIGNED = 17 ERR_WRITE_UNALIGNED = 18 ERR_FETCH_UNALIGNED = 19 MEM_READ = 16 MEM_WRITE = 17 - MEM_READ_WRITE = 18 - MEM_FETCH = 19 - MEM_WRITE_PROT = 20 - MEM_READ_PROT = 21 - MEM_EXEC_PROT = 22 - HOOK_INTR = 32 - HOOK_INSN = 33 - HOOK_CODE = 34 - HOOK_BLOCK = 35 - HOOK_MEM_INVALID = 36 - HOOK_MEM_READ = 37 - HOOK_MEM_WRITE = 38 - HOOK_MEM_READ_WRITE = 39 + MEM_FETCH = 18 + MEM_READ_INVALID = 19 + MEM_WRITE_INVALID = 20 + MEM_FETCH_INVALID = 21 + MEM_WRITE_PROT = 22 + MEM_READ_PROT = 23 + MEM_FETCH_PROT = 24 + HOOK_INTR = 1 + HOOK_INSN = 2 + HOOK_CODE = 4 + HOOK_BLOCK = 8 + HOOK_MEM_READ_INVALID = 16 + HOOK_MEM_WRITE_INVALID = 32 + HOOK_MEM_FETCH_INVALID = 64 + HOOK_MEM_READ_PROT = 128 + HOOK_MEM_WRITE_PROT = 256 + HOOK_MEM_FETCH_PROT = 512 + HOOK_MEM_READ = 1024 + HOOK_MEM_WRITE = 2048 + HOOK_MEM_FETCH = 4096 PROT_NONE = 0 PROT_READ = 1 diff --git a/bindings/java/Makefile b/bindings/java/Makefile index 4d3af3e5..df1916be 100644 --- a/bindings/java/Makefile +++ b/bindings/java/Makefile @@ -1,78 +1,26 @@ +.PHONY: gen_const clean jar all lib samples install -.PHONY: gen_const clean +all: gen_const + $(MAKE) -f Makefile.build all -JAVA_HOME := $(shell jrunscript -e 'java.lang.System.out.println(java.lang.System.getProperty("java.home"));') +lib: + $(MAKE) -f Makefile.build lib -JAVA_INC := $(shell realpath $(JAVA_HOME)/../include) +samples: + $(MAKE) -f Makefile.build samples -JAVA_PLATFORM_INC := $(shell dirname `find $(JAVA_INC) -name jni_md.h`) - -UNICORN_INC=../../include - -SAMPLES := $(shell ls samples/*.java) -SRC := $(shell ls unicorn/*.java) - -OS := $(shell uname) -ifeq ($(OS),Darwin) - LIB_EXT=.dylib -endif -ifeq ($(OS),Linux) - LIB_EXT=.so -else - LIB_EXT=.dll -endif - -CC=gcc -CFLAGS=-fPIC -LDFLAGS=-shared -fPIC -LIBS=-lunicorn -LIBDIR=-L../../ -INCS=-I$(JAVA_INC) -I$(JAVA_PLATFORM_INC) -I$(UNICORN_INC) - -JC=javac -CLASSPATH=./ - -.SUFFIXES: .java .class - -%.class: %.java - $(JC) $(JFLAGS) $< - -OBJS=unicorn_Unicorn.o - -JARFILE=unicorn.jar - -all: lib jar samples - -%.o: %.c - $(CC) -c $(CFLAGS) $(INCS) $< -o $@ - -unicorn_Unicorn.h: unicorn/Unicorn.java - javah unicorn.Unicorn - -unicorn_Unicorn.o: unicorn_Unicorn.c unicorn_Unicorn.h - $(CC) -c $(CFLAGS) $(INCS) $< -o $@ - -libunicorn_java$(LIB_EXT): unicorn_Unicorn.o - -lib: libunicorn_java$(LIB_EXT) unicorn_Unicorn.h - $(CC) -o $< $(LDFLAGS) $(OBJS) $(LIBDIR) $(LIBS) - -samples: $(SAMPLES:.java=.class) -jarfiles: $(SRC:.java=.class) - -jar: jarfiles - jar cf $(JARFILE) unicorn/*.class +jar: + $(MAKE) -f Makefile.build jar install: lib jar - cp libunicorn_java$(LIB_EXT) $(JAVA_HOME)/lib/ext - cp $(JARFILE) $(JAVA_HOME)/lib/ext + $(MAKE) -f Makefile.build install gen_const: cd .. && python const_generator.py java clean: - rm unicorn/*.class - rm samples/*.class - rm *.so - rm *.dylib - rm *.dll \ No newline at end of file + rm -f unicorn/*.class + rm -f samples/*.class + rm -f *.so + rm -f *.dylib + rm -f *.dll diff --git a/bindings/java/Makefile.build b/bindings/java/Makefile.build new file mode 100644 index 00000000..4d3af3e5 --- /dev/null +++ b/bindings/java/Makefile.build @@ -0,0 +1,78 @@ + +.PHONY: gen_const clean + +JAVA_HOME := $(shell jrunscript -e 'java.lang.System.out.println(java.lang.System.getProperty("java.home"));') + +JAVA_INC := $(shell realpath $(JAVA_HOME)/../include) + +JAVA_PLATFORM_INC := $(shell dirname `find $(JAVA_INC) -name jni_md.h`) + +UNICORN_INC=../../include + +SAMPLES := $(shell ls samples/*.java) +SRC := $(shell ls unicorn/*.java) + +OS := $(shell uname) +ifeq ($(OS),Darwin) + LIB_EXT=.dylib +endif +ifeq ($(OS),Linux) + LIB_EXT=.so +else + LIB_EXT=.dll +endif + +CC=gcc +CFLAGS=-fPIC +LDFLAGS=-shared -fPIC +LIBS=-lunicorn +LIBDIR=-L../../ +INCS=-I$(JAVA_INC) -I$(JAVA_PLATFORM_INC) -I$(UNICORN_INC) + +JC=javac +CLASSPATH=./ + +.SUFFIXES: .java .class + +%.class: %.java + $(JC) $(JFLAGS) $< + +OBJS=unicorn_Unicorn.o + +JARFILE=unicorn.jar + +all: lib jar samples + +%.o: %.c + $(CC) -c $(CFLAGS) $(INCS) $< -o $@ + +unicorn_Unicorn.h: unicorn/Unicorn.java + javah unicorn.Unicorn + +unicorn_Unicorn.o: unicorn_Unicorn.c unicorn_Unicorn.h + $(CC) -c $(CFLAGS) $(INCS) $< -o $@ + +libunicorn_java$(LIB_EXT): unicorn_Unicorn.o + +lib: libunicorn_java$(LIB_EXT) unicorn_Unicorn.h + $(CC) -o $< $(LDFLAGS) $(OBJS) $(LIBDIR) $(LIBS) + +samples: $(SAMPLES:.java=.class) +jarfiles: $(SRC:.java=.class) + +jar: jarfiles + jar cf $(JARFILE) unicorn/*.class + +install: lib jar + cp libunicorn_java$(LIB_EXT) $(JAVA_HOME)/lib/ext + cp $(JARFILE) $(JAVA_HOME)/lib/ext + +gen_const: + cd .. && python const_generator.py java + +clean: + rm unicorn/*.class + rm samples/*.class + rm *.so + rm *.dylib + rm *.dll \ No newline at end of file diff --git a/bindings/java/samples/Sample_x86.java b/bindings/java/samples/Sample_x86.java index 24985713..edda0e97 100644 --- a/bindings/java/samples/Sample_x86.java +++ b/bindings/java/samples/Sample_x86.java @@ -83,18 +83,14 @@ public class Sample_x86 { } } - private static class MyMemInvalidHook implements MemoryInvalidHook { - public boolean hook(Unicorn u, int type, long address, int size, long value, Object user) { - switch(type) { - case Unicorn.UC_MEM_WRITE: - System.out.printf(">>> Missing memory is being WRITE at 0x%x, data size = %d, data value = 0x%x\n", - address, size, value); - // map this memory in with 2MB in size - u.mem_map(0xaaaa0000, 2 * 1024*1024, Unicorn.UC_PROT_ALL); - // return true to indicate we want to continue - return true; - } - return false; + private static class MyWriteInvalidHook implements EventMemHook { + public boolean hook(Unicorn u, long address, int size, long value, Object user) { + System.out.printf(">>> Missing memory is being WRITE at 0x%x, data size = %d, data value = 0x%x\n", + address, size, value); + // map this memory in with 2MB in size + u.mem_map(0xaaaa0000, 2 * 1024*1024, Unicorn.UC_PROT_ALL); + // return true to indicate we want to continue + return true; } } @@ -423,7 +419,7 @@ public class Sample_x86 { u.hook_add(new MyCodeHook(), 1, 0, null); // intercept invalid memory events - u.hook_add(new MyMemInvalidHook(), null); + u.hook_add(new MyWriteInvalidHook(), Unicorn.UC_HOOK_MEM_WRITE_INVALID, null); // emulate machine code in infinite time try { diff --git a/bindings/java/unicorn/ReadWriteHook.java b/bindings/java/unicorn/EventMemHook.java similarity index 84% rename from bindings/java/unicorn/ReadWriteHook.java rename to bindings/java/unicorn/EventMemHook.java index 4514b483..db1f12d9 100644 --- a/bindings/java/unicorn/ReadWriteHook.java +++ b/bindings/java/unicorn/EventMemHook.java @@ -21,9 +21,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. package unicorn; -public interface ReadWriteHook extends Hook { +public interface EventMemHook extends Hook { - public void hook(Unicorn u, int type, long address, int size, long value, Object user); + public boolean hook(Unicorn u, long address, int size, long value, Object user); } diff --git a/bindings/java/unicorn/MemoryInvalidHook.java b/bindings/java/unicorn/MemHook.java similarity index 83% rename from bindings/java/unicorn/MemoryInvalidHook.java rename to bindings/java/unicorn/MemHook.java index 8b0a02d5..9f1a1889 100644 --- a/bindings/java/unicorn/MemoryInvalidHook.java +++ b/bindings/java/unicorn/MemHook.java @@ -21,9 +21,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. package unicorn; -public interface MemoryInvalidHook extends Hook { - - public boolean hook(Unicorn u, int type, long address, int size, long value, Object user); +public interface MemHook extends ReadHook,WriteHook { } diff --git a/bindings/java/unicorn/SparcConst.java b/bindings/java/unicorn/SparcConst.java index bd00c736..de0dc184 100644 --- a/bindings/java/unicorn/SparcConst.java +++ b/bindings/java/unicorn/SparcConst.java @@ -59,21 +59,21 @@ public interface SparcConst { public static final int UC_SPARC_REG_FCC1 = 50; public static final int UC_SPARC_REG_FCC2 = 51; public static final int UC_SPARC_REG_FCC3 = 52; - public static final int UC_SPARC_REG_FP = 53; - public static final int UC_SPARC_REG_G0 = 54; - public static final int UC_SPARC_REG_G1 = 55; - public static final int UC_SPARC_REG_G2 = 56; - public static final int UC_SPARC_REG_G3 = 57; - public static final int UC_SPARC_REG_G4 = 58; - public static final int UC_SPARC_REG_G5 = 59; - public static final int UC_SPARC_REG_G6 = 60; - public static final int UC_SPARC_REG_G7 = 61; - public static final int UC_SPARC_REG_I0 = 62; - public static final int UC_SPARC_REG_I1 = 63; - public static final int UC_SPARC_REG_I2 = 64; - public static final int UC_SPARC_REG_I3 = 65; - public static final int UC_SPARC_REG_I4 = 66; - public static final int UC_SPARC_REG_I5 = 67; + public static final int UC_SPARC_REG_G0 = 53; + public static final int UC_SPARC_REG_G1 = 54; + public static final int UC_SPARC_REG_G2 = 55; + public static final int UC_SPARC_REG_G3 = 56; + public static final int UC_SPARC_REG_G4 = 57; + public static final int UC_SPARC_REG_G5 = 58; + public static final int UC_SPARC_REG_G6 = 59; + public static final int UC_SPARC_REG_G7 = 60; + public static final int UC_SPARC_REG_I0 = 61; + public static final int UC_SPARC_REG_I1 = 62; + public static final int UC_SPARC_REG_I2 = 63; + public static final int UC_SPARC_REG_I3 = 64; + public static final int UC_SPARC_REG_I4 = 65; + public static final int UC_SPARC_REG_I5 = 66; + public static final int UC_SPARC_REG_FP = 67; public static final int UC_SPARC_REG_I7 = 68; public static final int UC_SPARC_REG_ICC = 69; public static final int UC_SPARC_REG_L0 = 70; @@ -90,13 +90,13 @@ public interface SparcConst { public static final int UC_SPARC_REG_O3 = 81; public static final int UC_SPARC_REG_O4 = 82; public static final int UC_SPARC_REG_O5 = 83; - public static final int UC_SPARC_REG_O7 = 84; - public static final int UC_SPARC_REG_SP = 85; + public static final int UC_SPARC_REG_SP = 84; + public static final int UC_SPARC_REG_O7 = 85; public static final int UC_SPARC_REG_Y = 86; public static final int UC_SPARC_REG_XCC = 87; public static final int UC_SPARC_REG_PC = 88; public static final int UC_SPARC_REG_ENDING = 89; - public static final int UC_SPARC_REG_O6 = 85; - public static final int UC_SPARC_REG_I6 = 53; + public static final int UC_SPARC_REG_O6 = 84; + public static final int UC_SPARC_REG_I6 = 67; } diff --git a/bindings/java/unicorn/Unicorn.java b/bindings/java/unicorn/Unicorn.java index 40476ae0..9abfeb60 100644 --- a/bindings/java/unicorn/Unicorn.java +++ b/bindings/java/unicorn/Unicorn.java @@ -31,10 +31,15 @@ public class Unicorn implements UnicornConst, ArmConst, Arm64Const, M68kConst, S private long interruptHandle = 0; private long codeHandle = 0; - private long memInvalidHandle = 0; + private Hashtable eventMemHandles = new Hashtable(); + private long readInvalidHandle = 0; + private long writeInvalidHandle = 0; + private long fetchProtHandle = 0; + private long readProtHandle = 0; + private long writeProtHandle = 0; + private long readHandle = 0; private long writeHandle = 0; - private long readWriteHandle = 0; private long inHandle = 0; private long outHandle = 0; private long syscallHandle = 0; @@ -51,21 +56,28 @@ public class Unicorn implements UnicornConst, ArmConst, Arm64Const, M68kConst, S private ArrayList blockList = new ArrayList(); private ArrayList intrList = new ArrayList(); private ArrayList codeList = new ArrayList(); - private ArrayList memInvalidList = new ArrayList(); private ArrayList readList = new ArrayList(); private ArrayList writeList = new ArrayList(); - private ArrayList readWriteList = new ArrayList(); private ArrayList inList = new ArrayList(); private ArrayList outList = new ArrayList(); private ArrayList syscallList = new ArrayList(); + private Hashtable > eventMemLists = new Hashtable >(); + private ArrayList> allLists = new ArrayList>(); + private static Hashtable eventMemMap = new Hashtable(); private static Hashtable unicorns = new Hashtable(); //required to load native method implementations static { System.loadLibrary("unicorn_java"); //loads unicorn.dll or libunicorn.so + eventMemMap.put(UC_HOOK_MEM_READ_INVALID, UC_MEM_READ_INVALID); + eventMemMap.put(UC_HOOK_MEM_WRITE_INVALID, UC_MEM_WRITE_INVALID); + eventMemMap.put(UC_HOOK_MEM_FETCH_INVALID, UC_MEM_FETCH_INVALID); + eventMemMap.put(UC_HOOK_MEM_READ_PROT, UC_MEM_READ_PROT); + eventMemMap.put(UC_HOOK_MEM_WRITE_PROT, UC_MEM_WRITE_PROT); + eventMemMap.put(UC_HOOK_MEM_FETCH_PROT, UC_MEM_FETCH_PROT); } /** @@ -128,25 +140,29 @@ public class Unicorn implements UnicornConst, ArmConst, Arm64Const, M68kConst, S } /** - * Invoke all UC_HOOK_MEM_INVALID callbacks registered for a specific Unicorn. + * Invoke all UC_HOOK_MEM_XXX_INVALID andor UC_HOOK_MEM_XXX_PROT callbacks registered + * for a specific Unicorn. * This function gets invoked from the native C callback registered for - * for UC_HOOK_MEM_INVALID + * for UC_HOOK_MEM_XXX_INVALID or UC_HOOK_MEM_XXX_PROT * * @param eng A Unicorn uc_engine* eng returned by uc_open - * @param type This memory is being read (UC_MEM_READ), or written (UC_MEM_WRITE) + * @param type The type of event that is taking place * @param address Address of instruction being executed * @param size Size of data being read or written * @param value Value of data being written to memory, or irrelevant if type = READ. * @return true to continue, or false to stop program (due to invalid memory). - * @see hook_add, unicorn.MemoryInvalidHook + * @see hook_add, unicorn.EventMemHook */ - private static boolean invokeMemInvalidCallbacks(long eng, int type, long address, int size, long value) { + private static boolean invokeEventMemCallbacks(long eng, int type, long address, int size, long value) { Unicorn u = unicorns.get(eng); boolean result = true; if (u != null) { - for (Tuple p : u.memInvalidList) { - MemoryInvalidHook mh = (MemoryInvalidHook)p.function; - result &= mh.hook(u, type, address, size, value, p.data); + ArrayList funcList = u.eventMemLists.get(type); + if (funcList != null) { + for (Tuple p : funcList) { + EventMemHook emh = (EventMemHook)p.function; + result &= emh.hook(u, address, size, value, p.data); + } } } return result; @@ -193,28 +209,6 @@ public class Unicorn implements UnicornConst, ArmConst, Arm64Const, M68kConst, S } } -/** - * Invoke all UC_HOOK_MEM_READ_WRITE callbacks registered for a specific Unicorn. - * This function gets invoked from the native C callback registered for - * for UC_HOOK_MEM_READ_WRITE - * - * @param eng A Unicorn uc_engine* eng returned by uc_open - * @param type Type of access being performed (UC_MEM_READ, UC_MEM_WRITE, UC_MEM_READ_WRITE) - * @param address Address of instruction being executed - * @param size Size of data being read - * @param value value being written (if applicable) - * @see hook_add, unicorn.ReadWriteHook - */ - private static void invokeReadWriteCallbacks(long eng, int type, long address, int size, long value) { - Unicorn u = unicorns.get(eng); - if (u != null) { - for (Tuple p : u.readWriteList) { - ReadWriteHook rwh = (ReadWriteHook)p.function; - rwh.hook(u, type, address, size, value, p.data); - } - } - } - /** * Invoke all UC_HOOK_INSN callbacks registered for a specific Unicorn. * This is specifically for the x86 IN instruction. @@ -303,10 +297,8 @@ public class Unicorn implements UnicornConst, ArmConst, Arm64Const, M68kConst, S allLists.add(blockList); allLists.add(intrList); allLists.add(codeList); - allLists.add(memInvalidList); allLists.add(readList); allLists.add(writeList); - allLists.add(readWriteList); allLists.add(inList); allLists.add(outList); allLists.add(syscallList); @@ -528,34 +520,47 @@ public class Unicorn implements UnicornConst, ArmConst, Arm64Const, M68kConst, S } /** - * Hook registration for UC_HOOK_MEM_READ_WRITE hooks. The registered callback function will be - * invoked whenever a memory read or write is performed within the address range begin <= mem_addr <= end. For - * the special case in which begin > end, the callback will be invoked for ALL memory reads and writes. + * Hook registration for UC_HOOK_MEM_WRITE | UC_HOOK_MEM_WRITE hooks. The registered callback function will be + * invoked whenever a memory write or read is performed within the address range begin <= addr <= end. For + * the special case in which begin > end, the callback will be invoked for ALL memory writes. * - * @param callback Implementation of a ReadWriteHook interface - * @param begin Start address of memory read/write range - * @param end End address of memory read/write range + * @param callback Implementation of a MemHook interface + * @param begin Start address of memory range + * @param end End address of memory range * @param user_data User data to be passed to the callback function each time the event is triggered */ - public void hook_add(ReadWriteHook callback, long begin, long end, Object user_data) throws UnicornException { - if (readWriteHandle == 0) { - readWriteHandle = registerHook(eng, UC_HOOK_MEM_READ_WRITE, begin, end); - } - readWriteList.add(new Tuple(callback, user_data)); + public void hook_add(MemHook callback, long begin, long end, Object user_data) throws UnicornException { + hook_add((ReadHook)callback, begin, end, user_data); + hook_add((WriteHook)callback, begin, end, user_data); } - + /** - * Hook registration for UC_HOOK_MEM_INVALID hooks. The registered callback function will be - * invoked whenever a read or write is attempted from an unmapped memory address. + * Hook registration for UC_HOOK_MEM_XXX_INVALID and UC_HOOK_MEM_XXX_PROT hooks. + * The registered callback function will be invoked whenever a read or write is + * attempted from an invalid or protected memory address. * - * @param callback Implementation of a MemoryInvalidHook interface + * @param callback Implementation of a EventMemHook interface + * @param type Type of memory event being hooked such as UC_HOOK_MEM_READ_INVALID or UC_HOOK_MEM_WRITE_PROT * @param user_data User data to be passed to the callback function each time the event is triggered */ - public void hook_add(MemoryInvalidHook callback, Object user_data) throws UnicornException { - if (memInvalidHandle == 0) { - memInvalidHandle = registerHook(eng, UC_HOOK_MEM_INVALID); + public void hook_add(EventMemHook callback, int type, Object user_data) throws UnicornException { + //test all of the EventMem related bits in type + for (Integer htype : eventMemMap.keySet()) { + if ((type & htype) != 0) { //the 'htype' bit is set in type + Long handle = eventMemHandles.get(htype); + if (handle == null) { + eventMemHandles.put(htype, registerHook(eng, htype)); + } + int cbType = eventMemMap.get(htype); + ArrayList flist = eventMemLists.get(cbType); + if (flist == null) { + flist = new ArrayList(); + allLists.add(flist); + eventMemLists.put(cbType, flist); + } + flist.add(new Tuple(callback, user_data)); + } } - memInvalidList.add(new Tuple(callback, user_data)); } /** diff --git a/bindings/java/unicorn/UnicornConst.java b/bindings/java/unicorn/UnicornConst.java index ba85074b..615a6906 100644 --- a/bindings/java/unicorn/UnicornConst.java +++ b/bindings/java/unicorn/UnicornConst.java @@ -50,23 +50,33 @@ public interface UnicornConst { public static final int UC_ERR_MAP = 12; public static final int UC_ERR_WRITE_PROT = 13; public static final int UC_ERR_READ_PROT = 14; - public static final int UC_ERR_EXEC_PROT = 15; + public static final int UC_ERR_FETCH_PROT = 15; public static final int UC_ERR_ARG = 16; + public static final int UC_ERR_READ_UNALIGNED = 17; + public static final int UC_ERR_WRITE_UNALIGNED = 18; + public static final int UC_ERR_FETCH_UNALIGNED = 19; public static final int UC_MEM_READ = 16; public static final int UC_MEM_WRITE = 17; - public static final int UC_MEM_READ_WRITE = 18; - public static final int UC_MEM_FETCH = 19; - public static final int UC_MEM_WRITE_PROT = 20; - public static final int UC_MEM_READ_PROT = 21; - public static final int UC_MEM_EXEC_PROT = 22; - public static final int UC_HOOK_INTR = 32; - public static final int UC_HOOK_INSN = 33; - public static final int UC_HOOK_CODE = 34; - public static final int UC_HOOK_BLOCK = 35; - public static final int UC_HOOK_MEM_INVALID = 36; - public static final int UC_HOOK_MEM_READ = 37; - public static final int UC_HOOK_MEM_WRITE = 38; - public static final int UC_HOOK_MEM_READ_WRITE = 39; + public static final int UC_MEM_FETCH = 18; + public static final int UC_MEM_READ_INVALID = 19; + public static final int UC_MEM_WRITE_INVALID = 20; + public static final int UC_MEM_FETCH_INVALID = 21; + public static final int UC_MEM_WRITE_PROT = 22; + public static final int UC_MEM_READ_PROT = 23; + public static final int UC_MEM_FETCH_PROT = 24; + public static final int UC_HOOK_INTR = 1; + public static final int UC_HOOK_INSN = 2; + public static final int UC_HOOK_CODE = 4; + public static final int UC_HOOK_BLOCK = 8; + public static final int UC_HOOK_MEM_READ_INVALID = 16; + public static final int UC_HOOK_MEM_WRITE_INVALID = 32; + public static final int UC_HOOK_MEM_FETCH_INVALID = 64; + public static final int UC_HOOK_MEM_READ_PROT = 128; + public static final int UC_HOOK_MEM_WRITE_PROT = 256; + public static final int UC_HOOK_MEM_FETCH_PROT = 512; + public static final int UC_HOOK_MEM_READ = 1024; + public static final int UC_HOOK_MEM_WRITE = 2048; + public static final int UC_HOOK_MEM_FETCH = 4096; public static final int UC_PROT_NONE = 0; public static final int UC_PROT_READ = 1; diff --git a/bindings/java/unicorn_Unicorn.c b/bindings/java/unicorn_Unicorn.c index 6a454a5a..470c6340 100644 --- a/bindings/java/unicorn_Unicorn.c +++ b/bindings/java/unicorn_Unicorn.c @@ -33,10 +33,9 @@ static jmethodID invokeBlockCallbacks = 0; static jmethodID invokeInterruptCallbacks = 0; static jmethodID invokeCodeCallbacks = 0; -static jmethodID invokeMemInvalidCallbacks = 0; +static jmethodID invokeEventMemCallbacks = 0; static jmethodID invokeReadCallbacks = 0; static jmethodID invokeWriteCallbacks = 0; -static jmethodID invokeReadWriteCallbacks = 0; static jmethodID invokeInCallbacks = 0; static jmethodID invokeOutCallbacks = 0; static jmethodID invokeSyscallCallbacks = 0; @@ -157,9 +156,6 @@ static void cb_hookmem(uc_engine *eng, uc_mem_type type, case UC_MEM_WRITE: (*env)->CallStaticVoidMethod(env, clz, invokeWriteCallbacks, (jlong)eng, (jlong)address, (int)size, (jlong)value); break; - case UC_MEM_READ_WRITE: - (*env)->CallStaticVoidMethod(env, clz, invokeReadWriteCallbacks, (jlong)eng, (int)type, (jlong)address, (int)size, (jlong)value); - break; } (*cachedJVM)->DetachCurrentThread(cachedJVM); } @@ -179,7 +175,7 @@ static bool cb_eventmem(uc_engine *eng, uc_mem_type type, if ((*env)->ExceptionCheck(env)) { return false; } - jboolean res = (*env)->CallStaticBooleanMethod(env, clz, invokeMemInvalidCallbacks, (jlong)eng, (int)type, (jlong)address, (int)size, (jlong)value); + jboolean res = (*env)->CallStaticBooleanMethod(env, clz, invokeEventMemCallbacks, (jlong)eng, (int)type, (jlong)address, (int)size, (jlong)value); (*cachedJVM)->DetachCurrentThread(cachedJVM); return res; } @@ -393,9 +389,14 @@ JNIEXPORT jlong JNICALL Java_unicorn_Unicorn_registerHook__JI } err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_hookintr, env); break; - case UC_HOOK_MEM_INVALID: // Hook for all invalid memory access events - if (invokeMemInvalidCallbacks == 0) { - invokeMemInvalidCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeMemInvalidCallbacks", "(JIJIJ)Z"); + case UC_HOOK_MEM_FETCH_INVALID: // Hook for all invalid memory access events + case UC_HOOK_MEM_READ_INVALID: // Hook for all invalid memory access events + case UC_HOOK_MEM_WRITE_INVALID: // Hook for all invalid memory access events + case UC_HOOK_MEM_FETCH_PROT: // Hook for all invalid memory access events + case UC_HOOK_MEM_READ_PROT: // Hook for all invalid memory access events + case UC_HOOK_MEM_WRITE_PROT: // Hook for all invalid memory access events + if (invokeEventMemCallbacks == 0) { + invokeEventMemCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeEventMemCallbacks", "(JIJIJ)Z"); } err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_eventmem, env); break; @@ -471,12 +472,6 @@ JNIEXPORT jlong JNICALL Java_unicorn_Unicorn_registerHook__JIJJ } err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_hookmem, env, arg1, arg2); break; - case UC_HOOK_MEM_READ_WRITE: // Hook all memory accesses (either READ or WRITE). - if (invokeReadWriteCallbacks == 0) { - invokeReadWriteCallbacks = (*env)->GetStaticMethodID(env, clz, "invokeReadWriteCallbacks", "(JIJIJ)V"); - } - err = uc_hook_add((uc_engine*)eng, &hh, (uc_hook_type)type, cb_hookmem, env, arg1, arg2); - break; } return (jlong)hh; } diff --git a/bindings/python/sample_x86.py b/bindings/python/sample_x86.py index 24b82450..151ed73c 100755 --- a/bindings/python/sample_x86.py +++ b/bindings/python/sample_x86.py @@ -33,7 +33,7 @@ def hook_code(uc, address, size, user_data): # callback for tracing invalid memory access (READ or WRITE) def hook_mem_invalid(uc, access, address, size, value, user_data): - if access == UC_MEM_WRITE: + if access == UC_MEM_WRITE_INVALID: print(">>> Missing memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" \ %(address, size, value)) # map this memory in with 2MB in size @@ -231,7 +231,7 @@ def test_i386_invalid_mem_write(): #mu.hook_add(UC_HOOK_CODE, hook_code) # intercept invalid memory events - mu.hook_add(UC_HOOK_MEM_INVALID, hook_mem_invalid) + mu.hook_add(UC_HOOK_MEM_READ_INVALID | UC_HOOK_MEM_WRITE_INVALID, hook_mem_invalid) try: # emulate machine code in infinite time @@ -349,7 +349,7 @@ def test_x86_64(): mu.hook_add(UC_HOOK_MEM_WRITE, hook_mem_access) mu.hook_add(UC_HOOK_MEM_READ, hook_mem_access) # actually you can also use READ_WRITE to trace all memory access - #mu.hook_add(UC_HOOK_MEM_READ_WRITE, hook_mem_access) + #mu.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, hook_mem_access) try: # emulate machine code in infinite time diff --git a/bindings/python/unicorn/sparc_const.py b/bindings/python/unicorn/sparc_const.py index cb66d89c..7bd326d0 100644 --- a/bindings/python/unicorn/sparc_const.py +++ b/bindings/python/unicorn/sparc_const.py @@ -55,21 +55,21 @@ UC_SPARC_REG_FCC0 = 49 UC_SPARC_REG_FCC1 = 50 UC_SPARC_REG_FCC2 = 51 UC_SPARC_REG_FCC3 = 52 -UC_SPARC_REG_FP = 53 -UC_SPARC_REG_G0 = 54 -UC_SPARC_REG_G1 = 55 -UC_SPARC_REG_G2 = 56 -UC_SPARC_REG_G3 = 57 -UC_SPARC_REG_G4 = 58 -UC_SPARC_REG_G5 = 59 -UC_SPARC_REG_G6 = 60 -UC_SPARC_REG_G7 = 61 -UC_SPARC_REG_I0 = 62 -UC_SPARC_REG_I1 = 63 -UC_SPARC_REG_I2 = 64 -UC_SPARC_REG_I3 = 65 -UC_SPARC_REG_I4 = 66 -UC_SPARC_REG_I5 = 67 +UC_SPARC_REG_G0 = 53 +UC_SPARC_REG_G1 = 54 +UC_SPARC_REG_G2 = 55 +UC_SPARC_REG_G3 = 56 +UC_SPARC_REG_G4 = 57 +UC_SPARC_REG_G5 = 58 +UC_SPARC_REG_G6 = 59 +UC_SPARC_REG_G7 = 60 +UC_SPARC_REG_I0 = 61 +UC_SPARC_REG_I1 = 62 +UC_SPARC_REG_I2 = 63 +UC_SPARC_REG_I3 = 64 +UC_SPARC_REG_I4 = 65 +UC_SPARC_REG_I5 = 66 +UC_SPARC_REG_FP = 67 UC_SPARC_REG_I7 = 68 UC_SPARC_REG_ICC = 69 UC_SPARC_REG_L0 = 70 @@ -86,11 +86,11 @@ UC_SPARC_REG_O2 = 80 UC_SPARC_REG_O3 = 81 UC_SPARC_REG_O4 = 82 UC_SPARC_REG_O5 = 83 -UC_SPARC_REG_O7 = 84 -UC_SPARC_REG_SP = 85 +UC_SPARC_REG_SP = 84 +UC_SPARC_REG_O7 = 85 UC_SPARC_REG_Y = 86 UC_SPARC_REG_XCC = 87 UC_SPARC_REG_PC = 88 UC_SPARC_REG_ENDING = 89 -UC_SPARC_REG_O6 = 85 -UC_SPARC_REG_I6 = 53 +UC_SPARC_REG_O6 = 84 +UC_SPARC_REG_I6 = 67 diff --git a/bindings/python/unicorn/unicorn.py b/bindings/python/unicorn/unicorn.py index 080481ce..dd67306a 100644 --- a/bindings/python/unicorn/unicorn.py +++ b/bindings/python/unicorn/unicorn.py @@ -141,6 +141,7 @@ class Uc(object): raise UcError(status) # internal mapping table to save callback & userdata self._callbacks = {} + self._ctype_cbs = {} self._callback_count = 0 @@ -261,6 +262,7 @@ class Uc(object): # save callback & user_data self._callback_count += 1 self._callbacks[self._callback_count] = (callback, user_data) + cb = None if htype in (UC_HOOK_BLOCK, UC_HOOK_CODE): begin = ctypes.c_uint64(arg1) @@ -270,11 +272,13 @@ class Uc(object): cb = ctypes.cast(UC_HOOK_CODE_CB(self._hookcode_cb), UC_HOOK_CODE_CB) status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, cb, \ ctypes.cast(self._callback_count, ctypes.c_void_p), begin, end) - elif htype == UC_HOOK_MEM_INVALID: + elif htype & UC_HOOK_MEM_READ_INVALID or htype & UC_HOOK_MEM_WRITE_INVALID or \ + htype & UC_HOOK_MEM_FETCH_INVALID or htype & UC_HOOK_MEM_READ_PROT or \ + htype & UC_HOOK_MEM_WRITE_PROT or htype & UC_HOOK_MEM_FETCH_PROT: cb = ctypes.cast(UC_HOOK_MEM_INVALID_CB(self._hook_mem_invalid_cb), UC_HOOK_MEM_INVALID_CB) status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \ cb, ctypes.cast(self._callback_count, ctypes.c_void_p)) - elif htype in (UC_HOOK_MEM_READ, UC_HOOK_MEM_WRITE, UC_HOOK_MEM_READ_WRITE): + elif htype in (UC_HOOK_MEM_READ, UC_HOOK_MEM_WRITE, UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE): cb = ctypes.cast(UC_HOOK_MEM_ACCESS_CB(self._hook_mem_access_cb), UC_HOOK_MEM_ACCESS_CB) status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \ cb, ctypes.cast(self._callback_count, ctypes.c_void_p)) @@ -293,6 +297,9 @@ class Uc(object): status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \ cb, ctypes.cast(self._callback_count, ctypes.c_void_p)) + # save the ctype function so gc will leave it alone. + self._ctype_cbs[self._callback_count] = cb + if status != UC_ERR_OK: raise UcError(status) diff --git a/bindings/python/unicorn/unicorn_const.py b/bindings/python/unicorn/unicorn_const.py index 7999afc1..467cdd29 100644 --- a/bindings/python/unicorn/unicorn_const.py +++ b/bindings/python/unicorn/unicorn_const.py @@ -46,26 +46,33 @@ UC_ERR_INSN_INVALID = 11 UC_ERR_MAP = 12 UC_ERR_WRITE_PROT = 13 UC_ERR_READ_PROT = 14 -UC_ERR_EXEC_PROT = 15 +UC_ERR_FETCH_PROT = 15 UC_ERR_ARG = 16 UC_ERR_READ_UNALIGNED = 17 UC_ERR_WRITE_UNALIGNED = 18 UC_ERR_FETCH_UNALIGNED = 19 UC_MEM_READ = 16 UC_MEM_WRITE = 17 -UC_MEM_READ_WRITE = 18 -UC_MEM_FETCH = 19 -UC_MEM_WRITE_PROT = 20 -UC_MEM_READ_PROT = 21 -UC_MEM_EXEC_PROT = 22 -UC_HOOK_INTR = 32 -UC_HOOK_INSN = 33 -UC_HOOK_CODE = 34 -UC_HOOK_BLOCK = 35 -UC_HOOK_MEM_INVALID = 36 -UC_HOOK_MEM_READ = 37 -UC_HOOK_MEM_WRITE = 38 -UC_HOOK_MEM_READ_WRITE = 39 +UC_MEM_FETCH = 18 +UC_MEM_READ_INVALID = 19 +UC_MEM_WRITE_INVALID = 20 +UC_MEM_FETCH_INVALID = 21 +UC_MEM_WRITE_PROT = 22 +UC_MEM_READ_PROT = 23 +UC_MEM_FETCH_PROT = 24 +UC_HOOK_INTR = 1 +UC_HOOK_INSN = 2 +UC_HOOK_CODE = 4 +UC_HOOK_BLOCK = 8 +UC_HOOK_MEM_READ_INVALID = 16 +UC_HOOK_MEM_WRITE_INVALID = 32 +UC_HOOK_MEM_FETCH_INVALID = 64 +UC_HOOK_MEM_READ_PROT = 128 +UC_HOOK_MEM_WRITE_PROT = 256 +UC_HOOK_MEM_FETCH_PROT = 512 +UC_HOOK_MEM_READ = 1024 +UC_HOOK_MEM_WRITE = 2048 +UC_HOOK_MEM_FETCH = 4096 UC_PROT_NONE = 0 UC_PROT_READ = 1 diff --git a/hook.c b/hook.c index c201c315..dbc61a78 100644 --- a/hook.c +++ b/hook.c @@ -73,7 +73,7 @@ size_t hook_add(struct uc_struct *uc, int type, uint64_t begin, uint64_t end, vo if (begin > end) uc->hook_write_idx = i; break; - case UC_HOOK_MEM_READ_WRITE: + case UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE: uc->hook_mem_read = true; uc->hook_mem_write = true; if (begin > end) { @@ -109,8 +109,28 @@ uc_err hook_del(struct uc_struct *uc, uc_hook hh) uc->hook_write_idx = 0; } - if (hh == uc->hook_mem_idx) { - uc->hook_mem_idx = 0; + if (hh == uc->hook_mem_read_idx) { + uc->hook_mem_read_idx = 0; + } + + if (hh == uc->hook_mem_write_idx) { + uc->hook_mem_write_idx = 0; + } + + if (hh == uc->hook_mem_fetch_idx) { + uc->hook_mem_fetch_idx = 0; + } + + if (hh == uc->hook_mem_read_prot_idx) { + uc->hook_mem_read_prot_idx = 0; + } + + if (hh == uc->hook_mem_write_prot_idx) { + uc->hook_mem_write_prot_idx = 0; + } + + if (hh == uc->hook_mem_fetch_prot_idx) { + uc->hook_mem_fetch_prot_idx = 0; } if (hh == uc->hook_intr_idx) { @@ -176,13 +196,13 @@ static struct hook_struct *_hook_find(struct uc_struct *uc, int type, uint64_t a } break; case UC_HOOK_MEM_READ: - if (uc->hook_callbacks[i].hook_type == UC_HOOK_MEM_READ || uc->hook_callbacks[i].hook_type == UC_HOOK_MEM_READ_WRITE) { + if (uc->hook_callbacks[i].hook_type & UC_HOOK_MEM_READ) { if (uc->hook_callbacks[i].begin <= address && address <= uc->hook_callbacks[i].end) return &uc->hook_callbacks[i]; } break; case UC_HOOK_MEM_WRITE: - if (uc->hook_callbacks[i].hook_type == UC_HOOK_MEM_WRITE || uc->hook_callbacks[i].hook_type == UC_HOOK_MEM_READ_WRITE) { + if (uc->hook_callbacks[i].hook_type & UC_HOOK_MEM_WRITE) { if (uc->hook_callbacks[i].begin <= address && address <= uc->hook_callbacks[i].end) return &uc->hook_callbacks[i]; } diff --git a/include/uc_priv.h b/include/uc_priv.h index c196291a..1f19a58a 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -152,7 +152,13 @@ struct uc_struct { bool hook_block, hook_insn, hook_mem_read, hook_mem_write; uint64_t block_addr; // save the last block address we hooked // indexes to event callbacks - int hook_mem_idx; // for handling invalid memory access + int hook_mem_read_idx; // for handling invalid memory read access on unmapped memory + int hook_mem_write_idx; // for handling invalid memory write access on unmapped memory + int hook_mem_fetch_idx; // for handling invalid memory fetch access on unmapped memory + int hook_mem_read_prot_idx; // for handling invalid memory read access on read-protected memory + int hook_mem_write_prot_idx; // for handling invalid memory write access on write-protected memory + int hook_mem_fetch_prot_idx; // for handling invalid memory fetch access on non-executable memory + int hook_intr_idx; // for handling interrupt int hook_out_idx; // for handling OUT instruction (X86) int hook_in_idx; // for handling IN instruction (X86) diff --git a/include/unicorn/sparc.h b/include/unicorn/sparc.h index 25a1140b..353dbb34 100644 --- a/include/unicorn/sparc.h +++ b/include/unicorn/sparc.h @@ -75,7 +75,6 @@ typedef enum uc_sparc_reg { UC_SPARC_REG_FCC1, UC_SPARC_REG_FCC2, UC_SPARC_REG_FCC3, - UC_SPARC_REG_FP, UC_SPARC_REG_G0, UC_SPARC_REG_G1, UC_SPARC_REG_G2, @@ -90,6 +89,7 @@ typedef enum uc_sparc_reg { UC_SPARC_REG_I3, UC_SPARC_REG_I4, UC_SPARC_REG_I5, + UC_SPARC_REG_FP, UC_SPARC_REG_I7, UC_SPARC_REG_ICC, // Integer condition codes UC_SPARC_REG_L0, @@ -106,14 +106,14 @@ typedef enum uc_sparc_reg { UC_SPARC_REG_O3, UC_SPARC_REG_O4, UC_SPARC_REG_O5, - UC_SPARC_REG_O7, UC_SPARC_REG_SP, + UC_SPARC_REG_O7, UC_SPARC_REG_Y, // special register UC_SPARC_REG_XCC, - // pseudo register + // pseudo register UC_SPARC_REG_PC, // program counter register UC_SPARC_REG_ENDING, // <-- mark the end of the list of registers diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index 08e01028..75f47064 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -120,7 +120,7 @@ typedef enum uc_err { UC_ERR_MAP, // Invalid memory mapping: uc_mem_map() UC_ERR_WRITE_PROT, // Quit emulation due to UC_MEM_WRITE_PROT violation: uc_emu_start() UC_ERR_READ_PROT, // Quit emulation due to UC_MEM_READ_PROT violation: uc_emu_start() - UC_ERR_EXEC_PROT, // Quit emulation due to UC_MEM_EXEC_PROT violation: uc_emu_start() + UC_ERR_FETCH_PROT, // Quit emulation due to UC_MEM_FETCH_PROT violation: uc_emu_start() UC_ERR_ARG, // Inavalid argument provided to uc_xxx function (See specific function API) UC_ERR_READ_UNALIGNED, // Unaligned read UC_ERR_WRITE_UNALIGNED, // Unaligned write @@ -153,28 +153,48 @@ typedef void (*uc_cb_insn_out_t)(uc_engine *uc, uint32_t port, int size, uint32_ // All type of memory accesses for UC_HOOK_MEM_* typedef enum uc_mem_type { - UC_MEM_READ = 16, // Unmapped memory is read from - UC_MEM_WRITE, // Unmapped memory is written to - UC_MEM_READ_WRITE, // Unmapped memory is accessed (either READ or WRITE) - UC_MEM_FETCH, // Unmapped memory is fetched + UC_MEM_READ = 16, // Memory is read from + UC_MEM_WRITE, // Memory is written to + UC_MEM_FETCH, // Memory is fetched + UC_MEM_READ_INVALID, // Unmapped memory is read from + UC_MEM_WRITE_INVALID, // Unmapped memory is written to + UC_MEM_FETCH_INVALID, // Unmapped memory is fetched UC_MEM_WRITE_PROT, // Write to write protected, but mapped, memory UC_MEM_READ_PROT, // Read from read protected, but mapped, memory - UC_MEM_EXEC_PROT, // Fetch from non-executable, but mapped, memory + UC_MEM_FETCH_PROT, // Fetch from non-executable, but mapped, memory } uc_mem_type; // All type of hooks for uc_hook_add() API. typedef enum uc_hook_type { - UC_HOOK_INTR = 32, // Hook all interrupt events - UC_HOOK_INSN, // Hook a particular instruction - UC_HOOK_CODE, // Hook a range of code - UC_HOOK_BLOCK, // Hook basic blocks - UC_HOOK_MEM_INVALID, // Hook for all invalid memory access events - UC_HOOK_MEM_READ, // Hook all memory read events. - UC_HOOK_MEM_WRITE, // Hook all memory write events. - UC_HOOK_MEM_READ_WRITE, // Hook all memory accesses (either READ or WRITE). + UC_HOOK_INTR = 1 << 0, // Hook all interrupt events + UC_HOOK_INSN = 1 << 1, // Hook a particular instruction + UC_HOOK_CODE = 1 << 2, // Hook a range of code + UC_HOOK_BLOCK = 1 << 3, // Hook basic blocks + UC_HOOK_MEM_READ_INVALID = 1 << 4, // Hook for invalid memory read events + UC_HOOK_MEM_WRITE_INVALID = 1 << 5, // Hook for invalid memory write events + UC_HOOK_MEM_FETCH_INVALID = 1 << 6, // Hook for invalid memory fetch for execution events + UC_HOOK_MEM_READ_PROT = 1 << 7, // Hook for memory read on read-protected memory + UC_HOOK_MEM_WRITE_PROT = 1 << 8, // Hook for memory write on write-protected memory + UC_HOOK_MEM_FETCH_PROT = 1 << 9, // Hook for memory fetch on non-executable memory + UC_HOOK_MEM_READ = 1 << 10, // Hook memory read events. + UC_HOOK_MEM_WRITE = 1 << 11, // Hook memory write events. + UC_HOOK_MEM_FETCH = 1 << 12, // Hook memory fetch for execution events } uc_hook_type; -// Callback function for hooking memory (UC_HOOK_MEM_*) +// hook type for all events of unmapped memory access +#define UC_HOOK_MEM_INVALID (UC_HOOK_MEM_READ_INVALID + UC_HOOK_MEM_WRITE_INVALID + UC_HOOK_MEM_FETCH_INVALID) +// hook type for all events of illegal protected memory access +#define UC_HOOK_MEM_PROT (UC_HOOK_MEM_READ_PROT + UC_HOOK_MEM_WRITE_PROT + UC_HOOK_MEM_FETCH_PROT) +// hook type for all events of illegal read memory access +#define UC_HOOK_MEM_READ_ERR (UC_HOOK_MEM_READ_PROT + UC_HOOK_MEM_READ_INVALID) +// hook type for all events of illegal write memory access +#define UC_HOOK_MEM_WRITE_ERR (UC_HOOK_MEM_WRITE_PROT + UC_HOOK_MEM_WRITE_INVALID) +// hook type for all events of illegal fetch memory access +#define UC_HOOK_MEM_FETCH_ERR (UC_HOOK_MEM_FETCH_PROT + UC_HOOK_MEM_FETCH_INVALID) +// hook type for all events of illegal memory access +#define UC_HOOK_MEM_ERR (UC_HOOK_MEM_INVALID + UC_HOOK_MEM_PROT) + +// Callback function for hooking memory (UC_MEM_READ, UC_MEM_WRITE & UC_MEM_FETCH) // @type: this memory is being READ, or WRITE // @address: address where the code is being executed // @size: size of data being read or written @@ -183,7 +203,8 @@ typedef enum uc_hook_type { typedef void (*uc_cb_hookmem_t)(uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, void *user_data); -// Callback function for handling memory events (for UC_HOOK_MEM_INVALID) +// Callback function for handling invalid memory access events (UC_MEM_*_INVALID and +// UC_MEM_*PROT events) // @type: this memory is being READ, or WRITE // @address: address where the code is being executed // @size: size of data being read or written @@ -380,7 +401,7 @@ uc_err uc_emu_stop(uc_engine *uc); for detailed error). */ UNICORN_EXPORT -uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, uc_hook_type type, void *callback, void *user_data, ...); +uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, ...); /* Unregister (remove) a hook callback. diff --git a/qemu/cputlb.c b/qemu/cputlb.c index 30a0727b..b9b16e30 100644 --- a/qemu/cputlb.c +++ b/qemu/cputlb.c @@ -300,7 +300,7 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr) (addr & TARGET_PAGE_MASK))) { cpu_ldub_code(env1, addr); //check for NX related error from softmmu - if (env1->invalid_error == UC_ERR_EXEC_PROT) { + if (env1->invalid_error == UC_ERR_FETCH_PROT) { env1->invalid_error = UC_ERR_CODE_INVALID; return -1; } diff --git a/qemu/hw/sparc64/sun4u.c b/qemu/hw/sparc64/sun4u.c index ccfea62e..7377a023 100644 --- a/qemu/hw/sparc64/sun4u.c +++ b/qemu/hw/sparc64/sun4u.c @@ -33,11 +33,13 @@ static void sun4u_init(struct uc_struct *uc, MachineState *machine) { const char *cpu_model = machine->cpu_model; + SPARCCPU *cpu; if (cpu_model == NULL) - cpu_model = "sun4uv"; + cpu_model = "Sun UltraSparc IV"; - if (cpu_sparc_init(uc, cpu_model) == NULL) { + cpu = cpu_sparc_init(uc, cpu_model); + if (cpu == NULL) { fprintf(stderr, "Unable to find Sparc CPU definition\n"); exit(1); } @@ -45,7 +47,7 @@ static void sun4u_init(struct uc_struct *uc, MachineState *machine) void sun4u_machine_init(struct uc_struct *uc) { - QEMUMachine sun4u_machine = { + static QEMUMachine sun4u_machine = { .name = "sun4u", .init = sun4u_init, .max_cpus = 1, // XXX for now diff --git a/qemu/softmmu_template.h b/qemu/softmmu_template.h index 9e8afd46..9730c840 100644 --- a/qemu/softmmu_template.h +++ b/qemu/softmmu_template.h @@ -177,7 +177,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; uintptr_t haddr; DATA_TYPE res; - int mem_access, error_code; + int error_code; struct uc_struct *uc = env->uc; MemoryRegion *mr = memory_mapping(uc, addr); @@ -185,15 +185,16 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, // memory can be unmapped while reading or fetching if (mr == NULL) { #if defined(SOFTMMU_CODE_ACCESS) - mem_access = UC_MEM_FETCH; error_code = UC_ERR_FETCH_INVALID; + if (uc->hook_mem_fetch_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_fetch_idx].callback)( + uc, UC_MEM_FETCH_INVALID, addr, DATA_SIZE, 0, + uc->hook_callbacks[uc->hook_mem_fetch_idx].user_data)) { #else - mem_access = UC_MEM_READ; error_code = UC_ERR_READ_INVALID; + if (uc->hook_mem_read_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_read_idx].callback)( + uc, UC_MEM_READ_INVALID, addr, DATA_SIZE, 0, + uc->hook_callbacks[uc->hook_mem_read_idx].user_data)) { #endif - if (uc->hook_mem_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( - uc, mem_access, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_idx].user_data)) { env->invalid_error = UC_ERR_OK; mr = memory_mapping(uc, addr); // FIXME: what if mr is still NULL at this time? } else { @@ -208,13 +209,13 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, #if defined(SOFTMMU_CODE_ACCESS) // Unicorn: callback on fetch from NX if (mr != NULL && !(mr->perms & UC_PROT_EXEC)) { // non-executable - if (uc->hook_mem_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( - uc, UC_MEM_EXEC_PROT, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_idx].user_data)) { + if (uc->hook_mem_fetch_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].callback)( + uc, UC_MEM_FETCH_PROT, addr, DATA_SIZE, 0, + uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].user_data)) { env->invalid_error = UC_ERR_OK; } else { env->invalid_addr = addr; - env->invalid_error = UC_ERR_EXEC_PROT; + env->invalid_error = UC_ERR_FETCH_PROT; // printf("***** Invalid fetch (non-executable) at " TARGET_FMT_lx "\n", addr); cpu_exit(uc->current_cpu); return 0; @@ -233,9 +234,9 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, // Unicorn: callback on non-readable memory if (READ_ACCESS_TYPE == MMU_DATA_LOAD && mr != NULL && !(mr->perms & UC_PROT_READ)) { //non-readable - if (uc->hook_mem_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( + if (uc->hook_mem_read_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_read_prot_idx].callback)( uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_idx].user_data)) { + uc->hook_callbacks[uc->hook_mem_read_prot_idx].user_data)) { env->invalid_error = UC_ERR_OK; } else { @@ -367,7 +368,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; uintptr_t haddr; DATA_TYPE res; - int mem_access, error_code; + int error_code; struct uc_struct *uc = env->uc; MemoryRegion *mr = memory_mapping(uc, addr); @@ -375,15 +376,16 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, // memory can be unmapped while reading or fetching if (mr == NULL) { #if defined(SOFTMMU_CODE_ACCESS) - mem_access = UC_MEM_FETCH; error_code = UC_ERR_FETCH_INVALID; + if (uc->hook_mem_fetch_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_fetch_idx].callback)( + uc, UC_MEM_FETCH_INVALID, addr, DATA_SIZE, 0, + uc->hook_callbacks[uc->hook_mem_fetch_idx].user_data)) { #else - mem_access = UC_MEM_READ; error_code = UC_ERR_READ_INVALID; + if (uc->hook_mem_read_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_read_idx].callback)( + uc, UC_MEM_READ_INVALID, addr, DATA_SIZE, 0, + uc->hook_callbacks[uc->hook_mem_read_idx].user_data)) { #endif - if (uc->hook_mem_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( - uc, mem_access, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_idx].user_data)) { env->invalid_error = UC_ERR_OK; mr = memory_mapping(uc, addr); // FIXME: what if mr is still NULL at this time? } else { @@ -398,13 +400,13 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, #if defined(SOFTMMU_CODE_ACCESS) // Unicorn: callback on fetch from NX if (mr != NULL && !(mr->perms & UC_PROT_EXEC)) { // non-executable - if (uc->hook_mem_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( - uc, UC_MEM_EXEC_PROT, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_idx].user_data)) { + if (uc->hook_mem_fetch_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].callback)( + uc, UC_MEM_FETCH_PROT, addr, DATA_SIZE, 0, + uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].user_data)) { env->invalid_error = UC_ERR_OK; } else { env->invalid_addr = addr; - env->invalid_error = UC_ERR_EXEC_PROT; + env->invalid_error = UC_ERR_FETCH_PROT; // printf("***** Invalid fetch (non-executable) at " TARGET_FMT_lx "\n", addr); cpu_exit(uc->current_cpu); return 0; @@ -423,9 +425,9 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, // Unicorn: callback on non-readable memory if (READ_ACCESS_TYPE == MMU_DATA_LOAD && mr != NULL && !(mr->perms & UC_PROT_READ)) { //non-readable - if (uc->hook_mem_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( + if (uc->hook_mem_read_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_read_prot_idx].callback)( uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_idx].user_data)) { + uc->hook_callbacks[uc->hook_mem_read_prot_idx].user_data)) { env->invalid_error = UC_ERR_OK; } else { env->invalid_addr = addr; @@ -608,10 +610,10 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, } // Unicorn: callback on invalid memory - if (uc->hook_mem_idx && mr == NULL) { - if (!((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( - uc, UC_MEM_WRITE, addr, DATA_SIZE, (int64_t)val, - uc->hook_callbacks[uc->hook_mem_idx].user_data)) { + if (uc->hook_mem_write_idx && mr == NULL) { + if (!((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_idx].callback)( + uc, UC_MEM_WRITE_INVALID, addr, DATA_SIZE, (int64_t)val, + uc->hook_callbacks[uc->hook_mem_write_idx].user_data)) { // save error & quit env->invalid_addr = addr; env->invalid_error = UC_ERR_WRITE_INVALID; @@ -620,14 +622,15 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, return; } else { env->invalid_error = UC_ERR_OK; + mr = memory_mapping(uc, addr); // FIXME: what if mr is still NULL at this time? } } // Unicorn: callback on non-writable memory if (mr != NULL && !(mr->perms & UC_PROT_WRITE)) { //non-writable - if (uc->hook_mem_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( + if (uc->hook_mem_write_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_prot_idx].callback)( uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, (int64_t)val, - uc->hook_callbacks[uc->hook_mem_idx].user_data)) { + uc->hook_callbacks[uc->hook_mem_write_prot_idx].user_data)) { env->invalid_error = UC_ERR_OK; } else { @@ -754,10 +757,10 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, } // Unicorn: callback on invalid memory - if (uc->hook_mem_idx && mr == NULL) { - if (!((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( - uc, UC_MEM_WRITE, addr, DATA_SIZE, (int64_t)val, - uc->hook_callbacks[uc->hook_mem_idx].user_data)) { + if (uc->hook_mem_write_idx && mr == NULL) { + if (!((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_idx].callback)( + uc, UC_MEM_WRITE_INVALID, addr, DATA_SIZE, (int64_t)val, + uc->hook_callbacks[uc->hook_mem_write_idx].user_data)) { // save error & quit env->invalid_addr = addr; env->invalid_error = UC_ERR_WRITE_INVALID; @@ -766,14 +769,15 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, return; } else { env->invalid_error = UC_ERR_OK; + mr = memory_mapping(uc, addr); // FIXME: what if mr is still NULL at this time? } } // Unicorn: callback on non-writable memory if (mr != NULL && !(mr->perms & UC_PROT_WRITE)) { //non-writable - if (uc->hook_mem_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( + if (uc->hook_mem_write_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_prot_idx].callback)( uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, (int64_t)val, - uc->hook_callbacks[uc->hook_mem_idx].user_data)) { + uc->hook_callbacks[uc->hook_mem_write_prot_idx].user_data)) { env->invalid_error = UC_ERR_OK; } else { diff --git a/qemu/target-arm/translate-a64.c b/qemu/target-arm/translate-a64.c index 5dda2eba..d081fc49 100644 --- a/qemu/target-arm/translate-a64.c +++ b/qemu/target-arm/translate-a64.c @@ -11105,6 +11105,13 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, tcg_clear_temp_count(); + // Unicorn: early check to see if the address of this block is the until address + if (tb->pc == env->uc->addr_end) { + gen_tb_start(tcg_ctx); + gen_exception_insn(dc, 0, EXCP_SWI, 0); + goto done_generating; + } + // Unicorn: trace this block on request // Only hook this block if it is not broken from previous translation due to // full translation cache diff --git a/qemu/target-arm/translate.c b/qemu/target-arm/translate.c index a3829ca2..8875b641 100644 --- a/qemu/target-arm/translate.c +++ b/qemu/target-arm/translate.c @@ -11228,6 +11228,13 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, tcg_clear_temp_count(); + // Unicorn: early check to see if the address of this block is the until address + if (tb->pc == env->uc->addr_end) { + gen_tb_start(tcg_ctx); + gen_exception_insn(dc, 0, EXCP_SWI, 0); + goto done_generating; + } + // Unicorn: trace this block on request // Only hook this block if it is not broken from previous translation due to // full translation cache diff --git a/qemu/target-m68k/translate.c b/qemu/target-m68k/translate.c index 133957df..fedffecc 100644 --- a/qemu/target-m68k/translate.c +++ b/qemu/target-m68k/translate.c @@ -3101,6 +3101,13 @@ gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb, if (max_insns == 0) max_insns = CF_COUNT_MASK; + // Unicorn: early check to see if the address of this block is the until address + if (tb->pc == env->uc->addr_end) { + gen_tb_start(tcg_ctx); + gen_exception(dc, dc->pc, EXCP_TRAP15); + goto done_generating; + } + // Unicorn: trace this block on request // Only hook this block if it is not broken from previous translation due to // full translation cache @@ -3179,6 +3186,8 @@ gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb, break; } } + +done_generating: gen_tb_end(tcg_ctx, tb, num_insns); *tcg_ctx->gen_opc_ptr = INDEX_op_end; diff --git a/qemu/target-mips/translate.c b/qemu/target-mips/translate.c index 718d69aa..e1ff51a7 100644 --- a/qemu/target-mips/translate.c +++ b/qemu/target-mips/translate.c @@ -1415,7 +1415,7 @@ enum { * exception condition */ BS_STOP = 1, /* We want to stop translation for any reason */ BS_BRANCH = 2, /* We reached a branch condition */ - BS_EXCP = 3, /* We reached an exception condition */ // qq + BS_EXCP = 3, /* We reached an exception condition */ }; static const char * const regnames[] = { @@ -11322,7 +11322,7 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx) return 4; } -static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) +static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, int is_slot) { TCGContext *tcg_ctx = ctx->uc->tcg_ctx; TCGv **cpu_gpr = (TCGv **)tcg_ctx->cpu_gpr; @@ -11343,7 +11343,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) n_bytes = 2; // Unicorn: trace this instruction on request - if (env->uc->hook_insn) { + if (!is_slot && env->uc->hook_insn) { struct hook_struct *trace = hook_find(env->uc, UC_HOOK_CODE, ctx->pc); if (trace) gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, ctx->pc, trace->user_data); @@ -13928,7 +13928,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, } } -static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) +static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int is_slot) { TCGContext *tcg_ctx = env->uc->tcg_ctx; TCGv **cpu_gpr = (TCGv **)tcg_ctx->cpu_gpr; @@ -13943,7 +13943,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) } // Unicorn: trace this instruction on request - if (env->uc->hook_insn) { + if (!is_slot && env->uc->hook_insn) { struct hook_struct *trace = hook_find(env->uc, UC_HOOK_CODE, ctx->pc); if (trace) gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, ctx->pc, trace->user_data); @@ -18503,7 +18503,7 @@ static void gen_msa(CPUMIPSState *env, DisasContext *ctx) } } -static void decode_opc (CPUMIPSState *env, DisasContext *ctx) +static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int is_slot) { TCGContext *tcg_ctx = ctx->uc->tcg_ctx; #if defined(TARGET_MIPS64) @@ -18514,6 +18514,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) uint32_t op, op1; int16_t imm; + /* make sure instructions are on a word boundary */ if (ctx->pc & 0x3) { env->CP0_BadVAddr = ctx->pc; @@ -18522,7 +18523,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) } // Unicorn: trace this instruction on request - if (env->uc->hook_insn) { + if (!is_slot && env->uc->hook_insn) { struct hook_struct *trace = hook_find(env->uc, UC_HOOK_CODE, ctx->pc); if (trace) gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, ctx->pc, trace->user_data); @@ -19207,6 +19208,13 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, max_insns = CF_COUNT_MASK; LOG_DISAS("\ntb %p idx %d hflags %04x\n", tb, ctx.mem_idx, ctx.hflags); + // Unicorn: early check to see if the address of this block is the until address + if (tb->pc == env->uc->addr_end) { + gen_tb_start(tcg_ctx); + generate_exception(&ctx, EXCP_SYSCALL); + goto done_generating; + } + // Unicorn: trace this block on request // Only hook this block if it is not broken from previous translation due to // full translation cache @@ -19220,7 +19228,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, } gen_tb_start(tcg_ctx); - while (ctx.bstate == BS_NONE) { // qq + while (ctx.bstate == BS_NONE) { if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { if (bp->pc == ctx.pc) { @@ -19261,16 +19269,16 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, save_opparam_ptr = tcg_ctx->gen_opparam_ptr; is_slot = ctx.hflags & MIPS_HFLAG_BMASK; - if (!(ctx.hflags & MIPS_HFLAG_M16)) { // qq + if (!(ctx.hflags & MIPS_HFLAG_M16)) { ctx.opcode = cpu_ldl_code(env, ctx.pc); insn_bytes = 4; - decode_opc(env, &ctx); - } else if (ctx.insn_flags & ASE_MICROMIPS) { // qq + decode_opc(env, &ctx, is_slot); + } else if (ctx.insn_flags & ASE_MICROMIPS) { ctx.opcode = cpu_lduw_code(env, ctx.pc); - insn_bytes = decode_micromips_opc(env, &ctx); - } else if (ctx.insn_flags & ASE_MIPS16) { // qq + insn_bytes = decode_micromips_opc(env, &ctx, is_slot); + } else if (ctx.insn_flags & ASE_MIPS16) { ctx.opcode = cpu_lduw_code(env, ctx.pc); - insn_bytes = decode_mips16_opc(env, &ctx); + insn_bytes = decode_mips16_opc(env, &ctx, is_slot); } else { generate_exception(&ctx, EXCP_RI); ctx.bstate = BS_STOP; diff --git a/qemu/target-sparc/unicorn.c b/qemu/target-sparc/unicorn.c index 9f00f340..d86497b5 100644 --- a/qemu/target-sparc/unicorn.c +++ b/qemu/target-sparc/unicorn.c @@ -42,6 +42,7 @@ void sparc_reg_reset(struct uc_struct *uc) env->pc = 0; env->npc = 0; + env->regwptr = env->regbase; } int sparc_reg_read(struct uc_struct *uc, unsigned int regid, void *value) @@ -50,41 +51,46 @@ int sparc_reg_read(struct uc_struct *uc, unsigned int regid, void *value) if (regid >= UC_SPARC_REG_G0 && regid <= UC_SPARC_REG_G7) *(int32_t *)value = SPARC_CPU(uc, mycpu)->env.gregs[regid - UC_SPARC_REG_G0]; + else if (regid >= UC_SPARC_REG_O0 && regid <= UC_SPARC_REG_O7) + *(int32_t *)value = SPARC_CPU(uc, mycpu)->env.regwptr[regid - UC_SPARC_REG_O0]; + else if (regid >= UC_SPARC_REG_L0 && regid <= UC_SPARC_REG_L7) + *(int32_t *)value = SPARC_CPU(uc, mycpu)->env.regwptr[8 + regid - UC_SPARC_REG_L0]; + else if (regid >= UC_SPARC_REG_I0 && regid <= UC_SPARC_REG_I7) + *(int32_t *)value = SPARC_CPU(uc, mycpu)->env.regwptr[16 + regid - UC_SPARC_REG_I0]; else { switch(regid) { default: break; case UC_SPARC_REG_PC: - *(int32_t *)value = SPARC_CPU(uc, mycpu)->env.pc; - break; + *(int32_t *)value = SPARC_CPU(uc, mycpu)->env.pc; + break; } } return 0; } - -#define WRITE_DWORD(x, w) (x = (x & ~0xffffffff) | (w & 0xffffffff)) -#define WRITE_WORD(x, w) (x = (x & ~0xffff) | (w & 0xffff)) -#define WRITE_BYTE_H(x, b) (x = (x & ~0xff00) | (b & 0xff)) -#define WRITE_BYTE_L(x, b) (x = (x & ~0xff) | (b & 0xff)) - int sparc_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) { CPUState *mycpu = first_cpu; if (regid >= UC_SPARC_REG_G0 && regid <= UC_SPARC_REG_G7) SPARC_CPU(uc, mycpu)->env.gregs[regid - UC_SPARC_REG_G0] = *(uint32_t *)value; + else if (regid >= UC_SPARC_REG_O0 && regid <= UC_SPARC_REG_O7) + SPARC_CPU(uc, mycpu)->env.regwptr[regid - UC_SPARC_REG_O0] = *(uint32_t *)value; + else if (regid >= UC_SPARC_REG_L0 && regid <= UC_SPARC_REG_L7) + SPARC_CPU(uc, mycpu)->env.regwptr[8 + regid - UC_SPARC_REG_L0] = *(uint32_t *)value; + else if (regid >= UC_SPARC_REG_I0 && regid <= UC_SPARC_REG_I7) + SPARC_CPU(uc, mycpu)->env.regwptr[16 + regid - UC_SPARC_REG_I0] = *(uint32_t *)value; else { switch(regid) { default: break; case UC_SPARC_REG_PC: - SPARC_CPU(uc, mycpu)->env.pc = *(uint32_t *)value; - SPARC_CPU(uc, mycpu)->env.npc = *(uint32_t *)value + 4; - break; + SPARC_CPU(uc, mycpu)->env.pc = *(uint32_t *)value; + SPARC_CPU(uc, mycpu)->env.npc = *(uint32_t *)value + 4; + break; } } - return 0; } diff --git a/qemu/target-sparc/unicorn64.c b/qemu/target-sparc/unicorn64.c index aefef116..e9257b75 100644 --- a/qemu/target-sparc/unicorn64.c +++ b/qemu/target-sparc/unicorn64.c @@ -6,6 +6,7 @@ #include "sysemu/cpus.h" #include "unicorn.h" #include "cpu.h" +#include "unicorn_common.h" #define READ_QWORD(x) ((uint64)x) @@ -15,6 +16,22 @@ #define READ_BYTE_L(x) (x & 0xff) +static bool sparc_stop_interrupt(int intno) +{ + switch(intno) { + default: + return false; + case TT_ILL_INSN: + return true; + } +} + +static void sparc_set_pc(struct uc_struct *uc, uint64_t address) +{ + ((CPUSPARCState *)uc->current_cpu->env_ptr)->pc = address; + ((CPUSPARCState *)uc->current_cpu->env_ptr)->npc = address + 4; +} + void sparc_reg_reset(struct uc_struct *uc) { CPUArchState *env = first_cpu->env_ptr; @@ -25,6 +42,7 @@ void sparc_reg_reset(struct uc_struct *uc) env->pc = 0; env->npc = 0; + env->regwptr = env->regbase; } int sparc_reg_read(struct uc_struct *uc, unsigned int regid, void *value) @@ -32,38 +50,44 @@ int sparc_reg_read(struct uc_struct *uc, unsigned int regid, void *value) CPUState *mycpu = first_cpu; if (regid >= UC_SPARC_REG_G0 && regid <= UC_SPARC_REG_G7) - *(int32_t *)value = SPARC_CPU(uc, mycpu)->env.gregs[regid - UC_SPARC_REG_G0]; + *(int64_t *)value = SPARC_CPU(uc, mycpu)->env.gregs[regid - UC_SPARC_REG_G0]; + else if (regid >= UC_SPARC_REG_O0 && regid <= UC_SPARC_REG_O7) + *(int64_t *)value = SPARC_CPU(uc, mycpu)->env.regwptr[regid - UC_SPARC_REG_O0]; + else if (regid >= UC_SPARC_REG_L0 && regid <= UC_SPARC_REG_L7) + *(int64_t *)value = SPARC_CPU(uc, mycpu)->env.regwptr[8 + regid - UC_SPARC_REG_L0]; + else if (regid >= UC_SPARC_REG_I0 && regid <= UC_SPARC_REG_I7) + *(int64_t *)value = SPARC_CPU(uc, mycpu)->env.regwptr[16 + regid - UC_SPARC_REG_I0]; else { switch(regid) { default: break; case UC_SPARC_REG_PC: - *(int32_t *)value = SPARC_CPU(uc, mycpu)->env.pc; - break; + *(int64_t *)value = SPARC_CPU(uc, mycpu)->env.pc; + break; } } return 0; } - -#define WRITE_DWORD(x, w) (x = (x & ~0xffffffff) | (w & 0xffffffff)) -#define WRITE_WORD(x, w) (x = (x & ~0xffff) | (w & 0xffff)) -#define WRITE_BYTE_H(x, b) (x = (x & ~0xff00) | (b & 0xff)) -#define WRITE_BYTE_L(x, b) (x = (x & ~0xff) | (b & 0xff)) - int sparc_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) { CPUState *mycpu = first_cpu; if (regid >= UC_SPARC_REG_G0 && regid <= UC_SPARC_REG_G7) - SPARC_CPU(uc, mycpu)->env.gregs[regid - UC_SPARC_REG_G0] = *(uint32_t *)value; + SPARC_CPU(uc, mycpu)->env.gregs[regid - UC_SPARC_REG_G0] = *(uint64_t *)value; + else if (regid >= UC_SPARC_REG_O0 && regid <= UC_SPARC_REG_O7) + SPARC_CPU(uc, mycpu)->env.regwptr[regid - UC_SPARC_REG_O0] = *(uint64_t *)value; + else if (regid >= UC_SPARC_REG_L0 && regid <= UC_SPARC_REG_L7) + SPARC_CPU(uc, mycpu)->env.regwptr[8 + regid - UC_SPARC_REG_L0] = *(uint64_t *)value; + else if (regid >= UC_SPARC_REG_I0 && regid <= UC_SPARC_REG_I7) + SPARC_CPU(uc, mycpu)->env.regwptr[16 + regid - UC_SPARC_REG_I0] = *(uint64_t *)value; else { switch(regid) { default: break; case UC_SPARC_REG_PC: - SPARC_CPU(uc, mycpu)->env.pc = *(uint32_t *)value; - SPARC_CPU(uc, mycpu)->env.npc = *(uint32_t *)value + 4; - break; + SPARC_CPU(uc, mycpu)->env.pc = *(uint64_t *)value; + SPARC_CPU(uc, mycpu)->env.npc = *(uint64_t *)value + 4; + break; } } @@ -80,4 +104,7 @@ void sparc64_uc_init(struct uc_struct* uc) uc->reg_read = sparc_reg_read; uc->reg_write = sparc_reg_write; uc->reg_reset = sparc_reg_reset; + uc->set_pc = sparc_set_pc; + uc->stop_interrupt = sparc_stop_interrupt; + uc_common_init(uc); } diff --git a/qemu/tcg/tcg.c b/qemu/tcg/tcg.c index 4246504c..53460c63 100644 --- a/qemu/tcg/tcg.c +++ b/qemu/tcg/tcg.c @@ -1431,7 +1431,6 @@ static inline void tcg_la_func_end(TCGContext *s, uint8_t *dead_temps, memset(mem_temps + s->nb_globals, 0, s->nb_temps - s->nb_globals); } -#if 0 /* liveness analysis: end of basic block: all temps are dead, globals and local temps should be in memory. */ static inline void tcg_la_bb_end(TCGContext *s, uint8_t *dead_temps, @@ -1445,7 +1444,6 @@ static inline void tcg_la_bb_end(TCGContext *s, uint8_t *dead_temps, mem_temps[i] = s->temps[i].temp_local; } } -#endif /* Liveness analysis : update the opc_dead_args array to tell if a given input arguments is dead. Instructions updating dead @@ -1684,12 +1682,12 @@ static void tcg_liveness_analysis(TCGContext *s) } /* if end of basic block, update */ - if (def->flags & TCG_OPF_BB_END) { - // Unicorn: do not optimize dead temps. + if (def->flags & TCG_OPF_BB_END && op != INDEX_op_brcond_i32) { + // Unicorn: do not optimize dead temps on brcond, // this causes problem because check_exit_request() inserts // brcond instruction in the middle of the TB, // which incorrectly flags end-of-block - // tcg_la_bb_end(s, dead_temps, mem_temps); + tcg_la_bb_end(s, dead_temps, mem_temps); } else if (def->flags & TCG_OPF_SIDE_EFFECTS) { /* globals should be synced to memory */ memset(mem_temps, 1, s->nb_globals); diff --git a/qemu/translate-all.c b/qemu/translate-all.c index aaebee42..8f1c43eb 100644 --- a/qemu/translate-all.c +++ b/qemu/translate-all.c @@ -1073,7 +1073,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGContext *tcg_ctx = env->uc->tcg_ctx; TranslationBlock *tb; tb_page_addr_t phys_pc, phys_page2; - target_ulong virt_page2; int code_gen_size; phys_pc = get_page_addr_code(env, pc); @@ -1094,11 +1093,13 @@ TranslationBlock *tb_gen_code(CPUState *cpu, tcg_ctx->code_gen_ptr = (void *)(((uintptr_t)tcg_ctx->code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1)); - /* check next page if needed */ - virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK; phys_page2 = -1; - if ((pc & TARGET_PAGE_MASK) != virt_page2) { - phys_page2 = get_page_addr_code(env, virt_page2); + /* check next page if needed */ + if (tb->size) { + target_ulong virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK; + if ((pc & TARGET_PAGE_MASK) != virt_page2) { + phys_page2 = get_page_addr_code(env, virt_page2); + } } tb_link_page(cpu->uc, tb, phys_pc, phys_page2); return tb; diff --git a/regress/arm_bxeq_hang.py b/regress/arm_bxeq_hang.py deleted file mode 100755 index 0e4a6116..00000000 --- a/regress/arm_bxeq_hang.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/python - -from unicorn import * -from unicorn.arm_const import * - -uc = Uc(UC_ARCH_ARM, UC_MODE_ARM) -uc.mem_map(0x1000, 0x1000) -uc.mem_write(0x1000, '1eff2f010000a0e1'.decode('hex')) -def hook_block(uc, addr, *args): - print 'enter block 0x%04x' % addr - -uc.reg_write(UC_ARM_REG_LR, 0x1004) -uc.hook_add(UC_HOOK_BLOCK, hook_block) -print 'block should only run once' -uc.emu_start(0x1000, 0x1004, timeout=250) diff --git a/regress/arm_movr12_hang.py b/regress/arm_movr12_hang.py deleted file mode 100755 index 8847694c..00000000 --- a/regress/arm_movr12_hang.py +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/python - -from unicorn import * -from unicorn.arm_const import * - -uc = Uc(UC_ARCH_ARM, UC_MODE_ARM) -uc.mem_map(0x1000, 0x1000) -uc.mem_write(0x1000, '00c000e3'.decode('hex')) -def hook_block(uc, addr, *args): - print 'enter block 0x%04x' % addr - -uc.reg_write(UC_ARM_REG_R12, 0x123) -print 'r12 =', uc.reg_read(UC_ARM_REG_R12) -uc.hook_add(UC_HOOK_BLOCK, hook_block) -print 'block should only run once' -uc.emu_start(0x1000, 0x1004, timeout=250) -print 'r12 =', uc.reg_read(UC_ARM_REG_R12) diff --git a/regress/callback-pc.py b/regress/callback-pc.py deleted file mode 100755 index be35a244..00000000 --- a/regress/callback-pc.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python - -# reg_write() can't modify PC from within trace callbacks - -from __future__ import print_function -from unicorn import * -from unicorn.arm_const import * - -BASE_ADDRESS = 0x10000000 - -# sub sp, #0xc -THUMB_CODE = "\x83\xb0" * 5 - -# callback for tracing instructions -def hook_code(uc, address, size, user_data): - print(">>> Tracing instruction at 0x%x, instruction size = %u" % (address, size)) - mu = user_data - print(">>> Setting PC to 0xffffffff") - mu.reg_write(ARM_REG_PC, 0xffffffff) - -# callback for tracing basic blocks -def hook_block(uc, address, size, user_data): - print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) - mu = user_data - print(">>> Setting PC to 0xffffffff") - mu.reg_write(ARM_REG_PC, 0xffffffff) - -# set up emulation -def instruction_trace_test(): - try: - # initialize emulator in ARM's Thumb mode - mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB) - - # map some memory - mu.mem_map(BASE_ADDRESS, 2 * 1024 * 1024) - - # write machine code to be emulated to memory - mu.mem_write(BASE_ADDRESS, THUMB_CODE) - - # setup stack - mu.reg_write(UC_ARM_REG_SP, BASE_ADDRESS + 2 * 1024 * 1024) - - # tracing all instructions with customized callback - mu.hook_add(UC_HOOK_CODE, hook_code, user_data=mu) - - # tracing all basic blocks with customized callback - mu.hook_add(UC_HOOK_BLOCK, hook_block, user_data=mu) - - # emulate machine code in infinite time - mu.emu_start(BASE_ADDRESS, BASE_ADDRESS + len(THUMB_CODE)) - - except UcError as e: - print("ERROR: %s" % e) - -if __name__ == '__main__': - instruction_trace_test() diff --git a/regress/crash_tb.py b/regress/crash_tb.py deleted file mode 100755 index d4f99723..00000000 --- a/regress/crash_tb.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/python - -from unicorn import * -from unicorn.x86_const import * - - -CODE_ADDR = 0x0 - - -binary1 = b'\xb8\x02\x00\x00\x00' -binary2 = b'\xb8\x01\x00\x00\x00' - -mu = Uc(UC_ARCH_X86, UC_MODE_64) - -mu.mem_map(CODE_ADDR, 2 * 1024 * 1024) - -# write machine code to be emulated to memory -mu.mem_write(CODE_ADDR, binary1) - -# emu for maximum 1 sec. -mu.emu_start(CODE_ADDR, len(binary1), UC_SECOND_SCALE) - -print("RAX = %x" %mu.reg_read(UC_X86_REG_RAX)) - -# write machine code to be emulated to memory -mu.mem_write(CODE_ADDR, binary2) - -# emu for maximum 1 sec. -mu.emu_start(CODE_ADDR, len(binary2), UC_SECOND_SCALE) - -print("RAX = %x" %mu.reg_read(UC_X86_REG_RAX)) - diff --git a/regress/deadlock_1.py b/regress/deadlock_1.py deleted file mode 100755 index 702376a3..00000000 --- a/regress/deadlock_1.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/python -# From issue #1 of Ryan Hileman - -from unicorn import * - -CODE = b"\x90\x91\x92" - -mu = Uc(UC_ARCH_X86, UC_MODE_64) -mu.mem_map(0x100000, 4 * 1024) -mu.mem_write(0x100000, CODE) -mu.emu_start(0x100000, 0x1000 + len(CODE)) diff --git a/regress/emu_stop_segfault.py b/regress/emu_stop_segfault.py deleted file mode 100755 index 8021d86d..00000000 --- a/regress/emu_stop_segfault.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/python - -"""See https://github.com/unicorn-engine/unicorn/issues/65""" - -import unicorn -ADDR = 0x10101000 -mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) -mu.mem_map(ADDR, 1024 * 4) -mu.mem_write(ADDR, b'\x41') -mu.emu_start(ADDR, ADDR + 1, count=1) -# The following should not trigger a null pointer dereference -mu.emu_stop() - diff --git a/regress/fpu_ip.py b/regress/fpu_ip.py deleted file mode 100755 index 77efa21b..00000000 --- a/regress/fpu_ip.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/python -from unicorn import * -from unicorn.x86_const import * -from capstone import * - -ESP = 0x2000 -PAGE_SIZE = 2 * 1024 * 1024 - -# mov [esp], DWORD 0x37f -# fldcw [esp] -# fnop -# fnstenv [esp + 8] -# pop ecx -CODE = b'\xc7\x04\x24\x7f\x03\x00\x00\xd9\x2c\x24\xd9\xd0\xd9\x74\x24\x08\x59' - -class SimpleEngine: - def __init__(self): - self.capmd = Cs(CS_ARCH_X86, CS_MODE_32) - - def disas_single(self, data): - for i in self.capmd.disasm(data, 16): - print("\t%s\t%s" % (i.mnemonic, i.op_str)) - break - -disasm = SimpleEngine() - -def hook_code(uc, addr, size, user_data): - mem = uc.mem_read(addr, size) - print(" 0x%X:" % (addr)), - disasm.disas_single(str(mem)) - -def mem_reader(addr, size): - tmp = mu.mem_read(addr, size) - - for i in tmp: - print(" 0x%x" % i), - print("") - - -mu = Uc(UC_ARCH_X86, UC_MODE_32) - -mu.mem_map(0x0, PAGE_SIZE) -mu.mem_write(0x4000, CODE) -mu.reg_write(UC_X86_REG_ESP, ESP) -mu.hook_add(UC_HOOK_CODE, hook_code) - - -mu.emu_start(0x4000, 0, 0, 5) -esp = mu.reg_read(UC_X86_REG_ESP) -print("value at ESP [0x%X - 4]: " % esp) -mem_reader(esp + 14, 4) - -# EXPECTED OUTPUT: -# 0x4000: mov dword ptr [esp], 0x37f -# 0x4007: fldcw word ptr [esp] -# 0x400A: fnop -# 0x400C: fnstenv dword ptr [esp + 8] -# 0x4010: pop ecx -# value at ESP [0x2004 - 4]: -# 0x0 0x0 0xa 0x40 -# ^ this value should match the fnop instuction addr \ No newline at end of file diff --git a/regress/fpu_ip64.py b/regress/fpu_ip64.py deleted file mode 100755 index dd0b5c1c..00000000 --- a/regress/fpu_ip64.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/python -from unicorn import * -from unicorn.x86_const import * -from capstone import * - -ESP = 0x2000 -PAGE_SIZE = 2 * 1024 * 1024 - -# mov [esp], DWORD 0x37f -# fldcw [esp] -# fnop -# fnstenv [esp + 8] -# pop ecx -CODE = "C704247F030000D92C24D9D0D974240859".decode('hex') - -class SimpleEngine: - def __init__(self): - self.capmd = Cs(CS_ARCH_X86, CS_MODE_64) - - def disas_single(self, data): - for i in self.capmd.disasm(data, 16): - print("\t%s\t%s" % (i.mnemonic, i.op_str)) - break - -disasm = SimpleEngine() - -def hook_code(uc, addr, size, user_data): - mem = uc.mem_read(addr, size) - print(" 0x%X:" % (addr)), - disasm.disas_single(str(mem)) - -def mem_reader(addr, size): - tmp = mu.mem_read(addr, size) - - for i in tmp: - print(" 0x%x" % i), - print("") - - -mu = Uc(UC_ARCH_X86, UC_MODE_64) - -mu.mem_map(0x0, PAGE_SIZE) -mu.mem_write(0x4000, CODE) -mu.reg_write(UC_X86_REG_RSP, ESP) -mu.hook_add(UC_HOOK_CODE, hook_code) - - -mu.emu_start(0x4000, 0, 0, 5) -rsp = mu.reg_read(UC_X86_REG_RSP) -print("Value of FPIP: [0x%X]" % (rsp + 10)) -mem_reader(rsp + 10, 8) -# EXPECTED OUTPUT: - -# 0x4000: mov dword ptr [rsp], 0x37f -# 0x4007: fldcw word ptr [rsp] -# 0x400A: fnop -# 0x400C: fnstenv dword ptr [rsp + 8] -# 0x4010: pop rcx -# Value of FPIP: [0x2012] -# 0x0 0x0 0xa 0x40 0x0 0x0 0x0 0x0 - -# WHERE: the value of FPIP should be the address of fnop \ No newline at end of file diff --git a/regress/fpu_mem_write.py b/regress/fpu_mem_write.py deleted file mode 100755 index 730914a6..00000000 --- a/regress/fpu_mem_write.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/python -from unicorn import * -from unicorn.x86_const import * - -ESP = 0x2000 -PAGE_SIZE = 1 * 1024 * 1024 - -# fstcw [esp] -# pop ecx -CODE = b'\x9B\xD9\x3C\x24\x59' - -def mem_reader(addr, size): - tmp = mu.mem_read(addr, size) - - for i in tmp: - print(" 0x%x" % i), - print("") - -def hook_mem_write(uc, access, address, size, value, user_data): - print("mem WRITE: 0x%x, data size = %u, data value = 0x%x" % (address, size, value)) - return True - -mu = Uc(UC_ARCH_X86, UC_MODE_32) -mu.mem_map(0, PAGE_SIZE) -mu.mem_write(0, CODE) -mu.reg_write(UC_X86_REG_ESP, ESP) -mu.hook_add(UC_HOOK_MEM_WRITE, hook_mem_write) - -mu.emu_start(0x0, 0, 0, 2) -esp = mu.reg_read(UC_X86_REG_ESP) -print("value at ESP [0x%X]: " % esp) -mem_reader(esp, 10) \ No newline at end of file diff --git a/regress/hang.py b/regress/hang.py deleted file mode 100755 index 50edaf2c..00000000 --- a/regress/hang.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/python - -from __future__ import print_function -from unicorn import * -from unicorn.x86_const import * - - -# callback for tracing instructions -def hook_code(uc, address, size, user_data): - tmp = uc.mem_read(address, size) - print("[0x%x] =" %(address), end="") - for i in tmp: - print(" %02x" %i, end="") - print("") - - -# callback for tracing Linux interrupt -def hook_intr(uc, intno, user_data): - # only handle Linux syscall - rip = uc.reg_read(UC_X86_REG_RIP) - if intno != 0x80: - print("=== 0x%x: got interrupt %x, quit" %(rip, intno)); - uc.emu_stop() - return - - eax = uc.reg_read(UC_X86_REG_EAX) - print(">>> 0x%x: interrupt 0x%x, EAX = 0x%x" %(rip, intno, eax)) - - -binary1 = b'\xeb\x1c\x5a\x89\xd6\x8b\x02\x66\x3d\xca\x7d\x75\x06\x66\x05\x03\x03\x89\x02\xfe\xc2\x3d\x41\x41\x41\x41\x75\xe9\xff\xe6\xe8\xdf\xff\xff\xff\x31\xd2\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xca\x7d\x41\x41\x41\x41\x41\x41\x41\x41' - -mu = Uc(UC_ARCH_X86, UC_MODE_64) - -mu.mem_map(0, 2 * 1024 * 1024) - -# tracing all instructions with customized callback -mu.hook_add(UC_HOOK_CODE, hook_code) - -# handle interrupt ourself -mu.hook_add(UC_HOOK_INTR, hook_intr) - -# setup stack -mu.reg_write(UC_X86_REG_RSP, 1024 * 1024) - -# fill in memory with 0xCC (software breakpoint int 3) -for i in xrange(1 * 1024): - mu.mem_write(0 + i, b'\xcc') - -# write machine code to be emulated to memory -mu.mem_write(0, binary1) - -mu.emu_start(0, len(binary1)) - diff --git a/regress/jmp_ebx_hang.py b/regress/jmp_ebx_hang.py deleted file mode 100755 index 5683de89..00000000 --- a/regress/jmp_ebx_hang.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python - -"""See https://github.com/unicorn-engine/unicorn/issues/82""" - -import unicorn -CODE_ADDR = 0x10101000 -CODE = b'\xff\xe3' # jmp ebx -mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) -mu.mem_map(CODE_ADDR, 1024 * 4) -mu.mem_write(CODE_ADDR, CODE) -# If EBX is zero then an exception is raised, as expected -mu.reg_write(unicorn.x86_const.UC_X86_REG_EBX, 0x0) - -print(">>> jmp ebx (ebx = 0)"); -try: - mu.emu_start(CODE_ADDR, CODE_ADDR + 2, count=1) -except unicorn.UcError as e: - print("ERROR: %s" % e) - assert(e.errno == unicorn.UC_ERR_CODE_INVALID) -else: - assert(False) - -print(">>> jmp ebx (ebx = 0xaa96a47f)"); -mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) -mu.mem_map(CODE_ADDR, 1024 * 4) -# If we write this address to EBX then the emulator hangs on emu_start -mu.reg_write(unicorn.x86_const.UC_X86_REG_EBX, 0xaa96a47f) -mu.mem_write(CODE_ADDR, CODE) -try: - mu.emu_start(CODE_ADDR, CODE_ADDR + 2, count=1) -except unicorn.UcError as e: - print("ERROR: %s" % e) - assert(e.errno == unicorn.UC_ERR_CODE_INVALID) -else: - assert(False) - -print "Success" diff --git a/regress/memmap.py b/regress/memmap.py deleted file mode 100755 index 2ac3508e..00000000 --- a/regress/memmap.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/python -# By Ryan Hileman, issue #9 - -# this prints out 2 lines and the contents must be the same - -from unicorn import * -uc = Uc(UC_ARCH_X86, UC_MODE_64) - -uc.mem_map(0x8048000, 0x2000) -uc.mem_write(0x8048000, 'test') -print 1, str(uc.mem_read(0x8048000, 4)).encode('hex') - -uc.mem_map(0x804a000, 0x8000) -print 2, str(uc.mem_read(0x8048000, 4)).encode('hex') diff --git a/regress/memmap_assert.py b/regress/memmap_assert.py deleted file mode 100755 index cebdb8e1..00000000 --- a/regress/memmap_assert.py +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python - -import unicorn - -u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) -u.mem_map(0x2000, 0) -u.mem_map(0x4000, 1) -print "I am never reached" diff --git a/regress/memmap_segfault.py b/regress/memmap_segfault.py deleted file mode 100755 index 83aea625..00000000 --- a/regress/memmap_segfault.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -import unicorn - -u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) -u.mem_map(0x2000, 0x1000) -u.mem_read(0x2000, 1) - -for i in range(20): - try: - u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) - u.mem_map(i*0x1000, 5) - u.mem_read(i*0x1000, 1) - print hex(i*0x1000) + " succeeeded" - except unicorn.UcError: - print hex(i*0x1000) + " failed" - -for i in range(20): - try: - u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) - u.mem_map(i*0x1000, 5) - u.mem_read(i*0x1000, 1) - print hex(i*0x1000) + " succeeeded" - except unicorn.UcError: - print hex(i*0x1000) + " failed" diff --git a/regress/memmap_segfault2.py b/regress/memmap_segfault2.py deleted file mode 100755 index bd3845b7..00000000 --- a/regress/memmap_segfault2.py +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/python - -from unicorn import * -uc = Uc(UC_ARCH_X86, UC_MODE_32) -uc.mem_map(0x0000, 0x2000) -uc.mem_map(0x2000, 0x4000) -uc.mem_write(0x1000, 0x1004 * ' ') -print 'If not reached, then we have BUG (crash on x86_64 Linux).' diff --git a/regress/memmap_weirdness.py b/regress/memmap_weirdness.py deleted file mode 100755 index d81eccd6..00000000 --- a/regress/memmap_weirdness.py +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python - -import unicorn - -for i in range(20): - #try: - u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) - u.mem_map(i*0x1000, 5) - u.mem_read(i*0x1000+6, 1) - print hex(i*0x1000) + " succeeeded" - #except unicorn.UcError as e: - # print hex(i*0x1000) + " failed:",e diff --git a/regress/mips_branch_delay.py b/regress/mips_branch_delay.py deleted file mode 100755 index 50a71a6f..00000000 --- a/regress/mips_branch_delay.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/python -from capstone import * -from unicorn import * -from unicorn.mips_const import * - -md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + CS_MODE_LITTLE_ENDIAN) - -def disas(code, addr): - for i in md.disasm(code, addr): - print '0x%x: %s %s' % (i.address, str(i.bytes).encode('hex'), i.op_str) - -def hook_code(uc, addr, size, _): - mem = str(uc.mem_read(addr, size)) - disas(mem, addr) - -CODE = 0x400000 -asm = '0000a4126a00822800000000'.decode('hex') - -print 'Input instructions:' -disas(asm, CODE) -print - -print 'Hooked instructions:' - -uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN) -uc.hook_add(UC_HOOK_CODE, hook_code) -uc.mem_map(CODE, 0x1000) -uc.mem_write(CODE, asm) -uc.emu_start(CODE, CODE + len(asm)) diff --git a/regress/mips_except.py b/regress/mips_except.py deleted file mode 100755 index 28c8001e..00000000 --- a/regress/mips_except.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/python -from unicorn import * -from unicorn.mips_const import * - -def hook_intr(uc, intno, _): - print 'interrupt', intno - -CODE = 0x400000 -asm = '0000a48f'.decode('hex') # lw $a0, ($sp) - -uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN) -uc.hook_add(UC_HOOK_INTR, hook_intr) -uc.mem_map(CODE, 0x1000) -uc.mem_write(CODE, asm) - -try: - print 'unaligned access (exc 12)' - uc.reg_write(UC_MIPS_REG_SP, 0x400001) - uc.emu_start(CODE, CODE + len(asm), 300) - print -except UcError as e: - print("ERROR: %s" % e) - -try: - print 'dunno (exc 26)' - uc.reg_write(UC_MIPS_REG_SP, 0xFFFFFFF0) - uc.emu_start(CODE, CODE + len(asm), 200) - print -except UcError as e: - print("ERROR: %s" % e) - -try: - print 'unassigned access (exc 28)' - uc.reg_write(UC_MIPS_REG_SP, 0x80000000) - uc.emu_start(CODE, CODE + len(asm), 100) - print -except UcError as e: - print("ERROR: %s" % e) diff --git a/regress/pshufb.py b/regress/pshufb.py deleted file mode 100755 index 432a2300..00000000 --- a/regress/pshufb.py +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/python -# By Ryan Hileman, issue #91 - -# Invalid instruction = test failed - -from unicorn import * -from unicorn.x86_const import * -uc = Uc(UC_ARCH_X86, UC_MODE_64) -uc.mem_map(0x2000, 0x1000) -# pshufb xmm0, xmm1 -uc.mem_write(0x2000, '660f3800c1'.decode('hex')) -uc.emu_start(0x2000, 0x2005) diff --git a/regress/reg_write_sign_extension.py b/regress/reg_write_sign_extension.py deleted file mode 100755 index 582e3ee5..00000000 --- a/regress/reg_write_sign_extension.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -"""See https://github.com/unicorn-engine/unicorn/issues/98""" - -import unicorn -ADDR = 0xffaabbcc - -def hook_mem_invalid(mu, access, address, size, value, user_data): - print ">>> Access type: %u, expected value: 0x%x, actual value: 0x%x" % (access, ADDR, address) - assert(address == ADDR) - mu.mem_map(address & 0xfffff000, 4 * 1024) - mu.mem_write(address, b'\xcc') - return True - -mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) -mu.reg_write(unicorn.x86_const.UC_X86_REG_EBX, ADDR) - -mu.mem_map(0x10000000, 1024 * 4) -# jmp ebx -mu.mem_write(0x10000000, b'\xff\xe3') - -mu.hook_add(unicorn.UC_HOOK_MEM_INVALID, hook_mem_invalid) -mu.emu_start(0x10000000, 0x10000000 + 2, count=1) diff --git a/regress/sparc_reg.py b/regress/sparc_reg.py deleted file mode 100755 index 73858360..00000000 --- a/regress/sparc_reg.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/python - -from unicorn import * -from unicorn.sparc_const import * - -uc = Uc(UC_ARCH_SPARC, UC_MODE_32) -uc.reg_write(UC_SPARC_REG_SP, 100) -uc.reg_write(UC_SPARC_REG_FP, 100) -print 'writing sp = 100, fp = 100' -print 'sp =', uc.reg_read(UC_SPARC_REG_SP) -print 'fp =', uc.reg_read(UC_SPARC_REG_FP) diff --git a/regress/wrong_rip.py b/regress/wrong_rip.py deleted file mode 100755 index 8c0b4c1f..00000000 --- a/regress/wrong_rip.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/python - -from unicorn import * -from unicorn.x86_const import * - -binary1 = b'\xb8\x02\x00\x00\x00' # mov eax, 2 -binary2 = b'\xb8\x01\x00\x00\x00' # mov eax, 1 - -mu = Uc(UC_ARCH_X86, UC_MODE_64) - -mu.mem_map(0, 2 * 1024 * 1024) - -# write machine code to be emulated to memory -mu.mem_write(0, binary1 + binary2) - -# emu for maximum 1 instruction. -mu.emu_start(0, 5, 0, 1) - -print("RAX = %u" %mu.reg_read(UC_X86_REG_RAX)) - -pos = mu.reg_read(UC_X86_REG_RIP) - -print("RIP = %x" %pos) - -mu.emu_start(5, 10, 0, 1) - -pos = mu.reg_read(UC_X86_REG_RIP) - -print("RIP = %x" %pos) - -print("RAX = %u" %mu.reg_read(UC_X86_REG_RAX)) - diff --git a/regress/wrong_rip2.py b/regress/wrong_rip2.py deleted file mode 100755 index 24d028ce..00000000 --- a/regress/wrong_rip2.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/python - -from unicorn import * -from unicorn.x86_const import * - -binary1 = b'\xb8\x02\x00\x00\x00' # mov eax, 2 -binary2 = b'\xb8\x01\x00\x00\x00' # mov eax, 1 - -mu = Uc(UC_ARCH_X86, UC_MODE_64) - -mu.mem_map(0, 2 * 1024 * 1024) - -# write machine code to be emulated to memory -mu.mem_write(0, binary1 + binary2) - -# emu for maximum 1 instruction. -mu.emu_start(0, 10, 0, 1) - -print("RAX = %u" %mu.reg_read(UC_X86_REG_RAX)) - -pos = mu.reg_read(UC_X86_REG_RIP) - -print("RIP = %x" %pos) - -mu.emu_start(5, 10, 0, 1) - -pos = mu.reg_read(UC_X86_REG_RIP) - -print("RIP = %x" %pos) - -print("RAX = %u" %mu.reg_read(UC_X86_REG_RAX)) - diff --git a/regress/wrong_rip3.py b/regress/wrong_rip3.py deleted file mode 100755 index 77c90eef..00000000 --- a/regress/wrong_rip3.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/python - -from unicorn import * -from unicorn.x86_const import * - -binary1 = b'\x40\x01\xc1\x31\xf6' # inc eax; add ecx, eax; xor esi, esi - -mu = Uc(UC_ARCH_X86, UC_MODE_32) - -mu.mem_map(0, 2 * 1024 * 1024) - -# write machine code to be emulated to memory -mu.mem_write(0, binary1) - -# emu for maximum 1 instruction. -mu.emu_start(0, 10, 0, 1) - -print("EAX = %u" %mu.reg_read(UC_X86_REG_EAX)) - -pos = mu.reg_read(UC_X86_REG_EIP) - -print("EIP = %x" %pos) - diff --git a/regress/wrong_rip4.py b/regress/wrong_rip4.py deleted file mode 100755 index 9937483b..00000000 --- a/regress/wrong_rip4.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/python - -from unicorn import * -from unicorn.x86_const import * - -binary1 = b'\x40\x01\xc1\x31\xf6\x90\x90\x90' # inc eax; add ecx, eax; xor esi, esi - -mu = Uc(UC_ARCH_X86, UC_MODE_32) - -mu.mem_map(0, 2 * 1024 * 1024) - -# write machine code to be emulated to memory -mu.mem_write(0, binary1) - -pos = 0 -# emu for maximum 1 instruction. -mu.emu_start(pos, len(binary1), 0, 1) - -print("EAX = %u" %mu.reg_read(UC_X86_REG_EAX)) - -pos = mu.reg_read(UC_X86_REG_EIP) - -print("EIP = %x" %pos) - -# emu to the end -mu.emu_start(pos, len(binary1)) - -print("EAX = %u" %mu.reg_read(UC_X86_REG_EAX)) - -pos = mu.reg_read(UC_X86_REG_EIP) - -print("EIP = %x" %pos) diff --git a/regress/wrong_rip_arm.py b/regress/wrong_rip_arm.py deleted file mode 100755 index d10a47f3..00000000 --- a/regress/wrong_rip_arm.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/python - -from unicorn import * -from unicorn.x86_const import * -from unicorn.arm_const import * - -# adds r1, #0x48 -# ldrsb r7, [r7, r7] -# ldrsh r7, [r2, r1] -# ldr r0, [pc, #0x168] -# cmp r7, #0xbf -# str r7, [r5, #0x20] -# ldr r1, [r5, #0x64] -# strb r7, [r5, #0xc] -# ldr r0, [pc, #0x1a0] -binary1 = b'\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05' -binary1 = b'\x48\x31\xff\x57' -#adds r1, #0x48 -#ldrsb r7, [r7, r7] - -mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB) - -mu.mem_map(0, 2 * 1024 * 1024) - -# write machine code to be emulated to memory -mu.mem_write(0, binary1) - -mu.reg_write(ARM_REG_R13, 1*1024*1024) - -pos = 0 - -# emu for maximum 1 instruction. -mu.emu_start(pos, len(binary1), 0, 1) - -print("R1 = %x" % mu.reg_read(ARM_REG_R1)) - -pos = mu.reg_read(ARM_REG_R15) - -print("RIP = %x" %pos) diff --git a/regress/wrong_sp_arm.py b/regress/wrong_sp_arm.py deleted file mode 100755 index 762210f3..00000000 --- a/regress/wrong_sp_arm.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/python -# By Ryan Hileman, issue #16 - -from unicorn import * -from unicorn.arm_const import * - -try: - uc = Uc(UC_ARCH_ARM, UC_MODE_32) - uc.reg_write(UC_ARM_REG_SP, 4) - print 'Writing 4 to SP' - print 'SP =', uc.reg_read(UC_ARM_REG_SP) -except UcError as e: - print("ERROR: %s" % e) - -try: - print "===========" - uc = Uc(UC_ARCH_ARM, UC_MODE_ARM) - uc.reg_write(UC_ARM_REG_SP, 4) - print 'Writing 4 to SP' - print 'SP =', uc.reg_read(UC_ARM_REG_SP) -except UcError as e: - print("ERROR: %s" % e) diff --git a/regress/wrong_sp_arm64.py b/regress/wrong_sp_arm64.py deleted file mode 100755 index d63791e1..00000000 --- a/regress/wrong_sp_arm64.py +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/python - -from unicorn import * -from unicorn.arm64_const import * - -try: - uc = Uc(UC_ARCH_ARM64, UC_MODE_ARM) - uc.reg_write(UC_ARM64_REG_SP, 4) - print 'Writing 4 to SP' - print 'SP =', uc.reg_read(UC_ARM64_REG_SP) -except UcError as e: - print("ERROR: %s" % e) diff --git a/samples/mem_apis.c b/samples/mem_apis.c index 6237c578..28f2b60c 100644 --- a/samples/mem_apis.c +++ b/samples/mem_apis.c @@ -79,13 +79,13 @@ static bool hook_mem_invalid(uc_engine *uc, uc_mem_type type, default: printf("not ok - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", type, addr); return false; - case UC_MEM_READ: + case UC_MEM_READ_INVALID: printf("not ok - Read from invalid memory at 0x%"PRIx64 ", data size = %u\n", addr, size); return false; - case UC_MEM_WRITE: + case UC_MEM_WRITE_INVALID: printf("not ok - Write to invalid memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); return false; - case UC_MEM_EXEC_PROT: + case UC_MEM_FETCH_PROT: printf("not ok - Fetch from non-executable memory at 0x%"PRIx64 "\n", addr); return false; case UC_MEM_WRITE_PROT: @@ -147,7 +147,8 @@ static void do_nx_demo(bool cause_fault) // intercept code and invalid memory events if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK || - uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { + uc_hook_add(uc, &trace1, UC_HOOK_MEM_ERR, + hook_mem_invalid, NULL) != UC_ERR_OK) { printf("not ok - Failed to install hooks\n"); return; } @@ -226,7 +227,9 @@ static void do_perms_demo(bool change_perms) // intercept code and invalid memory events if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK || - uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { + uc_hook_add(uc, &trace1, + UC_HOOK_MEM_READ_INVALID | UC_HOOK_MEM_WRITE_INVALID | UC_HOOK_MEM_FETCH_INVALID | UC_HOOK_MEM_FETCH_PROT | UC_HOOK_MEM_WRITE_PROT | UC_HOOK_MEM_READ_PROT, + hook_mem_invalid, NULL) != UC_ERR_OK) { printf("not ok - Failed to install hooks\n"); return; } @@ -302,7 +305,9 @@ static void do_unmap_demo(bool do_unmap) // intercept code and invalid memory events if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK || - uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { + uc_hook_add(uc, &trace1, + UC_HOOK_MEM_READ_INVALID | UC_HOOK_MEM_WRITE_INVALID | UC_HOOK_MEM_FETCH_INVALID | UC_HOOK_MEM_FETCH_PROT | UC_HOOK_MEM_WRITE_PROT | UC_HOOK_MEM_READ_PROT, + hook_mem_invalid, NULL) != UC_ERR_OK) { printf("not ok - Failed to install hooks\n"); return; } diff --git a/samples/sample_x86.c b/samples/sample_x86.c index c6662dfe..f2a28916 100644 --- a/samples/sample_x86.c +++ b/samples/sample_x86.c @@ -73,7 +73,7 @@ static bool hook_mem_invalid(uc_engine *uc, uc_mem_type type, default: // return false to indicate we want to stop emulation return false; - case UC_MEM_WRITE: + case UC_MEM_WRITE_INVALID: printf(">>> Missing memory is being WRITE at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", address, size, value); // map this memory in with 2MB in size @@ -421,7 +421,7 @@ static void test_i386_invalid_mem_write(void) uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0); // intercept invalid memory events - uc_hook_add(uc, &trace3, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL); + uc_hook_add(uc, &trace3, UC_HOOK_MEM_READ_INVALID | UC_HOOK_MEM_WRITE_INVALID, hook_mem_invalid, NULL); // emulate machine code in infinite time err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_MEM_WRITE) - 1, 0, 0); diff --git a/regress/Makefile b/tests/regress/Makefile similarity index 80% rename from regress/Makefile rename to tests/regress/Makefile index 09214579..e03c27e9 100644 --- a/regress/Makefile +++ b/tests/regress/Makefile @@ -1,5 +1,5 @@ CFLAGS += -I../include -LDFLAGS += ../libunicorn.a $(shell pkg-config --libs glib-2.0) -lpthread -lm +LDFLAGS += ../../libunicorn.a $(shell pkg-config --libs glib-2.0) -lpthread -lm TESTS = map_crash map_write TESTS += sigill sigill2 diff --git a/tests/regress/arm_bxeq_hang.py b/tests/regress/arm_bxeq_hang.py new file mode 100755 index 00000000..12f4a2ae --- /dev/null +++ b/tests/regress/arm_bxeq_hang.py @@ -0,0 +1,27 @@ +#!/usr/bin/python + +from unicorn import * +from unicorn.arm_const import * + +import regress + +class BxHang(regress.RegressTest): + + def runTest(self): + uc = Uc(UC_ARCH_ARM, UC_MODE_ARM) + uc.mem_map(0x1000, 0x1000) + uc.mem_write(0x1000, '1eff2f010000a0e1'.decode('hex')) + uc.count = 0 + def hook_block(uc, addr, *args): + print 'enter block 0x%04x' % addr + uc.count += 1 + + uc.reg_write(UC_ARM_REG_LR, 0x1004) + uc.hook_add(UC_HOOK_BLOCK, hook_block) + print 'block should only run once' + uc.emu_start(0x1000, 0x1004) + + self.assertEqual(uc.count, 1) + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/arm_movr12_hang.py b/tests/regress/arm_movr12_hang.py new file mode 100755 index 00000000..1bb276e0 --- /dev/null +++ b/tests/regress/arm_movr12_hang.py @@ -0,0 +1,32 @@ +#!/usr/bin/python + +from unicorn import * +from unicorn.arm_const import * + +import regress + +class MovHang(regress.RegressTest): + + def runTest(self): + uc = Uc(UC_ARCH_ARM, UC_MODE_ARM) + uc.mem_map(0x1000, 0x1000) + uc.mem_write(0x1000, '00c000e3'.decode('hex')) # movw r12, #0 + + def hook_block(uc, addr, *args): + print 'enter block 0x%04x' % addr + uc.count += 1 + + uc.reg_write(UC_ARM_REG_R12, 0x123) + self.assertEquals(uc.reg_read(UC_ARM_REG_R12), 0x123) + + uc.hook_add(UC_HOOK_BLOCK, hook_block) + uc.count = 0 + + #print 'block should only run once' + uc.emu_start(0x1000, 0x1004, timeout=500) + + self.assertEquals(uc.reg_read(UC_ARM_REG_R12), 0x0) + self.assertEquals(uc.count, 1) + +if __name__ == '__main__': + regress.main() diff --git a/regress/block_test.c b/tests/regress/block_test.c similarity index 100% rename from regress/block_test.c rename to tests/regress/block_test.c diff --git a/tests/regress/callback-pc.py b/tests/regress/callback-pc.py new file mode 100755 index 00000000..3edc67e6 --- /dev/null +++ b/tests/regress/callback-pc.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python + +# reg_write() can't modify PC from within trace callbacks +# Pull Request #4 + +from __future__ import print_function +from unicorn import * +from unicorn.arm_const import * + +import regress + +BASE_ADDRESS = 0x10000000 + +# sub sp, #0xc +THUMB_CODE = "\x83\xb0" * 5 + +# callback for tracing instructions +def hook_code(uc, address, size, user_data): + print(">>> Tracing instruction at 0x%x, instruction size = %u" % (address, size)) + mu = user_data + print(">>> Setting PC to 0xffffffff") + mu.reg_write(UC_ARM_REG_PC, 0xffffffff) + +# callback for tracing basic blocks +def hook_block(uc, address, size, user_data): + print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) + mu = user_data + print(">>> Setting PC to 0xffffffff") + mu.reg_write(UC_ARM_REG_PC, 0xffffffff) + +class CallBackPCTest(regress.RegressTest): + + def runTest(self): + self.instruction_trace_test() + + # set up emulation + def instruction_trace_test(self): + try: + # initialize emulator in ARM's Thumb mode + mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB) + + # map some memory + mu.mem_map(BASE_ADDRESS, 2 * 1024 * 1024) + + # write machine code to be emulated to memory + mu.mem_write(BASE_ADDRESS, THUMB_CODE) + + # setup stack + mu.reg_write(UC_ARM_REG_SP, BASE_ADDRESS + 2 * 1024 * 1024) + + # tracing all instructions with customized callback + mu.hook_add(UC_HOOK_CODE, hook_code, user_data=mu) + + # tracing all basic blocks with customized callback + mu.hook_add(UC_HOOK_BLOCK, hook_block, user_data=mu) + + # emulate machine code in infinite time + mu.emu_start(BASE_ADDRESS, BASE_ADDRESS + len(THUMB_CODE)) + + except UcError as e: + assertFalse(0, "ERROR: %s" % e) + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/crash_tb.py b/tests/regress/crash_tb.py new file mode 100755 index 00000000..9ecf61cd --- /dev/null +++ b/tests/regress/crash_tb.py @@ -0,0 +1,37 @@ +#!/usr/bin/python + +from unicorn import * +from unicorn.x86_const import * + +import regress + +CODE_ADDR = 0x0 +binary1 = b'\xb8\x02\x00\x00\x00' +binary2 = b'\xb8\x01\x00\x00\x00' + +class CrashTB(regress.RegressTest): + + def runTest(self): + mu = Uc(UC_ARCH_X86, UC_MODE_64) + + mu.mem_map(CODE_ADDR, 2 * 1024 * 1024) + + # write machine code to be emulated to memory + mu.mem_write(CODE_ADDR, binary1) + + # emu for maximum 1 sec. + mu.emu_start(CODE_ADDR, len(binary1), UC_SECOND_SCALE) + + self.assertEqual(0x2, mu.reg_read(UC_X86_REG_RAX)) + + # write machine code to be emulated to memory + mu.mem_write(CODE_ADDR, binary2) + + # emu for maximum 1 sec. + mu.emu_start(CODE_ADDR, len(binary2), UC_SECOND_SCALE) + + self.assertEqual(0x1, mu.reg_read(UC_X86_REG_RAX)) + +if __name__ == '__main__': + regress.main() + diff --git a/tests/regress/deadlock_1.py b/tests/regress/deadlock_1.py new file mode 100755 index 00000000..269a573f --- /dev/null +++ b/tests/regress/deadlock_1.py @@ -0,0 +1,20 @@ +#!/usr/bin/python +# From issue #1 of Ryan Hileman + +from unicorn import * +import regress + +CODE = b"\x90\x91\x92" + +class DeadLock(regress.RegressTest): + + def runTest(self): + mu = Uc(UC_ARCH_X86, UC_MODE_64) + mu.mem_map(0x100000, 4 * 1024) + mu.mem_write(0x100000, CODE) + + with self.assertRaises(UcError): + mu.emu_start(0x100000, 0x1000 + len(CODE)) + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/emu_stop_segfault.py b/tests/regress/emu_stop_segfault.py new file mode 100755 index 00000000..8c22e9de --- /dev/null +++ b/tests/regress/emu_stop_segfault.py @@ -0,0 +1,20 @@ +#!/usr/bin/python + +"""See https://github.com/unicorn-engine/unicorn/issues/65""" + +import unicorn +import regress + +class EmuStopSegFault(regress.RegressTest): + + def runTest(self): + ADDR = 0x10101000 + mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) + mu.mem_map(ADDR, 1024 * 4) + mu.mem_write(ADDR, b'\x41') + mu.emu_start(ADDR, ADDR + 1, count=1) + # The following should not trigger a null pointer dereference + self.assertEqual(None, mu.emu_stop()) + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/fpu_ip.py b/tests/regress/fpu_ip.py new file mode 100755 index 00000000..3db6a8b5 --- /dev/null +++ b/tests/regress/fpu_ip.py @@ -0,0 +1,69 @@ +#!/usr/bin/python +from unicorn import * +from unicorn.x86_const import * +from capstone import * +import regress + +ESP = 0x2000 +PAGE_SIZE = 2 * 1024 * 1024 + +# mov [esp], DWORD 0x37f +# fldcw [esp] +# fnop +# fnstenv [esp + 8] +# pop ecx +CODE = b'\xc7\x04\x24\x7f\x03\x00\x00\xd9\x2c\x24\xd9\xd0\xd9\x74\x24\x08\x59' + +class SimpleEngine: + def __init__(self): + self.capmd = Cs(CS_ARCH_X86, CS_MODE_32) + + def disas_single(self, data): + for i in self.capmd.disasm(data, 16): + print("\t%s\t%s" % (i.mnemonic, i.op_str)) + break + +disasm = SimpleEngine() + +def hook_code(uc, addr, size, user_data): + mem = uc.mem_read(addr, size) + print(" 0x%X:" % (addr)), + disasm.disas_single(str(mem)) + +class FpuIP(regress.RegressTest): + + def mem_reader(self, mu, addr, size, expected): + tmp = mu.mem_read(addr, size) + for out, exp in zip(tmp, expected): + self.assertEqual(exp, out) + + def test_32(self): + mu = Uc(UC_ARCH_X86, UC_MODE_32) + + mu.mem_map(0x0, PAGE_SIZE) + mu.mem_write(0x4000, CODE) + mu.reg_write(UC_X86_REG_ESP, ESP) + mu.hook_add(UC_HOOK_CODE, hook_code) + + mu.emu_start(0x4000, 0, 0, 5) + esp = mu.reg_read(UC_X86_REG_ESP) + self.assertEqual(0x2004, esp) + expected = [0x0, 0x0, 0xa, 0x40] + self.mem_reader(mu, esp + 14, 4, expected) + + def test_64(self): + mu = Uc(UC_ARCH_X86, UC_MODE_64) + + mu.mem_map(0x0, PAGE_SIZE) + mu.mem_write(0x4000, CODE) + mu.reg_write(UC_X86_REG_ESP, ESP) + mu.hook_add(UC_HOOK_CODE, hook_code) + + mu.emu_start(0x4000, 0, 0, 5) + rsp = mu.reg_read(UC_X86_REG_RSP) + self.assertEqual(0x2012, rsp + 10) + expected = [0x0, 0x0, 0xa, 0x40, 0x0, 0x0, 0x0, 0x0] + self.mem_reader(mu, rsp + 10, 4, expected) + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/fpu_mem_write.py b/tests/regress/fpu_mem_write.py new file mode 100755 index 00000000..4017decb --- /dev/null +++ b/tests/regress/fpu_mem_write.py @@ -0,0 +1,38 @@ +#!/usr/bin/python +from unicorn import * +from unicorn.x86_const import * + +import regress + +ESP = 0x2000 +PAGE_SIZE = 1 * 1024 * 1024 + +# wait +# fnstcw word ptr [esp] +# pop ecx +CODE = b'\x9B\xD9\x3C\x24\x59' + +def hook_mem_write(uc, access, address, size, value, user_data): + print("mem WRITE: 0x%x, data size = %u, data value = 0x%x" % (address, size, value)) + return True + +class FpuWrite(regress.RegressTest): + + def mem_reader(self, mu, addr, size, expected): + tmp = mu.mem_read(addr, size) + for i, e in zip(tmp, expected): + self.assertEquals(e, i) + + def runTest(self): + mu = Uc(UC_ARCH_X86, UC_MODE_32) + mu.mem_map(0, PAGE_SIZE) + mu.mem_write(0, CODE) + mu.reg_write(UC_X86_REG_ESP, ESP) + + mu.hook_add(UC_HOOK_MEM_WRITE, hook_mem_write) + mu.emu_start(0x0, 5, 0, 2) + esp = mu.reg_read(UC_X86_REG_ESP) + self.mem_reader(mu, esp, 10, [0] * 10) + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/hang.py b/tests/regress/hang.py new file mode 100755 index 00000000..9c8a3775 --- /dev/null +++ b/tests/regress/hang.py @@ -0,0 +1,57 @@ +#!/usr/bin/python + +from __future__ import print_function +from unicorn import * +from unicorn.x86_const import * + +import regress + +# callback for tracing instructions +def hook_code(uc, address, size, user_data): + tmp = uc.mem_read(address, size) + print("[0x%x] =" %(address), end="") + for i in tmp: + print(" %02x" %i, end="") + print("") + +# callback for tracing Linux interrupt +def hook_intr(uc, intno, user_data): + # only handle Linux syscall + rip = uc.reg_read(UC_X86_REG_RIP) + if intno != 0x80: + print("=== 0x%x: got interrupt %x, quit" %(rip, intno)); + uc.emu_stop() + return + + eax = uc.reg_read(UC_X86_REG_EAX) + print(">>> 0x%x: interrupt 0x%x, EAX = 0x%x" %(rip, intno, eax)) + +class Hang(regress.RegressTest): + + def runTest(self): + binary1 = b'\xeb\x1c\x5a\x89\xd6\x8b\x02\x66\x3d\xca\x7d\x75\x06\x66\x05\x03\x03\x89\x02\xfe\xc2\x3d\x41\x41\x41\x41\x75\xe9\xff\xe6\xe8\xdf\xff\xff\xff\x31\xd2\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xca\x7d\x41\x41\x41\x41\x41\x41\x41\x41' + + mu = Uc(UC_ARCH_X86, UC_MODE_64) + + mu.mem_map(0, 2 * 1024 * 1024) + + # tracing all instructions with customized callback + mu.hook_add(UC_HOOK_CODE, hook_code) + + # handle interrupt ourself + mu.hook_add(UC_HOOK_INTR, hook_intr) + + # setup stack + mu.reg_write(UC_X86_REG_RSP, 1024 * 1024) + + # fill in memory with 0xCC (software breakpoint int 3) + for i in xrange(1 * 1024): + mu.mem_write(0 + i, b'\xcc') + + # write machine code to be emulated to memory + mu.mem_write(0, binary1) + + self.assertEqual(mu.emu_start(0, len(binary1)), None) + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/jmp_ebx_hang.py b/tests/regress/jmp_ebx_hang.py new file mode 100755 index 00000000..602c594f --- /dev/null +++ b/tests/regress/jmp_ebx_hang.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +"""See https://github.com/unicorn-engine/unicorn/issues/82""" + +import unicorn +from unicorn import * +import regress + +CODE_ADDR = 0x10101000 +CODE = b'\xff\xe3' # jmp ebx + +class JumEbxHang(regress.RegressTest): + + def runTest(self): + mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) + mu.mem_map(CODE_ADDR, 1024 * 4) + mu.mem_write(CODE_ADDR, CODE) + # If EBX is zero then an exception is raised, as expected + mu.reg_write(unicorn.x86_const.UC_X86_REG_EBX, 0x0) + + print(">>> jmp ebx (ebx = 0)"); + with self.assertRaises(UcError) as m: + mu.emu_start(CODE_ADDR, CODE_ADDR + 2, count=1) + + self.assertEqual(m.exception.errno, unicorn.UC_ERR_CODE_INVALID) + + print(">>> jmp ebx (ebx = 0xaa96a47f)"); + mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) + mu.mem_map(CODE_ADDR, 1024 * 4) + # If we write this address to EBX then the emulator hangs on emu_start + mu.reg_write(unicorn.x86_const.UC_X86_REG_EBX, 0xaa96a47f) + mu.mem_write(CODE_ADDR, CODE) + with self.assertRaises(UcError) as m: + mu.emu_start(CODE_ADDR, CODE_ADDR + 2, count=1) + + self.assertEqual(m.exception.errno, unicorn.UC_ERR_CODE_INVALID) + +if __name__ == '__main__': + regress.main() diff --git a/regress/map_crash.c b/tests/regress/map_crash.c similarity index 100% rename from regress/map_crash.c rename to tests/regress/map_crash.c diff --git a/regress/map_write.c b/tests/regress/map_write.c similarity index 100% rename from regress/map_write.c rename to tests/regress/map_write.c diff --git a/regress/mem_exec.c b/tests/regress/mem_exec.c similarity index 95% rename from regress/mem_exec.c rename to tests/regress/mem_exec.c index 28f7d7a0..db9a2bc1 100644 --- a/regress/mem_exec.c +++ b/tests/regress/mem_exec.c @@ -116,9 +116,9 @@ static bool hook_mem_invalid(uc_engine *uc, uc_mem_type type, { switch(type) { default: - printf("not ok %d - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); + printf("not ok %d - memory invalid type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); return false; - case UC_MEM_EXEC_PROT: + case UC_MEM_FETCH_PROT: printf("# Fetch from non-executable memory at 0x%"PRIx64 "\n", addr); //make page executable @@ -221,11 +221,11 @@ int main(int argc, char **argv, char **envp) } // intercept invalid memory events - if (uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { - printf("not ok %d - Failed to install UC_HOOK_MEM_INVALID ucr\n", log_num++); + if (uc_hook_add(uc, &trace1, UC_HOOK_MEM_WRITE_PROT | UC_HOOK_MEM_FETCH_PROT, hook_mem_invalid, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install memory invalid handler\n", log_num++); return 8; } else { - printf("ok %d - UC_HOOK_MEM_INVALID installed\n", log_num++); + printf("ok %d - memory invalid handler installed\n", log_num++); } // emulate machine code until told to stop by hook_code diff --git a/regress/mem_protect.c b/tests/regress/mem_protect.c similarity index 96% rename from regress/mem_protect.c rename to tests/regress/mem_protect.c index a9dcce9d..d29dc490 100644 --- a/regress/mem_protect.c +++ b/tests/regress/mem_protect.c @@ -138,7 +138,7 @@ static bool hook_mem_invalid(uc_engine *uc, uc_mem_type type, uint32_t testval; switch(type) { default: - printf("not ok %d - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); + printf("not ok %d - memory invalid type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); return false; case UC_MEM_WRITE_PROT: printf("# write to non-writeable memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); @@ -229,11 +229,11 @@ int main(int argc, char **argv, char **envp) } // intercept invalid memory events - if (uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { - printf("not ok %d - Failed to install UC_HOOK_MEM_INVALID ucr\n", log_num++); + if (uc_hook_add(uc, &trace1, UC_HOOK_MEM_WRITE_PROT, hook_mem_invalid, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install memory invalid handler\n", log_num++); return 7; } else { - printf("ok %d - UC_HOOK_MEM_INVALID installed\n", log_num++); + printf("ok %d - memory invalid handler installed\n", log_num++); } // emulate machine code until told to stop by hook_code diff --git a/regress/mem_unmap.c b/tests/regress/mem_unmap.c similarity index 96% rename from regress/mem_unmap.c rename to tests/regress/mem_unmap.c index 435d4665..06223bfa 100644 --- a/regress/mem_unmap.c +++ b/tests/regress/mem_unmap.c @@ -133,7 +133,7 @@ static bool hook_mem_invalid(uc_engine *uc, uc_mem_type type, uint32_t testval; switch(type) { default: - printf("not ok %d - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); + printf("not ok %d - memory invalid type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); return false; case UC_MEM_WRITE: printf("# write to invalid memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); @@ -224,11 +224,11 @@ int main(int argc, char **argv, char **envp) } // intercept invalid memory events - if (uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { - printf("not ok %d - Failed to install UC_HOOK_MEM_INVALID ucr\n", log_num++); + if (uc_hook_add(uc, &trace1, UC_HOOK_MEM_WRITE_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install memory invalid handler\n", log_num++); return 7; } else { - printf("ok %d - UC_HOOK_MEM_INVALID installed\n", log_num++); + printf("ok %d - memory invalid handler installed\n", log_num++); } // emulate machine code until told to stop by hook_code diff --git a/tests/regress/memmap.py b/tests/regress/memmap.py new file mode 100755 index 00000000..3252d51f --- /dev/null +++ b/tests/regress/memmap.py @@ -0,0 +1,40 @@ +#!/usr/bin/python +# By Ryan Hileman, issue #9 + +# this prints out 2 lines and the contents must be the same + +from unicorn import * +import regress + +class MemMap(regress.RegressTest): + + def test_mmap_write(self): + uc = Uc(UC_ARCH_X86, UC_MODE_64) + + uc.mem_map(0x8048000, 0x2000) + uc.mem_write(0x8048000, 'test') + s1 = str(uc.mem_read(0x8048000, 4)).encode('hex') + + self.assertEqual('test'.encode('hex'), s1) + + uc.mem_map(0x804a000, 0x8000) + s2 = str(uc.mem_read(0x8048000, 4)).encode('hex') + self.assertEqual(s1, s2) + + def test_mmap_invalid(self): + u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) + with self.assertRaises(UcError): + u.mem_map(0x2000, 0) + with self.assertRaises(UcError): + u.mem_map(0x4000, 1) + + def test_mmap_weird(self): + u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) + + for i in xrange(20): + with self.assertRaises(UcError): + u.mem_map(i*0x1000, 5) + u.mem_read(i*0x1000+6, 1) + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/memmap_segfault.py b/tests/regress/memmap_segfault.py new file mode 100755 index 00000000..5d012b8b --- /dev/null +++ b/tests/regress/memmap_segfault.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +import unicorn +from unicorn import * + +import regress + +class MmapSeg(regress.RegressTest): + + def test_seg1(self): + u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) + u.mem_map(0x2000, 0x1000) + u.mem_read(0x2000, 1) + + for i in range(50): + u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) + u.mem_map(i*0x1000, 0x1000) + u.mem_read(i*0x1000, 1) + + for i in range(20): + with self.assertRaises(UcError): + u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) + u.mem_map(i*0x1000, 5) + u.mem_read(i*0x1000, 1) + + def test_seg2(self): + uc = Uc(UC_ARCH_X86, UC_MODE_32) + uc.mem_map(0x0000, 0x2000) + uc.mem_map(0x2000, 0x4000) + uc.mem_write(0x1000, 0x1004 * ' ') + self.assertTrue(1, + 'If not reached, then we have BUG (crash on x86_64 Linux).') + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/mips_branch_delay.py b/tests/regress/mips_branch_delay.py new file mode 100755 index 00000000..2f90ef14 --- /dev/null +++ b/tests/regress/mips_branch_delay.py @@ -0,0 +1,36 @@ +#!/usr/bin/python +from capstone import * +from unicorn import * + +import regress + +class MipsBranchDelay(regress.RegressTest): + + def runTest(self): + md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + CS_MODE_LITTLE_ENDIAN) + + def disas(code, addr): + for i in md.disasm(code, addr): + print '0x%x: %s %s' % (i.address, str(i.bytes).encode('hex'), i.op_str) + + def hook_code(uc, addr, size, _): + mem = str(uc.mem_read(addr, size)) + disas(mem, addr) + + CODE = 0x400000 + asm = '0000a4126a00822800000000'.decode('hex') # beq $a0, $s5, 0x4008a0; slti $v0, $a0, 0x6a; nop + + print 'Input instructions:' + disas(asm, CODE) + print + + print 'Hooked instructions:' + + uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN) + uc.hook_add(UC_HOOK_CODE, hook_code) + uc.mem_map(CODE, 0x1000) + uc.mem_write(CODE, asm) + self.assertEqual(None, uc.emu_start(CODE, CODE + len(asm))) + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/mips_except.py b/tests/regress/mips_except.py new file mode 100755 index 00000000..23969e9e --- /dev/null +++ b/tests/regress/mips_except.py @@ -0,0 +1,41 @@ +#!/usr/bin/python +from unicorn import * +from unicorn.mips_const import * + +import regress + +def hook_intr(uc, intno, _): + print 'interrupt', intno + +CODE = 0x400000 +asm = '0000a48f'.decode('hex') # lw $a0, ($sp) + +class MipsExcept(regress.RegressTest): + + def runTest(self): + uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN) + uc.hook_add(UC_HOOK_INTR, hook_intr) + uc.mem_map(CODE, 0x1000) + uc.mem_write(CODE, asm) + + with self.assertRaises(UcError) as m: + uc.reg_write(UC_MIPS_REG_SP, 0x400001) + uc.emu_start(CODE, CODE + len(asm), 300) + + self.assertEqual(UC_ERR_READ_UNALIGNED, m.exception.errno) + + with self.assertRaises(UcError) as m: + uc.reg_write(UC_MIPS_REG_SP, 0xFFFFFFF0) + uc.emu_start(CODE, CODE + len(asm), 200) + + self.assertEqual(UC_ERR_READ_INVALID, m.exception.errno) + + with self.assertRaises(UcError) as m: + uc.reg_write(UC_MIPS_REG_SP, 0x80000000) + uc.emu_start(CODE, CODE + len(asm), 100) + + self.assertEqual(UC_ERR_READ_INVALID, m.exception.errno) + +if __name__ == '__main__': + regress.main() + diff --git a/regress/movsd.py b/tests/regress/movsd.py similarity index 62% rename from regress/movsd.py rename to tests/regress/movsd.py index e873b1cd..28766139 100755 --- a/regress/movsd.py +++ b/tests/regress/movsd.py @@ -5,6 +5,7 @@ from capstone import * from unicorn import * from unicorn.x86_const import * +import regress code = 'f20f1005aa120000'.decode('hex') def dis(mem, addr): @@ -20,9 +21,15 @@ def hook_code(uc, addr, size, user_data): print 'instruction:', str(mem).encode('hex'), dis(mem, addr) print 'reference: ', code.encode('hex'), dis(code, addr) -addr = 0x400000 -mu = Uc(UC_ARCH_X86, UC_MODE_64) -mu.hook_add(UC_HOOK_CODE, hook_code) -mu.mem_map(addr, 8 * 1024 * 1024) -mu.mem_write(addr, code) -mu.emu_start(addr, addr + len(code)) +class Movsd(regress.RegressTest): + + def runTest(self): + addr = 0x400000 + mu = Uc(UC_ARCH_X86, UC_MODE_64) + mu.hook_add(UC_HOOK_CODE, hook_code) + mu.mem_map(addr, 8 * 1024 * 1024) + mu.mem_write(addr, code) + mu.emu_start(addr, addr + len(code)) + +if __name__ == '__main__': + regress.main() diff --git a/regress/nr_mem_test.c b/tests/regress/nr_mem_test.c similarity index 97% rename from regress/nr_mem_test.c rename to tests/regress/nr_mem_test.c index 3fb1f0f4..60e97db7 100644 --- a/regress/nr_mem_test.c +++ b/tests/regress/nr_mem_test.c @@ -86,7 +86,7 @@ int main(int argc, char **argv, char **envp) //uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)0x400000, (uint64_t)0x400fff); // intercept invalid memory events - uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL); + uc_hook_add(uc, &trace1, UC_MEM_READ_PROT, hook_mem_invalid, NULL); // emulate machine code in infinite time printf("BEGIN execution\n"); diff --git a/tests/regress/pshufb.py b/tests/regress/pshufb.py new file mode 100755 index 00000000..4e60b7dc --- /dev/null +++ b/tests/regress/pshufb.py @@ -0,0 +1,21 @@ +#!/usr/bin/python +# By Ryan Hileman, issue #91 + +# Invalid instruction = test failed + +from unicorn import * +from unicorn.x86_const import * + +import regress + +class Pshufb(regress.RegressTest): + + def runTest(self): + uc = Uc(UC_ARCH_X86, UC_MODE_64) + uc.mem_map(0x2000, 0x1000) + # pshufb xmm0, xmm1 + uc.mem_write(0x2000, '660f3800c1'.decode('hex')) + uc.emu_start(0x2000, 0x2005) + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/reg_write_sign_extension.py b/tests/regress/reg_write_sign_extension.py new file mode 100755 index 00000000..34d65506 --- /dev/null +++ b/tests/regress/reg_write_sign_extension.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +"""See https://github.com/unicorn-engine/unicorn/issues/98""" + +import unicorn +import regress + +ADDR = 0xffaabbcc + +def hook_mem_invalid(mu, access, address, size, value, user_data): + print ">>> Access type: %u, expected value: 0x%x, actual value: 0x%x" % (access, ADDR, address) + assert(address == ADDR) + mu.mem_map(address & 0xfffff000, 4 * 1024) + mu.mem_write(address, b'\xcc') + return True + +class RegWriteSignExt(regress.RegressTest): + + def runTest(self): + mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) + mu.reg_write(unicorn.x86_const.UC_X86_REG_EBX, ADDR) + + mu.mem_map(0x10000000, 1024 * 4) + # jmp ebx + mu.mem_write(0x10000000, b'\xff\xe3') + + mu.hook_add(unicorn.UC_HOOK_MEM_FETCH_INVALID | unicorn.UC_HOOK_MEM_FETCH_PROT, hook_mem_invalid) + mu.emu_start(0x10000000, 0x10000000 + 2, count=1) + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/regress.py b/tests/regress/regress.py new file mode 100755 index 00000000..2e4f2536 --- /dev/null +++ b/tests/regress/regress.py @@ -0,0 +1,34 @@ +#!/usr/bin/python + +import unittest + +from os.path import dirname, basename, isfile +import glob + +# Find all unittest type in this directory and run it. + +class RegressTest(unittest.TestCase): + pass + +def main(): + unittest.main() + +if __name__ == '__main__': + directory = dirname(__file__) + if directory == '': + directory = '.' + modules = glob.glob(directory+"/*.py") + __all__ = [ basename(f)[:-3] for f in modules if isfile(f)] + suite = unittest.TestSuite() + + for module in __all__: + m = __import__(module) + for cl in dir(m): + try: + realcl = getattr(m,cl) + if issubclass(realcl, unittest.TestCase): + suite.addTest(realcl()) + except Exception as e: + pass + + unittest.TextTestRunner().run(suite) diff --git a/tests/regress/regress.sh b/tests/regress/regress.sh new file mode 100755 index 00000000..f0b79900 --- /dev/null +++ b/tests/regress/regress.sh @@ -0,0 +1,13 @@ +#!/bin/sh + + +./map_crash map_write +./sigill sigill2 +./block_test +./ro_mem_test nr_mem_test +./timeout_segfault +./rep_movsb +./mem_unmap +./mem_protect +./mem_exec + diff --git a/regress/rep_movsb.c b/tests/regress/rep_movsb.c similarity index 100% rename from regress/rep_movsb.c rename to tests/regress/rep_movsb.c diff --git a/regress/ro_mem_test.c b/tests/regress/ro_mem_test.c similarity index 98% rename from regress/ro_mem_test.c rename to tests/regress/ro_mem_test.c index 02e13bac..1e612033 100644 --- a/regress/ro_mem_test.c +++ b/tests/regress/ro_mem_test.c @@ -142,7 +142,7 @@ int main(int argc, char **argv, char **envp) //uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)0x400000, (uint64_t)0x400fff); // intercept invalid memory events - uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL); + uc_hook_add(uc, &trace1, UC_HOOK_MEM_WRITE_INVALID | UC_HOOK_MEM_WRITE_PROT, hook_mem_invalid, NULL); // emulate machine code in infinite time printf("BEGIN execution - 1\n"); diff --git a/regress/sigill.c b/tests/regress/sigill.c similarity index 100% rename from regress/sigill.c rename to tests/regress/sigill.c diff --git a/regress/sigill2.c b/tests/regress/sigill2.c similarity index 100% rename from regress/sigill2.c rename to tests/regress/sigill2.c diff --git a/tests/regress/sparc64.py b/tests/regress/sparc64.py new file mode 100755 index 00000000..27210bd1 --- /dev/null +++ b/tests/regress/sparc64.py @@ -0,0 +1,24 @@ +#!/usr/bin/python + +from unicorn import * +from unicorn.sparc_const import * + +PAGE_SIZE = 1 * 1024 * 1024 + +uc = Uc(UC_ARCH_SPARC, UC_MODE_64) +uc.reg_write(UC_SPARC_REG_SP, 100) +print 'writing sp = 100' + + # 0: b0 06 20 01 inc %i0 + # 4: b2 06 60 01 inc %i1 + +CODE = "\xb0\x06\x20\x01" \ + "\xb2\x06\x60\x01" + +uc.mem_map(0, PAGE_SIZE) +uc.mem_write(0, CODE) +uc.emu_start(0, len(CODE), 0, 2) + +print 'sp =', uc.reg_read(UC_SPARC_REG_SP) +print 'i0 =', uc.reg_read(UC_SPARC_REG_I0) +print 'i1 =', uc.reg_read(UC_SPARC_REG_I1) diff --git a/tests/regress/sparc_reg.py b/tests/regress/sparc_reg.py new file mode 100755 index 00000000..326d00e4 --- /dev/null +++ b/tests/regress/sparc_reg.py @@ -0,0 +1,205 @@ +#!/usr/bin/python + +from unicorn import * +from unicorn.sparc_const import * + +PAGE_SIZE = 1 * 1024 * 1024 + +uc = Uc(UC_ARCH_SPARC, UC_MODE_32) +uc.reg_write(UC_SPARC_REG_SP, 100) +uc.reg_write(UC_SPARC_REG_FP, 200) + + # 0x0: \x80\x00\x20\x01 add %g0, 1, %g0 + # 0x4: \x82\x00\x60\x01 add %g1, 1, %g1 + # 0x8: \x84\x00\xA0\x01 add %g2, 1, %g2 + # 0xc: \x86\x00\xE0\x01 add %g3, 1, %g3 + # 0x10: \x88\x01\x20\x01 add %g4, 1, %g4 + # 0x14: \x8A\x01\x60\x01 add %g5, 1, %g5 + # 0x18: \x8C\x01\xA0\x01 add %g6, 1, %g6 + # 0x1c: \x8E\x01\xE0\x01 add %g7, 1, %g7 + # 0x20: \x90\x02\x20\x01 add %o0, 1, %o0 + # 0x24: \x92\x02\x60\x01 add %o1, 1, %o1 + # 0x28: \x94\x02\xA0\x01 add %o2, 1, %o2 + # 0x2c: \x96\x02\xE0\x01 add %o3, 1, %o3 + # 0x30: \x98\x03\x20\x01 add %o4, 1, %o4 + # 0x34: \x9A\x03\x60\x01 add %o5, 1, %o5 + # 0x38: \x9C\x03\xA0\x01 add %sp, 1, %sp + # 0x3c: \x9E\x03\xE0\x01 add %o7, 1, %o7 + # 0x40: \xA0\x04\x20\x01 add %l0, 1, %l0 + # 0x44: \xA2\x04\x60\x01 add %l1, 1, %l1 + # 0x48: \xA4\x04\xA0\x01 add %l2, 1, %l2 + # 0x4c: \xA6\x04\xE0\x01 add %l3, 1, %l3 + # 0x50: \xA8\x05\x20\x01 add %l4, 1, %l4 + # 0x54: \xAA\x05\x60\x01 add %l5, 1, %l5 + # 0x58: \xAC\x05\xA0\x01 add %l6, 1, %l6 + # 0x5c: \xAE\x05\xE0\x01 add %l7, 1, %l7 + # 0x0: \xB0\x06\x20\x01 add %i0, 1, %i0 + # 0x4: \xB2\x06\x60\x01 add %i1, 1, %i1 + # 0x8: \xB4\x06\xA0\x01 add %i2, 1, %i2 + # 0xc: \xB6\x06\xE0\x01 add %i3, 1, %i3 + # 0x10: \xB8\x07\x20\x01 add %i4, 1, %i4 + # 0x14: \xBA\x07\x60\x01 add %i5, 1, %i5 + # 0x18: \xBC\x07\xA0\x01 add %fp, 1, %fp + # 0x1c: \xBE\x07\xE0\x01 add %i7, 1, %i7 + + +CODE = "\x80\x00\x20\x01" \ + "\x82\x00\x60\x01" \ + "\x84\x00\xA0\x01" \ + "\x86\x00\xE0\x01" \ + "\x88\x01\x20\x01" \ + "\x8A\x01\x60\x01" \ + "\x8C\x01\xA0\x01" \ + "\x8E\x01\xE0\x01" \ + "\x90\x02\x20\x01" \ + "\x92\x02\x60\x01" \ + "\x94\x02\xA0\x01" \ + "\x96\x02\xE0\x01" \ + "\x98\x03\x20\x01" \ + "\x9A\x03\x60\x01" \ + "\x9C\x03\xA0\x01" \ + "\x9E\x03\xE0\x01" \ + "\xA0\x04\x20\x01" \ + "\xA2\x04\x60\x01" \ + "\xA4\x04\xA0\x01" \ + "\xA6\x04\xE0\x01" \ + "\xA8\x05\x20\x01" \ + "\xAA\x05\x60\x01" \ + "\xAC\x05\xA0\x01" \ + "\xAE\x05\xE0\x01" \ + "\xB0\x06\x20\x01" \ + "\xB2\x06\x60\x01" \ + "\xB4\x06\xA0\x01" \ + "\xB6\x06\xE0\x01" \ + "\xB8\x07\x20\x01" \ + "\xBA\x07\x60\x01" \ + "\xBC\x07\xA0\x01" \ + "\xBE\x07\xE0\x01" + + +uc.mem_map(0, PAGE_SIZE) +uc.mem_write(0, CODE) +uc.emu_start(0, len(CODE), 0, 32) + +def print_registers(mu): + g0 = mu.reg_read(UC_SPARC_REG_G0) + g1 = mu.reg_read(UC_SPARC_REG_G1) + g2 = mu.reg_read(UC_SPARC_REG_G2) + g3 = mu.reg_read(UC_SPARC_REG_G3) + g4 = mu.reg_read(UC_SPARC_REG_G4) + g5 = mu.reg_read(UC_SPARC_REG_G5) + g6 = mu.reg_read(UC_SPARC_REG_G6) + g7 = mu.reg_read(UC_SPARC_REG_G7) + + o0 = mu.reg_read(UC_SPARC_REG_O0) + o1 = mu.reg_read(UC_SPARC_REG_O1) + o2 = mu.reg_read(UC_SPARC_REG_O2) + o3 = mu.reg_read(UC_SPARC_REG_O3) + o4 = mu.reg_read(UC_SPARC_REG_O4) + o5 = mu.reg_read(UC_SPARC_REG_O5) + o6 = mu.reg_read(UC_SPARC_REG_O6) + o7 = mu.reg_read(UC_SPARC_REG_O7) + + l0 = mu.reg_read(UC_SPARC_REG_L0) + l1 = mu.reg_read(UC_SPARC_REG_L1) + l2 = mu.reg_read(UC_SPARC_REG_L2) + l3 = mu.reg_read(UC_SPARC_REG_L3) + l4 = mu.reg_read(UC_SPARC_REG_L4) + l5 = mu.reg_read(UC_SPARC_REG_L5) + l6 = mu.reg_read(UC_SPARC_REG_L6) + l7 = mu.reg_read(UC_SPARC_REG_L7) + + i0 = mu.reg_read(UC_SPARC_REG_I0) + i1 = mu.reg_read(UC_SPARC_REG_I1) + i2 = mu.reg_read(UC_SPARC_REG_I2) + i3 = mu.reg_read(UC_SPARC_REG_I3) + i4 = mu.reg_read(UC_SPARC_REG_I4) + i5 = mu.reg_read(UC_SPARC_REG_I5) + i6 = mu.reg_read(UC_SPARC_REG_I6) + i7 = mu.reg_read(UC_SPARC_REG_I7) + + pc = mu.reg_read(UC_SPARC_REG_PC) + sp = mu.reg_read(UC_SPARC_REG_SP) + fp = mu.reg_read(UC_SPARC_REG_FP) + print(" G0 = %d" % g0) + print(" G1 = %d" % g1) + print(" G2 = %d" % g2) + print(" G3 = %d" % g3) + print(" G4 = %d" % g4) + print(" G5 = %d" % g5) + print(" G6 = %d" % g6) + print(" G7 = %d" % g7) + print("") + print(" O0 = %d" % o0) + print(" O1 = %d" % o1) + print(" O2 = %d" % o2) + print(" O3 = %d" % o3) + print(" O4 = %d" % o4) + print(" O5 = %d" % o5) + print(" O6 = %d" % o6) + print(" O7 = %d" % o7) + print("") + print(" L0 = %d" % l0) + print(" L1 = %d" % l1) + print(" L2 = %d" % l2) + print(" L3 = %d" % l3) + print(" L4 = %d" % l4) + print(" L5 = %d" % l5) + print(" L6 = %d" % l6) + print(" L7 = %d" % l7) + print("") + print(" I0 = %d" % i0) + print(" I1 = %d" % i1) + print(" I2 = %d" % i2) + print(" I3 = %d" % i3) + print(" I4 = %d" % i4) + print(" I5 = %d" % i5) + print(" I6 = %d" % i6) + print(" I7 = %d" % i7) + print("") + print(" PC = %d" % pc) + print(" SP = %d" % sp) + print(" FP = %d" % fp) + print("") + +print_registers(uc) + +assert uc.reg_read(UC_SPARC_REG_PC) == 128 # make sure we executed all instructions +assert uc.reg_read(UC_SPARC_REG_SP) == 101 +assert uc.reg_read(UC_SPARC_REG_FP) == 201 + +assert uc.reg_read(UC_SPARC_REG_G0) == 0 # G0 is always zero +assert uc.reg_read(UC_SPARC_REG_G1) == 1 +assert uc.reg_read(UC_SPARC_REG_G2) == 1 +assert uc.reg_read(UC_SPARC_REG_G3) == 1 +assert uc.reg_read(UC_SPARC_REG_G4) == 1 +assert uc.reg_read(UC_SPARC_REG_G5) == 1 +assert uc.reg_read(UC_SPARC_REG_G6) == 1 +assert uc.reg_read(UC_SPARC_REG_G7) == 1 + +assert uc.reg_read(UC_SPARC_REG_O0) == 1 +assert uc.reg_read(UC_SPARC_REG_O1) == 1 +assert uc.reg_read(UC_SPARC_REG_O2) == 1 +assert uc.reg_read(UC_SPARC_REG_O3) == 1 +assert uc.reg_read(UC_SPARC_REG_O4) == 1 +assert uc.reg_read(UC_SPARC_REG_O5) == 1 +assert uc.reg_read(UC_SPARC_REG_O6) == 101 +assert uc.reg_read(UC_SPARC_REG_O7) == 1 + +assert uc.reg_read(UC_SPARC_REG_L0) == 1 +assert uc.reg_read(UC_SPARC_REG_L1) == 1 +assert uc.reg_read(UC_SPARC_REG_L2) == 1 +assert uc.reg_read(UC_SPARC_REG_L3) == 1 +assert uc.reg_read(UC_SPARC_REG_L4) == 1 +assert uc.reg_read(UC_SPARC_REG_L5) == 1 +assert uc.reg_read(UC_SPARC_REG_L6) == 1 +assert uc.reg_read(UC_SPARC_REG_L7) == 1 + +assert uc.reg_read(UC_SPARC_REG_I0) == 1 +assert uc.reg_read(UC_SPARC_REG_I1) == 1 +assert uc.reg_read(UC_SPARC_REG_I2) == 1 +assert uc.reg_read(UC_SPARC_REG_I3) == 1 +assert uc.reg_read(UC_SPARC_REG_I4) == 1 +assert uc.reg_read(UC_SPARC_REG_I5) == 1 +assert uc.reg_read(UC_SPARC_REG_I6) == 201 +assert uc.reg_read(UC_SPARC_REG_I7) == 1 diff --git a/regress/timeout_segfault.c b/tests/regress/timeout_segfault.c similarity index 100% rename from regress/timeout_segfault.c rename to tests/regress/timeout_segfault.c diff --git a/tests/regress/wrong_rip.py b/tests/regress/wrong_rip.py new file mode 100755 index 00000000..87549806 --- /dev/null +++ b/tests/regress/wrong_rip.py @@ -0,0 +1,71 @@ +#!/usr/bin/python + +from unicorn import * +from unicorn.x86_const import * + +import regress + +binary1 = b'\xb8\x02\x00\x00\x00' # mov eax, 2 +binary2 = b'\xb8\x01\x00\x00\x00' # mov eax, 1 + +class WrongRIP(regress.RegressTest): + + def test_step(self): + mu = Uc(UC_ARCH_X86, UC_MODE_64) + mu.mem_map(0, 2 * 1024 * 1024) + # write machine code to be emulated to memory + mu.mem_write(0, binary1 + binary2) + # emu for maximum 1 instruction. + mu.emu_start(0, 5, 0, 1) + + self.assertEqual(0x2, mu.reg_read(UC_X86_REG_RAX)) + self.assertEqual(0x5, mu.reg_read(UC_X86_REG_RIP)) + + mu.emu_start(5, 10, 0, 1) + self.assertEqual(0xa, mu.reg_read(UC_X86_REG_RIP)) + self.assertEqual(0x1, mu.reg_read(UC_X86_REG_RAX)) + + def test_step2(self): + mu = Uc(UC_ARCH_X86, UC_MODE_64) + mu.mem_map(0, 2 * 1024 * 1024) + # write machine code to be emulated to memory + mu.mem_write(0, binary1 + binary2) + # emu for maximum 1 instruction. + mu.emu_start(0, 10, 0, 1) + self.assertEqual(0x2, mu.reg_read(UC_X86_REG_RAX)) + self.assertEqual(0x5, mu.reg_read(UC_X86_REG_RIP)) + + mu.emu_start(5, 10, 0, 1) + self.assertEqual(0x1, mu.reg_read(UC_X86_REG_RAX)) + self.assertEqual(0xa, mu.reg_read(UC_X86_REG_RIP)) + + def test_step3(self): + bin3 = b'\x40\x01\xc1\x31\xf6' # inc eax; add ecx, eax; xor esi, esi + mu = Uc(UC_ARCH_X86, UC_MODE_32) + mu.mem_map(0, 2 * 1024 * 1024) + # write machine code to be emulated to memory + mu.mem_write(0, bin3) + # emu for maximum 1 instruction. + mu.emu_start(0, 10, 0, 1) + self.assertEqual(0x1, mu.reg_read(UC_X86_REG_EAX)) + self.assertEqual(0x1, mu.reg_read(UC_X86_REG_EIP)) + + def test_step_then_fin(self): + bin4 = b'\x40\x01\xc1\x31\xf6\x90\x90\x90' # inc eax; add ecx, eax; xor esi, esi + mu = Uc(UC_ARCH_X86, UC_MODE_32) + mu.mem_map(0, 2 * 1024 * 1024) + # write machine code to be emulated to memory + mu.mem_write(0, bin4) + # emu for maximum 1 instruction. + mu.emu_start(0, len(binary1), 0, 1) + + self.assertEqual(0x1, mu.reg_read(UC_X86_REG_EAX)) + self.assertEqual(0x1, mu.reg_read(UC_X86_REG_EIP)) + # emu to the end + mu.emu_start(1, len(bin4)) + self.assertEqual(0x1, mu.reg_read(UC_X86_REG_EAX)) + self.assertEqual(len(bin4), mu.reg_read(UC_X86_REG_EIP)) + +if __name__ == '__main__': + regress.main() + diff --git a/tests/regress/wrong_rip_arm.py b/tests/regress/wrong_rip_arm.py new file mode 100755 index 00000000..de710d85 --- /dev/null +++ b/tests/regress/wrong_rip_arm.py @@ -0,0 +1,38 @@ +#!/usr/bin/python + +from unicorn import * +from unicorn.x86_const import * +from unicorn.arm_const import * + +import regress + +# adds r1, #0x48 +# ldrsb r7, [r7, r7] +# ldrsh r7, [r2, r1] +# ldr r0, [pc, #0x168] +# cmp r7, #0xbf +# str r7, [r5, #0x20] +# ldr r1, [r5, #0x64] +# strb r7, [r5, #0xc] +# ldr r0, [pc, #0x1a0] +binary1 = b'\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05' +# binary1 = b'\x48\x31\xff\x57' +#adds r1, #0x48 +#ldrsb r7, [r7, r7] + +class WrongRIPArm(regress.RegressTest): + + def runTest(self): + mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB) + mu.mem_map(0, 2 * 1024 * 1024) + # write machine code to be emulated to memory + mu.mem_write(0, binary1) + mu.reg_write(UC_ARM_REG_R13, 1 * 1024 * 1024) + # emu for maximum 1 instruction. + mu.emu_start(0, len(binary1), 0, 1) + self.assertEqual(0x48, mu.reg_read(UC_ARM_REG_R1)) + pos = mu.reg_read(UC_ARM_REG_R15) + self.assertEqual(0x2, pos) + +if __name__ == '__main__': + regress.main() diff --git a/tests/regress/wrong_sp_arm.py b/tests/regress/wrong_sp_arm.py new file mode 100755 index 00000000..13dbd36c --- /dev/null +++ b/tests/regress/wrong_sp_arm.py @@ -0,0 +1,28 @@ +#!/usr/bin/python +# By Ryan Hileman, issue #16 + +from unicorn import * +from unicorn.arm_const import * +from unicorn.arm64_const import * + +import regress + +class WrongSPArm(regress.RegressTest): + + def test_32(self): + with self.assertRaises(UcError): + uc = Uc(UC_ARCH_ARM, UC_MODE_32) + uc.reg_write(UC_ARM_REG_SP, 4) + + def test_64(self): + uc = Uc(UC_ARCH_ARM64, UC_MODE_ARM) + uc.reg_write(UC_ARM64_REG_SP, 4) + self.assertEqual(0x4, uc.reg_read(UC_ARM64_REG_SP)) + + def test_arm(self): + uc = Uc(UC_ARCH_ARM, UC_MODE_ARM) + uc.reg_write(UC_ARM_REG_SP, 4) + self.assertEqual(0x4, uc.reg_read(UC_ARM_REG_SP)) + +if __name__ == '__main__': + regress.main() diff --git a/tests/unit/.gitignore b/tests/unit/.gitignore new file mode 100644 index 00000000..39d66b15 --- /dev/null +++ b/tests/unit/.gitignore @@ -0,0 +1,3 @@ +test_x86 +test_mem_map +test_sanity diff --git a/tests/unit/Makefile b/tests/unit/Makefile new file mode 100644 index 00000000..ae66d08e --- /dev/null +++ b/tests/unit/Makefile @@ -0,0 +1,31 @@ + +CFLAGS += -Wall -Werror -Wno-unused-function -g +CFLAGS += -L ../../ +CFLAGS += -lcmocka -lunicorn +CFLAGS += -I ../../include + +ALL_TESTS = test_sanity test_x86 test_mem_map + +.PHONY: all +all: ${ALL_TESTS} + +.PHONY: clean +clean: + rm -rf ${ALL_TESTS} + +.PHONY: test +test: export LD_LIBRARY_PATH=../../ +test: ${ALL_TESTS} + ./test_sanity + ./test_x86 + ./test_mem_map + +test_sanity: test_sanity.c +test_x86: test_x86.c +test_mem_map: test_mem_map.c + +${ALL_TESTS}: + gcc ${CFLAGS} -o $@ $^ + + + diff --git a/tests/unit/test_mem_map.c b/tests/unit/test_mem_map.c new file mode 100644 index 00000000..13b96d9d --- /dev/null +++ b/tests/unit/test_mem_map.c @@ -0,0 +1,143 @@ +/** + * Unicorn memory API tests + * + * This tests memory read/write and map/unmap functionality. + * One is necessary for doing the other. + */ +#include "unicorn_test.h" +#include +#include + +/* Called before every test to set up a new instance */ +static int setup(void **state) +{ + uc_engine *uc; + + uc_assert_success(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); + + *state = uc; + return 0; +} + +/* Called after every test to clean up */ +static int teardown(void **state) +{ + uc_engine *uc = *state; + + uc_assert_success(uc_close(uc)); + + *state = NULL; + return 0; +} + +/******************************************************************************/ + + +/** + * A basic test showing mapping of memory, and reading/writing it + */ +static void test_basic(void **state) +{ + uc_engine *uc = *state; + const uint64_t mem_start = 0x1000; + const uint64_t mem_len = 0x1000; + const uint64_t test_addr = mem_start + 0x100; + + /* Map a region */ + uc_assert_success(uc_mem_map(uc, mem_start, mem_len, UC_PROT_NONE)); + + /* Write some data to it */ + uc_assert_success(uc_mem_write(uc, test_addr, "test", 4)); + + uint8_t buf[4]; + memset(buf, 0xCC, sizeof(buf)); + + /* Read it back */ + uc_assert_success(uc_mem_read(uc, test_addr, buf, sizeof(buf))); + + /* And make sure it matches what we expect */ + assert_memory_equal(buf, "test", 4); + + /* Unmap the region */ + //uc_assert_success(uc_mem_unmap(uc, mem_start, mem_len)); +} + +static void test_bad_read(void **state) +{ + uc_engine *uc = *state; + + uint8_t readbuf[0x10]; + memset(readbuf, 0xCC, sizeof(readbuf)); + + uint8_t checkbuf[0x10]; + memset(checkbuf, 0xCC, sizeof(checkbuf)); + + /* Reads to unmapped addresses should fail */ + /* TODO: Which error? */ + uc_assert_fail(uc_mem_read(uc, 0x1000, readbuf, sizeof(readbuf))); + + /* And our buffer should be unchanged */ + assert_memory_equal(readbuf, checkbuf, sizeof(checkbuf)); +} + +static void test_bad_write(void **state) +{ + uc_engine *uc = *state; + + uint8_t writebuf[0x10]; + memset(writebuf, 0xCC, sizeof(writebuf)); + + /* Writes to unmapped addresses should fail */ + /* TODO: Which error? */ + uc_assert_fail(uc_mem_write(uc, 0x1000, writebuf, sizeof(writebuf))); +} + + + +/** + * Verify that we can read/write across memory map region boundaries + */ +static void test_rw_across_boundaries(void **state) +{ + uc_engine *uc = *state; + + /* Map in two adjacent regions */ + uc_assert_success(uc_mem_map(uc, 0, 0x1000, 0)); /* 0x0000 - 0x1000 */ + uc_assert_success(uc_mem_map(uc, 0x1000, 0x1000, 0)); /* 0x1000 - 0x2000 */ + + const uint64_t addr = 0x1000 - 2; /* 2 bytes before end of block */ + + /* Write some data across the boundary */ + uc_assert_success(uc_mem_write(uc, addr, "test", 4)); + + uint8_t buf[4]; + memset(buf, 0xCC, sizeof(buf)); + + /* Read the data across the boundary */ + uc_assert_success(uc_mem_read(uc, addr, buf, sizeof(buf))); + + assert_memory_equal(buf, "test", 4); +} + +/* Try to unmap memory that has not been mapped */ +static void test_bad_unmap(void **state) +{ + uc_engine *uc = *state; + + /* TODO: Which error should this return? */ + uc_assert_fail(uc_mem_unmap(uc, 0x0, 0x1000)); +} + + +int main(void) { +#define test(x) cmocka_unit_test_setup_teardown(x, setup, teardown) + const struct CMUnitTest tests[] = { + test(test_basic), + //test(test_bad_read), + //test(test_bad_write), + test(test_bad_unmap), + test(test_rw_across_boundaries), + }; +#undef test + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/unit/test_sanity.c b/tests/unit/test_sanity.c new file mode 100644 index 00000000..9788658b --- /dev/null +++ b/tests/unit/test_sanity.c @@ -0,0 +1,88 @@ +#include "unicorn_test.h" + +/* Make sure the uc_assert macros work with constants */ +static void test_uc_assert_macros_constants(void **state) +{ + const uc_err nomem = UC_ERR_NOMEM; + + uc_assert_success(UC_ERR_OK); + uc_assert_err(UC_ERR_NOMEM, nomem); + uc_assert_fail(UC_ERR_VERSION); +} + +/******************************************************************************/ + +static uc_err feedback(uc_err err, int *callcount) +{ + assert_int_equal(++(*callcount), 1); + return err; +} + +/** + * Make sure the uc_assert macros work with function calls + * and only evaluate them once! + */ +static void test_uc_assert_macros_func_calls(void **state) +{ + int callcount; + + callcount = 0; + uc_assert_success(feedback(UC_ERR_OK, &callcount)); + + callcount = 0; + uc_assert_err(UC_ERR_NOMEM, feedback(UC_ERR_NOMEM, &callcount)); + + callcount = 0; + uc_assert_fail(feedback(UC_ERR_VERSION, &callcount)); +} + +/******************************************************************************/ + +static void fail_uc_assert_success(void **state) +{ + uc_assert_success(UC_ERR_NOMEM); +} + +static void fail_uc_assert_err(void **state) +{ + const uc_err ok = UC_ERR_OK; + uc_assert_err(UC_ERR_VERSION, ok); +} + +static void fail_uc_assert_fail(void **state) +{ + uc_assert_fail(UC_ERR_OK); +} + +static void test_uc_assert_macros_fail(void **state) +{ + /* A test-inside-a-test */ + + const struct CMUnitTest tests[] = { + /* these should all fail */ + cmocka_unit_test(fail_uc_assert_success), + cmocka_unit_test(fail_uc_assert_err), + cmocka_unit_test(fail_uc_assert_fail), + }; + + print_message("\n\n--------------------------------------------------------------------------------\n"); + print_message("START: Failure of the following tests is expected.\n\n"); + + assert_int_not_equal(0, cmocka_run_group_tests(tests, NULL, NULL)); + + print_message("\n\nEND: Failure of the preceding tests was expected.\n"); + print_message("--------------------------------------------------------------------------------\n\n"); +} + +/******************************************************************************/ + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_uc_assert_macros_constants), + cmocka_unit_test(test_uc_assert_macros_func_calls), + cmocka_unit_test(test_uc_assert_macros_fail), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/unit/test_x86.c b/tests/unit/test_x86.c new file mode 100644 index 00000000..e1d6856c --- /dev/null +++ b/tests/unit/test_x86.c @@ -0,0 +1,750 @@ +#include "unicorn_test.h" +#include + +#define OK(x) uc_assert_success(x) + +/* Called before every test to set up a new instance */ +static int setup32(void **state) +{ + uc_engine *uc; + + OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); + + *state = uc; + return 0; +} + +/* Called after every test to clean up */ +static int teardown(void **state) +{ + uc_engine *uc = *state; + + OK(uc_close(uc)); + + *state = NULL; + return 0; +} + +/******************************************************************************/ + +struct bb { + uint64_t addr; + size_t size; +}; + +struct bbtest { + const struct bb *blocks; + unsigned int blocknum; +}; + + +static void test_basic_blocks_hook(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) +{ + struct bbtest *bbtest = user_data; + const struct bb *bb = &bbtest->blocks[bbtest->blocknum++]; + + assert_int_equal(address, bb->addr); + assert_int_equal((size_t)size, bb->size); +} + +static void test_basic_blocks(void **state) +{ + uc_engine *uc = *state; + uc_hook trace1; + +#define BASEADDR 0x1000000 + + uint64_t address = BASEADDR; + const uint8_t code[] = { + 0x33, 0xC0, // xor eax, eax + 0x90, // nop + 0x90, // nop + 0xEB, 0x00, // jmp $+2 + 0x90, // nop + 0x90, // nop + 0x90, // nop + }; + + static const struct bb blocks[] = { + {BASEADDR, 6}, + {BASEADDR+ 6, 3}, + }; + + struct bbtest bbtest = { + .blocks = blocks, + .blocknum = 0, + }; + + +#undef BASEADDR + + // map 2MB memory for this emulation + OK(uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL)); + + // write machine code to be emulated to memory + OK(uc_mem_write(uc, address, code, sizeof(code))); + + // trace all basic blocks + OK(uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, test_basic_blocks_hook, &bbtest, (uint64_t)1, (uint64_t)0)); + + OK(uc_emu_start(uc, address, address+sizeof(code), 0, 0)); +} + +/******************************************************************************/ + +// callback for tracing basic blocks +static void hook_block(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) +{ + //printf(">>> Tracing basic block at 0x%"PRIx64 ", block size = 0x%x\n", address, size); +} + +// callback for tracing instruction +static void hook_code(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) +{ + //int eflags; + //printf(">>> Tracing instruction at 0x%"PRIx64 ", instruction size = 0x%x\n", address, size); + + //uc_reg_read(uc, UC_X86_REG_EFLAGS, &eflags); + //printf(">>> --- EFLAGS is 0x%x\n", eflags); + + // Uncomment below code to stop the emulation using uc_emu_stop() + // if (address == 0x1000009) + // uc_emu_stop(uc); +} + +static void test_i386(void **state) +{ + uc_engine *uc; + uc_err err; + uint32_t tmp; + uc_hook trace1, trace2; + + const uint8_t code[] = "\x41\x4a"; // INC ecx; DEC edx + const uint64_t address = 0x1000000; + + int r_ecx = 0x1234; // ECX register + int r_edx = 0x7890; // EDX register + + // Initialize emulator in X86-32bit mode + err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc); + uc_assert_success(err); + + // map 2MB memory for this emulation + err = uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL); + uc_assert_success(err); + + // write machine code to be emulated to memory + err = uc_mem_write(uc, address, code, sizeof(code)-1); + uc_assert_success(err); + + // initialize machine registers + err = uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx); + uc_assert_success(err); + err = uc_reg_write(uc, UC_X86_REG_EDX, &r_edx); + uc_assert_success(err); + + // tracing all basic blocks with customized callback + err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0); + uc_assert_success(err); + + // tracing all instruction by having @begin > @end + err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0); + uc_assert_success(err); + + // emulate machine code in infinite time + err = uc_emu_start(uc, address, address+sizeof(code)-1, 0, 0); + uc_assert_success(err); + + // now print out some registers + //printf(">>> Emulation done. Below is the CPU context\n"); + + uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx); + uc_reg_read(uc, UC_X86_REG_EDX, &r_edx); + + assert_int_equal(r_ecx, 0x1235); + assert_int_equal(r_edx, 0x788F); + + // read from memory + err = uc_mem_read(uc, address, (uint8_t *)&tmp, 4); + uc_assert_success(err); + //printf(">>> Read 4 bytes from [0x%"PRIX64"] = 0x%x\n", address, tmp); + + uc_close(uc); +} + +static void test_i386_jump(void **state) +{ + uc_engine *uc; + uc_err err; + uc_hook trace1, trace2; + + const uint8_t code[] = "\xeb\x02\x90\x90\x90\x90\x90\x90"; // jmp 4; nop; nop; nop; nop; nop; nop + const uint64_t address = 0x1000000; + + // Initialize emulator in X86-32bit mode + err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc); + uc_assert_success(err); + + // map 2MB memory for this emulation + err = uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL); + uc_assert_success(err); + + // write machine code to be emulated to memory + err = uc_mem_write(uc, address, code, sizeof(code)-1); + uc_assert_success(err); + + // tracing 1 basic block with customized callback + err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)address, (uint64_t)address); + uc_assert_success(err); + + // tracing 1 instruction at address + err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)address, (uint64_t)address); + uc_assert_success(err); + + // emulate machine code in infinite time + err = uc_emu_start(uc, address, address+sizeof(code)-1, 0, 0); + uc_assert_success(err); + + err = uc_close(uc); + uc_assert_success(err); +} + +/******************************************************************************/ + +// callback for IN instruction (X86). +// this returns the data read from the port +static uint32_t hook_in(uc_engine *uc, uint32_t port, int size, void *user_data) +{ + uint32_t eip; + + uc_reg_read(uc, UC_X86_REG_EIP, &eip); + + //printf("--- reading from port 0x%x, size: %u, address: 0x%x\n", port, size, eip); + + switch(size) { + default: + return 0; // should never reach this + case 1: + // read 1 byte to AL + return 0xf1; + case 2: + // read 2 byte to AX + return 0xf2; + case 4: + // read 4 byte to EAX + return 0xf4; + } +} + +// callback for OUT instruction (X86). +static void hook_out(uc_engine *uc, uint32_t port, int size, uint32_t value, void *user_data) +{ + uint32_t tmp; + uint32_t eip; + + uc_reg_read(uc, UC_X86_REG_EIP, &eip); + + //printf("--- writing to port 0x%x, size: %u, value: 0x%x, address: 0x%x\n", port, size, value, eip); + + // TODO: confirm that value is indeed the value of AL/AX/EAX + switch(size) { + default: + return; // should never reach this + case 1: + uc_reg_read(uc, UC_X86_REG_AL, &tmp); + break; + case 2: + uc_reg_read(uc, UC_X86_REG_AX, &tmp); + break; + case 4: + uc_reg_read(uc, UC_X86_REG_EAX, &tmp); + break; + } + + //printf("--- register value = 0x%x\n", tmp); +} + +static void test_i386_inout(void **state) +{ + uc_engine *uc; + uc_err err; + uc_hook trace1, trace2, trace3, trace4; + + int r_eax = 0x1234; // EAX register + int r_ecx = 0x6789; // ECX register + + static const uint64_t address = 0x1000000; + static const uint8_t code[] = { + 0x41, // inc ecx + 0xE4, 0x3F, // in al, 0x3F + 0x4A, // dec edx + 0xE6, 0x46, // out 0x46, al + 0x43, // inc ebx + }; + + + // Initialize emulator in X86-32bit mode + err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc); + uc_assert_success(err); + + // map 2MB memory for this emulation + err = uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL); + uc_assert_success(err); + + // write machine code to be emulated to memory + err = uc_mem_write(uc, address, code, sizeof(code)); + uc_assert_success(err); + + // initialize machine registers + err = uc_reg_write(uc, UC_X86_REG_EAX, &r_eax); + uc_assert_success(err); + err = uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx); + uc_assert_success(err); + + // tracing all basic blocks with customized callback + err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0); + uc_assert_success(err); + + // tracing all instructions + err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0); + uc_assert_success(err); + + // uc IN instruction + err = uc_hook_add(uc, &trace3, UC_HOOK_INSN, hook_in, NULL, UC_X86_INS_IN); + uc_assert_success(err); + + // uc OUT instruction + err = uc_hook_add(uc, &trace4, UC_HOOK_INSN, hook_out, NULL, UC_X86_INS_OUT); + uc_assert_success(err); + + // emulate machine code in infinite time + err = uc_emu_start(uc, address, address+sizeof(code), 0, 0); + uc_assert_success(err); + + uc_reg_read(uc, UC_X86_REG_EAX, &r_eax); + uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx); + //printf(">>> EAX = 0x%x\n", r_eax); + //printf(">>> ECX = 0x%x\n", r_ecx); + // TODO: Assert on the register values here + + uc_assert_success(uc_close(uc)); +} + +/******************************************************************************/ + +// emulate code that loop forever +static void test_i386_loop(void **state) +{ + uc_engine *uc; + uc_err err; + + int r_ecx = 0x1234; // ECX register + int r_edx = 0x7890; // EDX register + + static const uint64_t address = 0x1000000; + static const uint8_t code[] = { + 0x41, // inc ecx + 0x4a, // dec edx + 0xEB, 0xFE, // jmp $ + }; + + // Initialize emulator in X86-32bit mode + err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc); + uc_assert_success(err); + + // map 2MB memory for this emulation + err = uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL); + uc_assert_success(err); + + // write machine code to be emulated to memory + err = uc_mem_write(uc, address, code, sizeof(code)); + uc_assert_success(err); + + // initialize machine registers + err = uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx); + uc_assert_success(err); + err = uc_reg_write(uc, UC_X86_REG_EDX, &r_edx); + uc_assert_success(err); + + // emulate machine code in 2 seconds, so we can quit even + // if the code loops + err = uc_emu_start(uc, address, address+sizeof(code), 2*UC_SECOND_SCALE, 0); + uc_assert_success(err); + + // verify register values + uc_assert_success(uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx)); + uc_assert_success(uc_reg_read(uc, UC_X86_REG_EDX, &r_edx)); + + assert_int_equal(r_ecx, 0x1235); + assert_int_equal(r_edx, 0x788F); + + uc_assert_success(uc_close(uc)); +} + +/******************************************************************************/ + +// emulate code that reads invalid memory +static void test_i386_invalid_mem_read(void **state) +{ + uc_engine *uc; + uc_err err; + + static const uint64_t address = 0x1000000; + static const uint8_t code[] = { + 0x8b, 0x0D, 0xAA, 0xAA, 0xAA, 0xAA, // mov ecx, [0xAAAAAAAA] + }; + + // Initialize emulator in X86-32bit mode + err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc); + uc_assert_success(err); + + // map 2MB memory for this emulation + err = uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL); + uc_assert_success(err); + + // write machine code to be emulated to memory + err = uc_mem_write(uc, address, code, sizeof(code)); + uc_assert_success(err); + + // emulate machine code in infinite time + err = uc_emu_start(uc, address, address+sizeof(code), 0, 0); + uc_assert_err(UC_ERR_READ_INVALID, err); + + uc_assert_success(uc_close(uc)); +} + +// emulate code that writes invalid memory +static void test_i386_invalid_mem_write(void **state) +{ + uc_engine *uc; + uc_err err; + + static const uint64_t address = 0x1000000; + static const uint8_t code[] = { + 0x89, 0x0D, 0xAA, 0xAA, 0xAA, 0xAA, // mov [0xAAAAAAAA], ecx + }; + + // Initialize emulator in X86-32bit mode + err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc); + uc_assert_success(err); + + // map 2MB memory for this emulation + err = uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL); + uc_assert_success(err); + + // write machine code to be emulated to memory + err = uc_mem_write(uc, address, code, sizeof(code)); + uc_assert_success(err); + + // emulate machine code in infinite time + err = uc_emu_start(uc, address, address+sizeof(code), 0, 0); + uc_assert_err(UC_ERR_WRITE_INVALID, err); + + + uc_assert_success(uc_close(uc)); +} + +// emulate code that jumps to invalid memory +static void test_i386_jump_invalid(void **state) +{ + uc_engine *uc; + uc_err err; + + static const uint64_t address = 0x1000000; + static const uint8_t code[] = { + 0xE9, 0xE9, 0xEE, 0xEE, 0xEE, // jmp 0xEEEEEEEE + }; + + // Initialize emulator in X86-32bit mode + err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc); + uc_assert_success(err); + + // map 2MB memory for this emulation + err = uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL); + uc_assert_success(err); + + // write machine code to be emulated to memory + err = uc_mem_write(uc, address, code, sizeof(code)); + uc_assert_success(err); + + // emulate machine code in infinite time + err = uc_emu_start(uc, address, address+sizeof(code), 0, 0); + uc_assert_err(UC_ERR_CODE_INVALID, err); + + + uc_assert_success(uc_close(uc)); +} + + +/******************************************************************************/ + +static void hook_mem64(uc_engine *uc, uc_mem_type type, + uint64_t address, int size, int64_t value, void *user_data) +{ + switch(type) { + default: break; + case UC_MEM_READ: + //printf(">>> Memory is being READ at 0x%"PRIx64 ", data size = %u\n", + // address, size); + break; + case UC_MEM_WRITE: + //printf(">>> Memory is being WRITE at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", + // address, size, value); + break; + } +} + +// callback for tracing instruction +static void hook_code64(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) +{ + uint64_t rip; + + uc_reg_read(uc, UC_X86_REG_RIP, &rip); + //printf(">>> Tracing instruction at 0x%"PRIx64 ", instruction size = 0x%x\n", address, size); + //printf(">>> RIP is 0x%"PRIx64 "\n", rip); + + // Uncomment below code to stop the emulation using uc_emu_stop() + // if (address == 0x1000009) + // uc_emu_stop(uc); +} + +static void test_x86_64(void **state) +{ + uc_engine *uc; + uc_err err; + uc_hook trace1, trace2, trace3, trace4; + + static const uint64_t address = 0x1000000; + static const uint8_t code[] = "\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"; + + int64_t rax = 0x71f3029efd49d41d; + int64_t rbx = 0xd87b45277f133ddb; + int64_t rcx = 0xab40d1ffd8afc461; + int64_t rdx = 0x919317b4a733f01; + int64_t rsi = 0x4c24e753a17ea358; + int64_t rdi = 0xe509a57d2571ce96; + int64_t r8 = 0xea5b108cc2b9ab1f; + int64_t r9 = 0x19ec097c8eb618c1; + int64_t r10 = 0xec45774f00c5f682; + int64_t r11 = 0xe17e9dbec8c074aa; + int64_t r12 = 0x80f86a8dc0f6d457; + int64_t r13 = 0x48288ca5671c5492; + int64_t r14 = 0x595f72f6e4017f6e; + int64_t r15 = 0x1efd97aea331cccc; + + int64_t rsp = address + 0x200000; + + + // Initialize emulator in X86-64bit mode + err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc); + uc_assert_success(err); + + // map 2MB memory for this emulation + err = uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL); + uc_assert_success(err); + + // write machine code to be emulated to memory + err = uc_mem_write(uc, address, code, sizeof(code) - 1); + uc_assert_success(err); + + // initialize machine registers + uc_assert_success(uc_reg_write(uc, UC_X86_REG_RSP, &rsp)); + + uc_assert_success(uc_reg_write(uc, UC_X86_REG_RAX, &rax)); + uc_assert_success(uc_reg_write(uc, UC_X86_REG_RBX, &rbx)); + uc_assert_success(uc_reg_write(uc, UC_X86_REG_RCX, &rcx)); + uc_assert_success(uc_reg_write(uc, UC_X86_REG_RDX, &rdx)); + uc_assert_success(uc_reg_write(uc, UC_X86_REG_RSI, &rsi)); + uc_assert_success(uc_reg_write(uc, UC_X86_REG_RDI, &rdi)); + uc_assert_success(uc_reg_write(uc, UC_X86_REG_R8, &r8)); + uc_assert_success(uc_reg_write(uc, UC_X86_REG_R9, &r9)); + uc_assert_success(uc_reg_write(uc, UC_X86_REG_R10, &r10)); + uc_assert_success(uc_reg_write(uc, UC_X86_REG_R11, &r11)); + uc_assert_success(uc_reg_write(uc, UC_X86_REG_R12, &r12)); + uc_assert_success(uc_reg_write(uc, UC_X86_REG_R13, &r13)); + uc_assert_success(uc_reg_write(uc, UC_X86_REG_R14, &r14)); + uc_assert_success(uc_reg_write(uc, UC_X86_REG_R15, &r15)); + + // tracing all basic blocks with customized callback + err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0); + uc_assert_success(err); + + // tracing all instructions in the range [address, address+20] + err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code64, NULL, (uint64_t)address, (uint64_t)(address+20)); + uc_assert_success(err); + + // tracing all memory WRITE access (with @begin > @end) + err = uc_hook_add(uc, &trace3, UC_HOOK_MEM_WRITE, hook_mem64, NULL, (uint64_t)1, (uint64_t)0); + uc_assert_success(err); + + // tracing all memory READ access (with @begin > @end) + err = uc_hook_add(uc, &trace4, UC_HOOK_MEM_READ, hook_mem64, NULL, (uint64_t)1, (uint64_t)0); + uc_assert_success(err); + + // emulate machine code in infinite time (last param = 0), or when + // finishing all the code. + err = uc_emu_start(uc, address, address+sizeof(code) - 1, 0, 0); + uc_assert_success(err); + + // Read registers + uc_reg_read(uc, UC_X86_REG_RAX, &rax); + uc_reg_read(uc, UC_X86_REG_RBX, &rbx); + uc_reg_read(uc, UC_X86_REG_RCX, &rcx); + uc_reg_read(uc, UC_X86_REG_RDX, &rdx); + uc_reg_read(uc, UC_X86_REG_RSI, &rsi); + uc_reg_read(uc, UC_X86_REG_RDI, &rdi); + uc_reg_read(uc, UC_X86_REG_R8, &r8); + uc_reg_read(uc, UC_X86_REG_R9, &r9); + uc_reg_read(uc, UC_X86_REG_R10, &r10); + uc_reg_read(uc, UC_X86_REG_R11, &r11); + uc_reg_read(uc, UC_X86_REG_R12, &r12); + uc_reg_read(uc, UC_X86_REG_R13, &r13); + uc_reg_read(uc, UC_X86_REG_R14, &r14); + uc_reg_read(uc, UC_X86_REG_R15, &r15); + +#if 0 + printf(">>> RAX = 0x%" PRIx64 "\n", rax); + printf(">>> RBX = 0x%" PRIx64 "\n", rbx); + printf(">>> RCX = 0x%" PRIx64 "\n", rcx); + printf(">>> RDX = 0x%" PRIx64 "\n", rdx); + printf(">>> RSI = 0x%" PRIx64 "\n", rsi); + printf(">>> RDI = 0x%" PRIx64 "\n", rdi); + printf(">>> R8 = 0x%" PRIx64 "\n", r8); + printf(">>> R9 = 0x%" PRIx64 "\n", r9); + printf(">>> R10 = 0x%" PRIx64 "\n", r10); + printf(">>> R11 = 0x%" PRIx64 "\n", r11); + printf(">>> R12 = 0x%" PRIx64 "\n", r12); + printf(">>> R13 = 0x%" PRIx64 "\n", r13); + printf(">>> R14 = 0x%" PRIx64 "\n", r14); + printf(">>> R15 = 0x%" PRIx64 "\n", r15); +#endif + + uc_assert_success(uc_close(uc)); +} + +/******************************************************************************/ + +// callback for SYSCALL instruction (X86). +static void hook_syscall(uc_engine *uc, void *user_data) +{ + uint64_t rax; + + uc_assert_success(uc_reg_read(uc, UC_X86_REG_RAX, &rax)); + assert_int_equal(0x100, rax); + + rax = 0x200; + uc_assert_success(uc_reg_write(uc, UC_X86_REG_RAX, &rax)); +} + +static void test_x86_64_syscall(void **state) +{ + uc_engine *uc; + uc_hook trace1; + uc_err err; + + static const uint64_t address = 0x1000000; + static const uint8_t code[] = { + 0x0F, 0x05, // SYSCALL + }; + + int64_t rax = 0x100; + + // Initialize emulator in X86-64bit mode + err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc); + uc_assert_success(err); + + // map 2MB memory for this emulation + err = uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL); + uc_assert_success(err); + + // write machine code to be emulated to memory + err = uc_mem_write(uc, address, code, sizeof(code)); + uc_assert_success(err); + + // hook interrupts for syscall + err = uc_hook_add(uc, &trace1, UC_HOOK_INSN, hook_syscall, NULL, UC_X86_INS_SYSCALL); + uc_assert_success(err); + + // initialize machine registers + err = uc_reg_write(uc, UC_X86_REG_RAX, &rax); + uc_assert_success(err); + + // emulate machine code in infinite time (last param = 0), or when + // finishing all the code. + err = uc_emu_start(uc, address, address + sizeof(code), 0, 0); + uc_assert_success(err); + + // verify register values + uc_assert_success(uc_reg_read(uc, UC_X86_REG_RAX, &rax)); + assert_int_equal(0x200, rax); + + uc_assert_success(uc_close(uc)); +} + +/******************************************************************************/ + +static void test_x86_16(void **state) +{ + uc_engine *uc; + uc_err err; + uint8_t tmp; + + static const uint64_t address = 0; + static const uint8_t code[] = { + 0x00, 0x00, // add byte ptr [bx + si], al + }; + + int32_t eax = 7; + int32_t ebx = 5; + int32_t esi = 6; + + // Initialize emulator in X86-16bit mode + err = uc_open(UC_ARCH_X86, UC_MODE_16, &uc); + uc_assert_success(err); + + // map 8KB memory for this emulation + err = uc_mem_map(uc, address, 8 * 1024, UC_PROT_ALL); + uc_assert_success(err); + + // write machine code to be emulated to memory + err = uc_mem_write(uc, address, code, sizeof(code)); + uc_assert_success(err); + + // initialize machine registers + uc_assert_success(uc_reg_write(uc, UC_X86_REG_EAX, &eax)); + uc_assert_success(uc_reg_write(uc, UC_X86_REG_EBX, &ebx)); + uc_assert_success(uc_reg_write(uc, UC_X86_REG_ESI, &esi)); + + // emulate machine code in infinite time (last param = 0), or when + // finishing all the code. + err = uc_emu_start(uc, address, address+sizeof(code), 0, 0); + uc_assert_success(err); + + // read from memory + uc_assert_success(uc_mem_read(uc, 11, &tmp, 1)); + assert_int_equal(7, tmp); + + uc_assert_success(uc_close(uc)); +} + +/******************************************************************************/ + +int main(void) { + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_i386), + cmocka_unit_test(test_i386_jump), + cmocka_unit_test(test_i386_inout), + cmocka_unit_test(test_i386_loop), + cmocka_unit_test(test_i386_invalid_mem_read), + cmocka_unit_test(test_i386_invalid_mem_write), + cmocka_unit_test(test_i386_jump_invalid), + + cmocka_unit_test(test_x86_64), + cmocka_unit_test(test_x86_64_syscall), + + cmocka_unit_test(test_x86_16), + + cmocka_unit_test_setup_teardown(test_basic_blocks, setup32, teardown), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/unit/unicorn_test.h b/tests/unit/unicorn_test.h new file mode 100644 index 00000000..9342566d --- /dev/null +++ b/tests/unit/unicorn_test.h @@ -0,0 +1,42 @@ +#ifndef UNICORN_TEST_H +#define UNICORN_TEST_H + +#include +#include +#include +#include +#include + +/** + * Assert that err matches expect + */ +#define uc_assert_err(expect, err) \ +do { \ + uc_err __err = err; \ + if (__err != expect) { \ + fail_msg("%s", uc_strerror(__err)); \ + } \ +} while (0) + +/** + * Assert that err is UC_ERR_OK + */ +#define uc_assert_success(err) uc_assert_err(UC_ERR_OK, err) + +/** + * Assert that err is anything but UC_ERR_OK + * + * Note: Better to use uc_assert_err(, err), + * as this serves to document which errors a function will return + * in various scenarios. + */ +#define uc_assert_fail(err) \ +do { \ + uc_err __err = err; \ + if (__err == UC_ERR_OK) { \ + fail_msg("%s", uc_strerror(__err)); \ + } \ +} while (0) + + +#endif /* UNICORN_TEST_H */ diff --git a/uc.c b/uc.c index 76a3f5f7..d5219542 100644 --- a/uc.c +++ b/uc.c @@ -87,8 +87,8 @@ const char *uc_strerror(uc_err code) return "Write to write-protected memory (UC_ERR_WRITE_PROT)"; case UC_ERR_READ_PROT: return "Read from non-readable memory (UC_ERR_READ_PROT)"; - case UC_ERR_EXEC_PROT: - return "Fetch from non-executable memory (UC_ERR_EXEC_PROT)"; + case UC_ERR_FETCH_PROT: + return "Fetch from non-executable memory (UC_ERR_FETCH_PROT)"; case UC_ERR_ARG: return "Invalid argumet (UC_ERR_ARG)"; @@ -809,6 +809,9 @@ MemoryRegion *memory_mapping(struct uc_struct* uc, uint64_t address) { unsigned int i; + if (uc->mapped_block_count == 0) + return NULL; + // try with the cache index first i = uc->mapped_block_cache_index; @@ -827,7 +830,7 @@ MemoryRegion *memory_mapping(struct uc_struct* uc, uint64_t address) return NULL; } -static uc_err _hook_mem_invalid(struct uc_struct* uc, uc_cb_eventmem_t callback, +static uc_err _hook_mem_invalid(struct uc_struct* uc, int type, uc_cb_eventmem_t callback, void *user_data, uc_hook *evh) { size_t i; @@ -839,7 +842,18 @@ static uc_err _hook_mem_invalid(struct uc_struct* uc, uc_cb_eventmem_t callback, uc->hook_callbacks[i].callback = callback; uc->hook_callbacks[i].user_data = user_data; *evh = i; - uc->hook_mem_idx = i; + if (type & UC_HOOK_MEM_READ_INVALID) + uc->hook_mem_read_idx = i; + if (type & UC_HOOK_MEM_READ_PROT) + uc->hook_mem_read_prot_idx = i; + if (type & UC_HOOK_MEM_WRITE_INVALID) + uc->hook_mem_write_idx = i; + if (type & UC_HOOK_MEM_WRITE_PROT) + uc->hook_mem_write_prot_idx = i; + if (type & UC_HOOK_MEM_FETCH_INVALID) + uc->hook_mem_fetch_idx = i; + if (type & UC_HOOK_MEM_FETCH_PROT) + uc->hook_mem_fetch_prot_idx = i; return UC_ERR_OK; } else return UC_ERR_NOMEM; @@ -917,7 +931,7 @@ static uc_err _hook_insn(struct uc_struct *uc, unsigned int insn_id, void *callb } UNICORN_EXPORT -uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, uc_hook_type type, void *callback, void *user_data, ...) +uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, ...) { va_list valist; int ret = UC_ERR_OK; @@ -926,9 +940,26 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, uc_hook_type type, void *callback va_start(valist, user_data); + if (type & UC_HOOK_MEM_READ_INVALID) + ret = _hook_mem_invalid(uc, UC_HOOK_MEM_READ_INVALID, callback, user_data, hh); + + if (type & UC_HOOK_MEM_WRITE_INVALID) + ret = _hook_mem_invalid(uc, UC_HOOK_MEM_WRITE_INVALID, callback, user_data, hh); + + if (type & UC_HOOK_MEM_FETCH_INVALID) + ret = _hook_mem_invalid(uc, UC_HOOK_MEM_FETCH_INVALID, callback, user_data, hh); + + if (type & UC_HOOK_MEM_READ_PROT) + ret = _hook_mem_invalid(uc, UC_HOOK_MEM_READ_PROT, callback, user_data, hh); + + if (type & UC_HOOK_MEM_WRITE_PROT) + ret = _hook_mem_invalid(uc, UC_HOOK_MEM_WRITE_PROT, callback, user_data, hh); + + if (type & UC_HOOK_MEM_FETCH_PROT) + ret = _hook_mem_invalid(uc, UC_HOOK_MEM_FETCH_PROT, callback, user_data, hh); + switch(type) { default: - ret = UC_ERR_HOOK; break; case UC_HOOK_INTR: ret = _hook_intr(uc, callback, user_data, hh); @@ -947,9 +978,6 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, uc_hook_type type, void *callback end = va_arg(valist, uint64_t); ret = _hook_code(uc, UC_HOOK_BLOCK, begin, end, callback, user_data, hh); break; - case UC_HOOK_MEM_INVALID: - ret = _hook_mem_invalid(uc, callback, user_data, hh); - break; case UC_HOOK_MEM_READ: begin = va_arg(valist, uint64_t); end = va_arg(valist, uint64_t); @@ -960,10 +988,10 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, uc_hook_type type, void *callback end = va_arg(valist, uint64_t); ret = _hook_mem_access(uc, UC_HOOK_MEM_WRITE, begin, end, callback, user_data, hh); break; - case UC_HOOK_MEM_READ_WRITE: + case UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE: begin = va_arg(valist, uint64_t); end = va_arg(valist, uint64_t); - ret = _hook_mem_access(uc, UC_HOOK_MEM_READ_WRITE, begin, end, callback, user_data, hh); + ret = _hook_mem_access(uc, UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, begin, end, callback, user_data, hh); break; }