diff --git a/.gitignore b/.gitignore index b05a5ff6..a1efef9f 100644 --- a/.gitignore +++ b/.gitignore @@ -138,6 +138,7 @@ test_tb_x86 test_multihook test_pc_change mem_fuzz +test_x86_soft_paging memleak_x86 memleak_arm diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..39c18b8e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: c +sudo: false +before_install: + - export LD_LIBRARY_PATH=`pwd`/samples/:$LD_LIBRARY_PATH + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install glib; fi + +script: + - ./make.sh +compiler: + - clang + - gcc +os: + - linux + - osx diff --git a/CREDITS.TXT b/CREDITS.TXT index 049e9b56..e4319580 100644 --- a/CREDITS.TXT +++ b/CREDITS.TXT @@ -55,3 +55,7 @@ Loi Anh Tuan Shaun Wheelhouse: Homebrew package Kamil Rytarowski: Pkgsrc package Zak Escano: MSVC binding +Chris Eagle: Java binding +Ryan Hileman: Go binding +Antonio Parata: .NET binding +Jonathon Reinhart: C unit test diff --git a/README.md b/README.md index 942e1b8e..f57b1df6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ Unicorn Engine ============== +[![Build Status](https://travis-ci.org/unicorn-engine/unicorn.svg?branch=master)](https://travis-ci.org/unicorn-engine/unicorn) +[![Build status](https://ci.appveyor.com/api/projects/status/kojr7bald748ba2x/branch/master?svg=true)](https://ci.appveyor.com/project/aquynh/unicorn/branch/master) + Unicorn is a lightweight, multi-platform, multi-architecture CPU emulator framework based on [QEMU](http://qemu.org). diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..cbc1c215 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,16 @@ +version: 1.0-{build} + +platform: + - x64 + +environment: + global: + MSYS2_ARCH: x86_64 + matrix: + - HOST_ARCH_ARG: --host=x86_64-w64-mingw32 + ADD_PATH: /mingw64/bin + - HOST_ARCH_ARG: --host=i686-w64-mingw32 + ADD_PATH: /mingw32/bin + +build_script: + - make.sh cross-win64 diff --git a/bindings/dotnet/UnicornManaged/Const/Common.fs b/bindings/dotnet/UnicornManaged/Const/Common.fs index 6f106a3e..ef9004e6 100644 --- a/bindings/dotnet/UnicornManaged/Const/Common.fs +++ b/bindings/dotnet/UnicornManaged/Const/Common.fs @@ -6,9 +6,9 @@ open System [] module Common = + let UC_API_MAJOR = 1 - let UC_API_MAJOR = 0 - let UC_API_MINOR = 9 + let UC_API_MINOR = 0 let UC_SECOND_SCALE = 1000000 let UC_MILISECOND_SCALE = 1000 let UC_ARCH_ARM = 1 @@ -93,6 +93,7 @@ module Common = let UC_HOOK_MEM_INVALID = 1008 let UC_HOOK_MEM_VALID = 7168 let UC_QUERY_MODE = 1 + let UC_QUERY_PAGE_SIZE = 2 let UC_PROT_NONE = 0 let UC_PROT_READ = 1 diff --git a/bindings/go/unicorn/hook.c b/bindings/go/unicorn/hook.c index 4ed41b51..a2b7dc93 100644 --- a/bindings/go/unicorn/hook.c +++ b/bindings/go/unicorn/hook.c @@ -1,12 +1,16 @@ #include #include "_cgo_export.h" -uc_err uc_hook_add_i1(uc_engine *handle, uc_hook *h2, uc_hook_type type, void *callback, uintptr_t user, int arg1) { - return uc_hook_add(handle, h2, type, callback, (void *)user, arg1); +uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, + void *user_data, uint64_t begin, uint64_t end, ...); + + +uc_err uc_hook_add_wrap(uc_engine *handle, uc_hook *h2, uc_hook_type type, void *callback, uintptr_t user, uint64_t begin, uint64_t end) { + return uc_hook_add(handle, h2, type, callback, (void *)user, begin, end); } -uc_err uc_hook_add_u2(uc_engine *handle, uc_hook *h2, uc_hook_type type, void *callback, uintptr_t user, uint64_t arg1, uint64_t arg2) { - return uc_hook_add(handle, h2, type, callback, (void *)user, arg1, arg2); +uc_err uc_hook_add_insn(uc_engine *handle, uc_hook *h2, uc_hook_type type, void *callback, uintptr_t user, uint64_t begin, uint64_t end, int insn) { + return uc_hook_add(handle, h2, type, callback, (void *)user, begin, end, insn); } void hookCode_cgo(uc_engine *handle, uint64_t addr, uint32_t size, uintptr_t user) { diff --git a/bindings/go/unicorn/hook.go b/bindings/go/unicorn/hook.go index d5dfc874..0a1a7d6f 100644 --- a/bindings/go/unicorn/hook.go +++ b/bindings/go/unicorn/hook.go @@ -63,23 +63,21 @@ func hookX86Syscall(handle unsafe.Pointer, user unsafe.Pointer) { hook.Callback.(func(Unicorn))(hook.Uc) } -func (u *uc) HookAdd(htype int, cb interface{}, extra ...uint64) (Hook, error) { +func (u *uc) HookAdd(htype int, cb interface{}, begin, end uint64, extra ...int) (Hook, error) { var callback unsafe.Pointer - var iarg1 C.int - var uarg1, uarg2 C.uint64_t - rangeMode := false + var insn C.int + var insnMode bool switch htype { case HOOK_BLOCK, HOOK_CODE: - rangeMode = true callback = C.hookCode_cgo case HOOK_MEM_READ, HOOK_MEM_WRITE, HOOK_MEM_READ | HOOK_MEM_WRITE: - rangeMode = true callback = C.hookMemAccess_cgo case HOOK_INTR: callback = C.hookInterrupt_cgo case HOOK_INSN: - iarg1 = C.int(extra[0]) - switch iarg1 { + insn = C.int(extra[0]) + insnMode = true + switch insn { case X86_INS_IN: callback = C.hookX86In_cgo case X86_INS_OUT: @@ -93,7 +91,6 @@ func (u *uc) HookAdd(htype int, cb interface{}, extra ...uint64) (Hook, error) { // special case for mask if htype&(HOOK_MEM_READ_UNMAPPED|HOOK_MEM_WRITE_UNMAPPED|HOOK_MEM_FETCH_UNMAPPED| 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.") @@ -102,16 +99,10 @@ func (u *uc) HookAdd(htype int, cb interface{}, extra ...uint64) (Hook, error) { var h2 C.uc_hook data := &HookData{u, cb} uptr := uintptr(unsafe.Pointer(data)) - if rangeMode { - if len(extra) == 2 { - uarg1 = C.uint64_t(extra[0]) - uarg2 = C.uint64_t(extra[1]) - } else { - uarg1, uarg2 = 1, 0 - } - C.uc_hook_add_u2(u.handle, &h2, C.uc_hook_type(htype), callback, C.uintptr_t(uptr), uarg1, uarg2) + if insnMode { + C.uc_hook_add_insn(u.handle, &h2, C.uc_hook_type(htype), callback, C.uintptr_t(uptr), C.uint64_t(begin), C.uint64_t(end), insn) } else { - C.uc_hook_add_i1(u.handle, &h2, C.uc_hook_type(htype), callback, C.uintptr_t(uptr), iarg1) + C.uc_hook_add_wrap(u.handle, &h2, C.uc_hook_type(htype), callback, C.uintptr_t(uptr), C.uint64_t(begin), C.uint64_t(end)) } hookDataMap[uptr] = data hookToUintptr[Hook(h2)] = uptr diff --git a/bindings/go/unicorn/hook.h b/bindings/go/unicorn/hook.h index 6c209516..35813a0a 100644 --- a/bindings/go/unicorn/hook.h +++ b/bindings/go/unicorn/hook.h @@ -1,5 +1,5 @@ -uc_err uc_hook_add_i1(uc_engine *handle, uc_hook *h2, uc_hook_type type, void *callback, uintptr_t user, int arg1); -uc_err uc_hook_add_u2(uc_engine *handle, uc_hook *h2, uc_hook_type type, void *callback, uintptr_t user, uint64_t arg1, uint64_t arg2); +uc_err uc_hook_add_wrap(uc_engine *handle, uc_hook *h2, uc_hook_type type, void *callback, uintptr_t user, uint64_t begin, uint64_t end); +uc_err uc_hook_add_insn(uc_engine *handle, uc_hook *h2, uc_hook_type type, void *callback, uintptr_t user, uint64_t begin, uint64_t end, int insn); void hookCode_cgo(uc_engine *handle, uint64_t addr, uint32_t size, uintptr_t user); bool hookMemInvalid_cgo(uc_engine *handle, uc_mem_type type, uint64_t addr, int size, int64_t value, uintptr_t user); void hookMemAccess_cgo(uc_engine *handle, uc_mem_type type, uint64_t addr, int size, int64_t value, uintptr_t user); diff --git a/bindings/go/unicorn/unicorn.go b/bindings/go/unicorn/unicorn.go index 4f6f1f99..2b0e456c 100644 --- a/bindings/go/unicorn/unicorn.go +++ b/bindings/go/unicorn/unicorn.go @@ -25,22 +25,31 @@ func errReturn(err C.uc_err) error { return nil } +type MemRegion struct { + Begin, End uint64 + Prot int +} + type Unicorn interface { MemMap(addr, size uint64) error MemMapProt(addr, size uint64, prot int) error MemMapPtr(addr, size uint64, prot int, ptr unsafe.Pointer) error MemProtect(addr, size uint64, prot int) error MemUnmap(addr, size uint64) error + MemRegions() ([]*MemRegion, error) MemRead(addr, size uint64) ([]byte, error) MemReadInto(dst []byte, addr uint64) error MemWrite(addr uint64, data []byte) error RegRead(reg int) (uint64, error) RegWrite(reg int, value uint64) error + RegReadMmr(reg int) (*X86Mmr, error) + RegWriteMmr(reg int, value *X86Mmr) error Start(begin, until uint64) error StartWithOptions(begin, until uint64, options *UcOptions) error Stop() error - HookAdd(htype int, cb interface{}, extra ...uint64) (Hook, error) + HookAdd(htype int, cb interface{}, begin, end uint64, extra ...int) (Hook, error) HookDel(hook Hook) error + Query(queryType int) (uint64, error) Close() error } @@ -103,6 +112,25 @@ func (u *uc) RegRead(reg int) (uint64, error) { return uint64(val), errReturn(ucerr) } +func (u *uc) MemRegions() ([]*MemRegion, error) { + var regions *C.struct_uc_mem_region + var count C.uint32_t + ucerr := C.uc_mem_regions(u.handle, ®ions, &count) + if ucerr != C.UC_ERR_OK { + return nil, errReturn(ucerr) + } + ret := make([]*MemRegion, count) + tmp := (*[1 << 30]C.struct_uc_mem_region)(unsafe.Pointer(regions))[:count] + for i, v := range tmp { + ret[i] = &MemRegion{ + Begin: uint64(v.begin), + End: uint64(v.end), + Prot: int(v.perms), + } + } + return ret, nil +} + func (u *uc) MemWrite(addr uint64, data []byte) error { if len(data) == 0 { return nil @@ -141,3 +169,9 @@ func (u *uc) MemProtect(addr, size uint64, prot int) error { func (u *uc) MemUnmap(addr, size uint64) error { return errReturn(C.uc_mem_unmap(u.handle, C.uint64_t(addr), C.size_t(size))) } + +func (u *uc) Query(queryType int) (uint64, error) { + var ret C.size_t + ucerr := C.uc_query(u.handle, C.uc_query_type(queryType), &ret) + return uint64(ret), errReturn(ucerr) +} diff --git a/bindings/go/unicorn/unicorn_const.go b/bindings/go/unicorn/unicorn_const.go index 8d634be9..01b62fca 100644 --- a/bindings/go/unicorn/unicorn_const.go +++ b/bindings/go/unicorn/unicorn_const.go @@ -1,9 +1,9 @@ package unicorn // For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [unicorn_const.go] const ( + API_MAJOR = 1 - API_MAJOR = 0 - API_MINOR = 9 + API_MINOR = 0 SECOND_SCALE = 1000000 MILISECOND_SCALE = 1000 ARCH_ARM = 1 @@ -88,6 +88,7 @@ const ( HOOK_MEM_INVALID = 1008 HOOK_MEM_VALID = 7168 QUERY_MODE = 1 + QUERY_PAGE_SIZE = 2 PROT_NONE = 0 PROT_READ = 1 diff --git a/bindings/go/unicorn/unicorn_test.go b/bindings/go/unicorn/unicorn_test.go index caf13126..6627528f 100644 --- a/bindings/go/unicorn/unicorn_test.go +++ b/bindings/go/unicorn/unicorn_test.go @@ -37,3 +37,39 @@ func TestDoubleClose(t *testing.T) { t.Fatal(err) } } + +func TestMemRegions(t *testing.T) { + mu, err := NewUnicorn(ARCH_X86, MODE_32) + if err != nil { + t.Fatal(err) + } + err = mu.MemMap(0x1000, 0x1000) + if err != nil { + t.Fatal(err) + } + regions, err := mu.MemRegions() + if err != nil { + t.Fatal(err) + } + if len(regions) != 1 { + t.Fatalf("returned wrong number of regions: %d != 1", len(regions)) + } + r := regions[0] + if r.Begin != 0x1000 || r.End != 0x1fff || r.Prot != 7 { + t.Fatalf("incorrect region: %#v", r) + } +} + +func TestQuery(t *testing.T) { + mu, err := NewUnicorn(ARCH_ARM, MODE_THUMB) + if err != nil { + t.Fatal(err) + } + mode, err := mu.Query(QUERY_MODE) + if err != nil { + t.Fatal(err) + } + if mode != MODE_THUMB { + t.Fatal("query returned invalid mode: %d != %d", mode, MODE_THUMB) + } +} diff --git a/bindings/go/unicorn/x86.go b/bindings/go/unicorn/x86.go new file mode 100644 index 00000000..9097de2a --- /dev/null +++ b/bindings/go/unicorn/x86.go @@ -0,0 +1,38 @@ +package unicorn + +import ( + "unsafe" +) + +// #include +// #include +import "C" + +type X86Mmr struct { + Selector uint16 + Base uint64 + Limit uint32 + Flags uint32 +} + +func (u *uc) RegWriteMmr(reg int, value *X86Mmr) error { + var val C.uc_x86_mmr + val.selector = C.uint16_t(value.Selector) + val.base = C.uint64_t(value.Base) + val.limit = C.uint32_t(value.Limit) + val.flags = C.uint32_t(value.Flags) + ucerr := C.uc_reg_write(u.handle, C.int(reg), unsafe.Pointer(&val)) + return errReturn(ucerr) +} + +func (u *uc) RegReadMmr(reg int) (*X86Mmr, error) { + var val C.uc_x86_mmr + ucerr := C.uc_reg_read(u.handle, C.int(reg), unsafe.Pointer(&val)) + ret := &X86Mmr{ + Selector: uint16(val.selector), + Base: uint64(val.base), + Limit: uint32(val.limit), + Flags: uint32(val.flags), + } + return ret, errReturn(ucerr) +} diff --git a/bindings/go/unicorn/x86_test.go b/bindings/go/unicorn/x86_test.go index 67400552..c74a0611 100644 --- a/bindings/go/unicorn/x86_test.go +++ b/bindings/go/unicorn/x86_test.go @@ -96,7 +96,7 @@ func TestX86InOut(t *testing.T) { default: return 0 } - }, X86_INS_IN) + }, 1, 0, X86_INS_IN) mu.HookAdd(HOOK_INSN, func(_ Unicorn, port, size, value uint32) { outCalled = true var err error @@ -111,7 +111,7 @@ func TestX86InOut(t *testing.T) { if err != nil { t.Fatal(err) } - }, X86_INS_OUT) + }, 1, 0, X86_INS_OUT) if err := mu.Start(ADDRESS, ADDRESS+uint64(len(code))); err != nil { t.Fatal(err) } @@ -132,7 +132,7 @@ func TestX86Syscall(t *testing.T) { mu.HookAdd(HOOK_INSN, func(_ Unicorn) { rax, _ := mu.RegRead(X86_REG_RAX) mu.RegWrite(X86_REG_RAX, rax+1) - }, X86_INS_SYSCALL) + }, 1, 0, X86_INS_SYSCALL) mu.RegWrite(X86_REG_RAX, 0x100) err = mu.Start(ADDRESS, ADDRESS+uint64(len(code))) if err != nil { @@ -143,3 +143,18 @@ func TestX86Syscall(t *testing.T) { t.Fatal("Incorrect syscall return value.") } } + +func TestX86Mmr(t *testing.T) { + mu, err := MakeUc(MODE_64, "") + if err != nil { + t.Fatal(err) + } + err = mu.RegWriteMmr(X86_REG_GDTR, &X86Mmr{Selector: 0, Base: 0x1000, Limit: 0x1fff, Flags: 0}) + if err != nil { + t.Fatal(err) + } + mmr, err := mu.RegReadMmr(X86_REG_GDTR) + if mmr.Selector != 0 || mmr.Base != 0x1000 || mmr.Limit != 0x1fff || mmr.Flags != 0 { + t.Fatalf("mmr read failed: %#v", mmr) + } +} diff --git a/bindings/java/unicorn/UnicornConst.java b/bindings/java/unicorn/UnicornConst.java index 0fe4f193..033267ae 100644 --- a/bindings/java/unicorn/UnicornConst.java +++ b/bindings/java/unicorn/UnicornConst.java @@ -3,9 +3,9 @@ package unicorn; public interface UnicornConst { + public static final int UC_API_MAJOR = 1; - public static final int UC_API_MAJOR = 0; - public static final int UC_API_MINOR = 9; + public static final int UC_API_MINOR = 0; public static final int UC_SECOND_SCALE = 1000000; public static final int UC_MILISECOND_SCALE = 1000; public static final int UC_ARCH_ARM = 1; @@ -90,6 +90,7 @@ public interface UnicornConst { public static final int UC_HOOK_MEM_INVALID = 1008; public static final int UC_HOOK_MEM_VALID = 7168; public static final int UC_QUERY_MODE = 1; + public static final int UC_QUERY_PAGE_SIZE = 2; public static final int UC_PROT_NONE = 0; public static final int UC_PROT_READ = 1; diff --git a/bindings/python/sample_x86.py b/bindings/python/sample_x86.py index b470f4ce..32947a3c 100755 --- a/bindings/python/sample_x86.py +++ b/bindings/python/sample_x86.py @@ -291,8 +291,8 @@ def test_i386_inout(): mu.hook_add(UC_HOOK_CODE, hook_code) # handle IN & OUT instruction - mu.hook_add(UC_HOOK_INSN, hook_in, None, UC_X86_INS_IN) - mu.hook_add(UC_HOOK_INSN, hook_out, None, UC_X86_INS_OUT) + mu.hook_add(UC_HOOK_INSN, hook_in, None, 1, 0, UC_X86_INS_IN) + mu.hook_add(UC_HOOK_INSN, hook_out, None, 1, 0, UC_X86_INS_OUT) # emulate machine code in infinite time mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_INOUT)) @@ -417,7 +417,7 @@ def test_x86_64_syscall(): print('ERROR: was not expecting rax=%d in syscall' % rax) # hook interrupts for syscall - mu.hook_add(UC_HOOK_INSN, hook_syscall, None, UC_X86_INS_SYSCALL) + mu.hook_add(UC_HOOK_INSN, hook_syscall, None, 1, 0, UC_X86_INS_SYSCALL) # syscall handler is expecting rax=0x100 mu.reg_write(UC_X86_REG_RAX, 0x100) diff --git a/bindings/python/setup.py b/bindings/python/setup.py index 66ea8ab0..6b98bae5 100755 --- a/bindings/python/setup.py +++ b/bindings/python/setup.py @@ -24,7 +24,7 @@ PKG_NAME = 'unicorn' if os.path.exists(PATH_LIB64) and os.path.exists(PATH_LIB32): PKG_NAME = 'unicorn-windows' -VERSION = '0.9' +VERSION = '1.0' SYSTEM = sys.platform # virtualenv breaks import, but get_python_lib() will work. @@ -63,7 +63,7 @@ def copy_sources(): src.extend(glob.glob("../../Makefile")) src.extend(glob.glob("../../LICENSE*")) - src.extend(glob.glob("../../README")) + src.extend(glob.glob("../../README.md")) src.extend(glob.glob("../../*.TXT")) src.extend(glob.glob("../../RELEASE_NOTES")) src.extend(glob.glob("../../make.sh")) diff --git a/bindings/python/shellcode.py b/bindings/python/shellcode.py index a4a473b0..898ada7b 100755 --- a/bindings/python/shellcode.py +++ b/bindings/python/shellcode.py @@ -97,7 +97,7 @@ def test_i386(mode, code): mu.hook_add(UC_HOOK_INTR, hook_intr) # handle SYSCALL - mu.hook_add(UC_HOOK_INSN, hook_syscall, None, UC_X86_INS_SYSCALL) + mu.hook_add(UC_HOOK_INSN, hook_syscall, None, 1, 0, UC_X86_INS_SYSCALL) # emulate machine code in infinite time mu.emu_start(ADDRESS, ADDRESS + len(code)) diff --git a/bindings/python/unicorn/__init__.py b/bindings/python/unicorn/__init__.py index 3f63904c..9d2b717c 100644 --- a/bindings/python/unicorn/__init__.py +++ b/bindings/python/unicorn/__init__.py @@ -1,4 +1,4 @@ # Unicorn Python bindings, by Nguyen Anh Quynnh from . import arm_const, arm64_const, mips_const, sparc_const, m68k_const, x86_const from .unicorn_const import * -from .unicorn import Uc, uc_version, uc_arch_supported, version_bind, debug, UcError +from .unicorn import Uc, uc_version, uc_arch_supported, version_bind, debug, UcError, __version__ diff --git a/bindings/python/unicorn/unicorn.py b/bindings/python/unicorn/unicorn.py index 476ac552..989d985f 100644 --- a/bindings/python/unicorn/unicorn.py +++ b/bindings/python/unicorn/unicorn.py @@ -81,6 +81,8 @@ if _found == False: raise ImportError("ERROR: fail to load the dynamic library.") +__version__ = "%s.%s" %(UC_API_MAJOR, UC_API_MINOR) + # setup all the function prototype def _setup_prototype(lib, fname, restype, *argtypes): getattr(lib, fname).restype = restype @@ -315,7 +317,7 @@ class Uc(object): # add a hook - def hook_add(self, htype, callback, user_data=None, arg1=1, arg2=0): + def hook_add(self, htype, callback, user_data=None, begin=1, end=0, arg1=0): _h2 = uc_hook_h() # save callback & user_data @@ -332,30 +334,28 @@ class Uc(object): if arg1 in (x86_const.UC_X86_INS_SYSCALL, x86_const.UC_X86_INS_SYSENTER): # SYSCALL/SYSENTER instruction cb = ctypes.cast(UC_HOOK_INSN_SYSCALL_CB(self._hook_insn_syscall_cb), UC_HOOK_INSN_SYSCALL_CB) status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \ - cb, ctypes.cast(self._callback_count, ctypes.c_void_p), insn) + cb, ctypes.cast(self._callback_count, ctypes.c_void_p), ctypes.c_uint64(begin), ctypes.c_uint64(end), insn) elif htype == UC_HOOK_INTR: cb = ctypes.cast(UC_HOOK_INTR_CB(self._hook_intr_cb), UC_HOOK_INTR_CB) status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \ - cb, ctypes.cast(self._callback_count, ctypes.c_void_p)) + cb, ctypes.cast(self._callback_count, ctypes.c_void_p), ctypes.c_uint64(begin), ctypes.c_uint64(end)) else: - begin = ctypes.c_uint64(arg1) - end = ctypes.c_uint64(arg2) if htype in (UC_HOOK_BLOCK, UC_HOOK_CODE): # set callback with wrapper, so it can be called # with this object as param 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) + ctypes.cast(self._callback_count, ctypes.c_void_p), ctypes.c_uint64(begin), ctypes.c_uint64(end)) elif htype & UC_HOOK_MEM_READ_UNMAPPED or htype & UC_HOOK_MEM_WRITE_UNMAPPED or \ htype & UC_HOOK_MEM_FETCH_UNMAPPED 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)) + cb, ctypes.cast(self._callback_count, ctypes.c_void_p), ctypes.c_uint64(begin), ctypes.c_uint64(end)) else: 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)) + cb, ctypes.cast(self._callback_count, ctypes.c_void_p), ctypes.c_uint64(begin), ctypes.c_uint64(end)) # save the ctype function so gc will leave it alone. self._ctype_cbs[self._callback_count] = cb diff --git a/bindings/python/unicorn/unicorn_const.py b/bindings/python/unicorn/unicorn_const.py index 4e942ef5..89ccd919 100644 --- a/bindings/python/unicorn/unicorn_const.py +++ b/bindings/python/unicorn/unicorn_const.py @@ -1,7 +1,7 @@ # For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [unicorn_const.py] +UC_API_MAJOR = 1 -UC_API_MAJOR = 0 -UC_API_MINOR = 9 +UC_API_MINOR = 0 UC_SECOND_SCALE = 1000000 UC_MILISECOND_SCALE = 1000 UC_ARCH_ARM = 1 @@ -86,6 +86,7 @@ UC_HOOK_MEM_FETCH_INVALID = 576 UC_HOOK_MEM_INVALID = 1008 UC_HOOK_MEM_VALID = 7168 UC_QUERY_MODE = 1 +UC_QUERY_PAGE_SIZE = 2 UC_PROT_NONE = 0 UC_PROT_READ = 1 diff --git a/include/uc_priv.h b/include/uc_priv.h index d9fe1a2d..75b45a30 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -22,6 +22,17 @@ #define ARR_SIZE(a) (sizeof(a)/sizeof(a[0])) +#define READ_QWORD(x) ((uint64)x) +#define READ_DWORD(x) (x & 0xffffffff) +#define READ_WORD(x) (x & 0xffff) +#define READ_BYTE_H(x) ((x & 0xffff) >> 8) +#define READ_BYTE_L(x) (x & 0xff) +#define WRITE_DWORD(x, w) (x = (x & ~0xffffffff) | (w & 0xffffffff)) +#define WRITE_WORD(x, w) (x = (x & ~0xffff) | (w & 0xffff)) +#define WRITE_BYTE_H(x, b) (x = (x & ~0xff00) | ((b & 0xff) << 8)) +#define WRITE_BYTE_L(x, b) (x = (x & ~0xff) | (b & 0xff)) + + QTAILQ_HEAD(CPUTailQ, CPUState); typedef struct ModuleEntry { diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index ae8cd736..65d6aa53 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -57,8 +57,8 @@ typedef size_t uc_hook; #endif // Unicorn API version -#define UC_API_MAJOR 0 -#define UC_API_MINOR 9 +#define UC_API_MAJOR 1 +#define UC_API_MINOR 0 /* Macro to create combined version which can be compared to @@ -262,6 +262,7 @@ typedef struct uc_mem_region { typedef enum uc_query_type { // Dynamically query current hardware mode. UC_QUERY_MODE = 1, + UC_QUERY_PAGE_SIZE, } uc_query_type; /* @@ -457,13 +458,19 @@ uc_err uc_emu_stop(uc_engine *uc); @callback: callback to be run when instruction is hit @user_data: user-defined data. This will be passed to callback function in its last argument @user_data + @begin: start address of the area where the callback is effect (inclusive) + @end: end address of the area where the callback is effect (inclusive) + NOTE 1: the callback is called only if related address is in range [@begin, @end] + NOTE 2: if @begin > @end, callback is called whenever this hook type is triggered @...: variable arguments (depending on @type) + NOTE: if @type = UC_HOOK_INSN, this is the instruction ID (ex: UC_X86_INS_OUT) @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum for detailed error). */ UNICORN_EXPORT -uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, ...); +uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, + void *user_data, uint64_t begin, uint64_t end, ...); /* Unregister (remove) a hook callback. diff --git a/pkgconfig.mk b/pkgconfig.mk index 125322eb..e4374b15 100644 --- a/pkgconfig.mk +++ b/pkgconfig.mk @@ -2,8 +2,8 @@ # To be used to generate unicorn.pc for pkg-config # version major & minor -PKG_MAJOR = 0 -PKG_MINOR = 9 +PKG_MAJOR = 1 +PKG_MINOR = 0 # version bugfix level. Example: PKG_EXTRA = 1 PKG_EXTRA = diff --git a/qemu/target-arm/unicorn_aarch64.c b/qemu/target-arm/unicorn_aarch64.c index 2f43d5ea..8e7f610c 100644 --- a/qemu/target-arm/unicorn_aarch64.c +++ b/qemu/target-arm/unicorn_aarch64.c @@ -3,21 +3,11 @@ #include "hw/boards.h" #include "hw/arm/arm.h" - #include "sysemu/cpus.h" - #include "unicorn.h" - #include "cpu.h" - #include "unicorn_common.h" - - -#define READ_QWORD(x) ((uint64)x) -#define READ_DWORD(x) (x & 0xffffffff) -#define READ_WORD(x) (x & 0xffff) -#define READ_BYTE_H(x) ((x & 0xffff) >> 8) -#define READ_BYTE_L(x) (x & 0xff) +#include "uc_priv.h" static void arm64_set_pc(struct uc_struct *uc, uint64_t address) @@ -77,11 +67,6 @@ int arm64_reg_read(struct uc_struct *uc, unsigned int regid, void *value) 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 arm64_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) { CPUState *mycpu = first_cpu; diff --git a/qemu/target-arm/unicorn_arm.c b/qemu/target-arm/unicorn_arm.c index 0254dae4..ac71d805 100644 --- a/qemu/target-arm/unicorn_arm.c +++ b/qemu/target-arm/unicorn_arm.c @@ -3,20 +3,11 @@ #include "hw/boards.h" #include "hw/arm/arm.h" - #include "sysemu/cpus.h" - #include "unicorn.h" - #include "cpu.h" #include "unicorn_common.h" - - -#define READ_QWORD(x) ((uint64)x) -#define READ_DWORD(x) (x & 0xffffffff) -#define READ_WORD(x) (x & 0xffff) -#define READ_BYTE_H(x) ((x & 0xffff) >> 8) -#define READ_BYTE_L(x) (x & 0xff) +#include "uc_priv.h" static void arm_set_pc(struct uc_struct *uc, uint64_t address) @@ -84,11 +75,6 @@ int arm_reg_read(struct uc_struct *uc, unsigned int regid, void *value) 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 arm_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) { CPUState *mycpu = first_cpu; diff --git a/qemu/target-i386/translate.c b/qemu/target-i386/translate.c index d68cb516..76bab2d6 100644 --- a/qemu/target-i386/translate.c +++ b/qemu/target-i386/translate.c @@ -4721,6 +4721,17 @@ static void sync_eflags(DisasContext *s, TCGContext *tcg_ctx) tcg_gen_st_tl(tcg_ctx, *cpu_T[0], cpu_env, offsetof(CPUX86State, eflags)); } +static void restore_eflags(DisasContext *s, TCGContext *tcg_ctx) +{ + TCGv **cpu_T = (TCGv **)tcg_ctx->cpu_T; + TCGv_ptr cpu_env = tcg_ctx->cpu_env; + + tcg_gen_ld_tl(tcg_ctx, *cpu_T[0], cpu_env, offsetof(CPUX86State, eflags)); + gen_helper_write_eflags(tcg_ctx, cpu_env, *cpu_T[0], + tcg_const_i32(tcg_ctx, (TF_MASK | AC_MASK | ID_MASK | NT_MASK) & 0xffff)); + set_cc_op(s, CC_OP_EFLAGS); +} + /* convert one instruction. s->is_jmp is set if the translation must be stopped. Return the next pc value */ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, @@ -4773,6 +4784,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, changed_cc_op = true; } gen_uc_tracecode(tcg_ctx, 0xf1f1f1f1, UC_HOOK_CODE_IDX, env->uc, pc_start); + restore_eflags(s, tcg_ctx); // the callback might want to stop emulation immediately check_exit_request(tcg_ctx); } diff --git a/qemu/target-i386/unicorn.c b/qemu/target-i386/unicorn.c index 93866a87..d2f5ecf6 100644 --- a/qemu/target-i386/unicorn.c +++ b/qemu/target-i386/unicorn.c @@ -2,20 +2,14 @@ /* By Nguyen Anh Quynh , 2015 */ #include "hw/boards.h" -#include "sysemu/cpus.h" #include "hw/i386/pc.h" +#include "sysemu/cpus.h" #include "unicorn.h" #include "cpu.h" #include "tcg.h" - #include "unicorn_common.h" #include /* needed for uc_x86_mmr */ - -#define READ_QWORD(x) ((uint64)x) -#define READ_DWORD(x) (x & 0xffffffff) -#define READ_WORD(x) (x & 0xffff) -#define READ_BYTE_H(x) ((x & 0xffff) >> 8) -#define READ_BYTE_L(x) (x & 0xff) +#include "uc_priv.h" static void x86_set_pc(struct uc_struct *uc, uint64_t address) @@ -576,12 +570,6 @@ int x86_reg_read(struct uc_struct *uc, unsigned int regid, void *value) 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 x86_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) { CPUState *mycpu = first_cpu; diff --git a/qemu/target-m68k/unicorn.c b/qemu/target-m68k/unicorn.c index 0055d4a1..0edf04c2 100644 --- a/qemu/target-m68k/unicorn.c +++ b/qemu/target-m68k/unicorn.c @@ -6,14 +6,8 @@ #include "sysemu/cpus.h" #include "unicorn.h" #include "cpu.h" - #include "unicorn_common.h" - -#define READ_QWORD(x) ((uint64)x) -#define READ_DWORD(x) (x & 0xffffffff) -#define READ_WORD(x) (x & 0xffff) -#define READ_BYTE_H(x) ((x & 0xffff) >> 8) -#define READ_BYTE_L(x) (x & 0xff) +#include "uc_priv.h" static void m68k_set_pc(struct uc_struct *uc, uint64_t address) @@ -51,12 +45,6 @@ int m68k_reg_read(struct uc_struct *uc, unsigned int regid, void *value) 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 m68k_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) { CPUState *mycpu = first_cpu; diff --git a/qemu/target-mips/unicorn.c b/qemu/target-mips/unicorn.c index 6af98dce..7740d0d9 100644 --- a/qemu/target-mips/unicorn.c +++ b/qemu/target-mips/unicorn.c @@ -6,15 +6,8 @@ #include "sysemu/cpus.h" #include "unicorn.h" #include "cpu.h" - #include "unicorn_common.h" - - -#define READ_QWORD(x) ((uint64)x) -#define READ_DWORD(x) (x & 0xffffffff) -#define READ_WORD(x) (x & 0xffff) -#define READ_BYTE_H(x) ((x & 0xffff) >> 8) -#define READ_BYTE_L(x) (x & 0xff) +#include "uc_priv.h" static uint64_t mips_mem_redirect(uint64_t address) @@ -64,12 +57,6 @@ int mips_reg_read(struct uc_struct *uc, unsigned int regid, void *value) 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 mips_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) { CPUState *mycpu = first_cpu; diff --git a/qemu/target-sparc/unicorn.c b/qemu/target-sparc/unicorn.c index e612457b..3775776a 100644 --- a/qemu/target-sparc/unicorn.c +++ b/qemu/target-sparc/unicorn.c @@ -7,13 +7,7 @@ #include "unicorn.h" #include "cpu.h" #include "unicorn_common.h" - - -#define READ_QWORD(x) ((uint64)x) -#define READ_DWORD(x) (x & 0xffffffff) -#define READ_WORD(x) (x & 0xffff) -#define READ_BYTE_H(x) ((x & 0xffff) >> 8) -#define READ_BYTE_L(x) (x & 0xff) +#include "uc_priv.h" static bool sparc_stop_interrupt(int intno) diff --git a/qemu/target-sparc/unicorn64.c b/qemu/target-sparc/unicorn64.c index e9257b75..ef99b5ab 100644 --- a/qemu/target-sparc/unicorn64.c +++ b/qemu/target-sparc/unicorn64.c @@ -7,13 +7,7 @@ #include "unicorn.h" #include "cpu.h" #include "unicorn_common.h" - - -#define READ_QWORD(x) ((uint64)x) -#define READ_DWORD(x) (x & 0xffffffff) -#define READ_WORD(x) (x & 0xffff) -#define READ_BYTE_H(x) ((x & 0xffff) >> 8) -#define READ_BYTE_L(x) (x & 0xff) +#include "uc_priv.h" static bool sparc_stop_interrupt(int intno) diff --git a/samples/mem_apis.c b/samples/mem_apis.c index 008f077d..dc1c2481 100644 --- a/samples/mem_apis.c +++ b/samples/mem_apis.c @@ -168,9 +168,9 @@ 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 || + if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK || uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, - hook_mem_invalid, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK) { + hook_mem_invalid, NULL, 1, 0) != UC_ERR_OK) { printf("not ok - Failed to install hooks\n"); return; } @@ -248,10 +248,10 @@ 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 || + if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK || uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, - hook_mem_invalid, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK) { + hook_mem_invalid, NULL, 1, 0) != UC_ERR_OK) { printf("not ok - Failed to install hooks\n"); return; } @@ -326,10 +326,10 @@ 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 || + if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK || uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, - hook_mem_invalid, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK) { + hook_mem_invalid, NULL, 1, 0) != UC_ERR_OK) { printf("not ok - Failed to install hooks\n"); return; } diff --git a/samples/sample_arm.c b/samples/sample_arm.c index 8a5067db..2c69ce6a 100644 --- a/samples/sample_arm.c +++ b/samples/sample_arm.c @@ -77,10 +77,10 @@ static void test_arm(void) uc_reg_write(uc, UC_ARM_REG_R3, &r3); // tracing all basic blocks with customized callback - uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0); // tracing one instruction at ADDRESS with customized callback - uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS); + uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, ADDRESS, ADDRESS); // emulate machine code in infinite time (last param = 0), or when // finishing all the code. @@ -128,10 +128,10 @@ static void test_thumb(void) uc_reg_write(uc, UC_ARM_REG_SP, &sp); // tracing all basic blocks with customized callback - uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0); // tracing one instruction at ADDRESS with customized callback - uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS); + uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, ADDRESS, ADDRESS); // emulate machine code in infinite time (last param = 0), or when // finishing all the code. diff --git a/samples/sample_arm64.c b/samples/sample_arm64.c index 39d500aa..785708d0 100644 --- a/samples/sample_arm64.c +++ b/samples/sample_arm64.c @@ -75,10 +75,10 @@ static void test_arm64(void) uc_reg_write(uc, UC_ARM64_REG_X15, &x15); // tracing all basic blocks with customized callback - uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0); // tracing one instruction at ADDRESS with customized callback - uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS); + uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, ADDRESS, ADDRESS); // emulate machine code in infinite time (last param = 0), or when // finishing all the code. diff --git a/samples/sample_m68k.c b/samples/sample_m68k.c index efda5294..4bbf1cce 100644 --- a/samples/sample_m68k.c +++ b/samples/sample_m68k.c @@ -108,10 +108,10 @@ static void test_m68k(void) uc_reg_write(uc, UC_M68K_REG_SR, &sr); // tracing all basic blocks with customized callback - uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0); // tracing all instruction - uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0); // emulate machine code in infinite time (last param = 0), or when // finishing all the code. diff --git a/samples/sample_mips.c b/samples/sample_mips.c index b27a02b1..994047c2 100644 --- a/samples/sample_mips.c +++ b/samples/sample_mips.c @@ -72,10 +72,10 @@ static void test_mips_eb(void) uc_reg_write(uc, UC_MIPS_REG_1, &r1); // tracing all basic blocks with customized callback - uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0); // tracing one instruction at ADDRESS with customized callback - uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS); + uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, ADDRESS, ADDRESS); // emulate machine code in infinite time (last param = 0), or when // finishing all the code. @@ -122,10 +122,10 @@ static void test_mips_el(void) uc_reg_write(uc, UC_MIPS_REG_1, &r1); // tracing all basic blocks with customized callback - uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0); // tracing one instruction at ADDRESS with customized callback - uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS); + uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, ADDRESS, ADDRESS); // emulate machine code in infinite time (last param = 0), or when // finishing all the code. diff --git a/samples/sample_sparc.c b/samples/sample_sparc.c index e6276b14..649a6dec 100644 --- a/samples/sample_sparc.c +++ b/samples/sample_sparc.c @@ -76,10 +76,10 @@ static void test_sparc(void) uc_reg_write(uc, UC_SPARC_REG_G3, &g3); // tracing all basic blocks with customized callback - uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0); // tracing all instructions with customized callback - uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0); // emulate machine code in infinite time (last param = 0), or when // finishing all the code. diff --git a/samples/sample_x86.c b/samples/sample_x86.c index 87a6d60f..7a04b3ff 100644 --- a/samples/sample_x86.c +++ b/samples/sample_x86.c @@ -219,10 +219,10 @@ static void test_i386(void) uc_reg_write(uc, UC_X86_REG_EDX, &r_edx); // tracing all basic blocks with customized callback - uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0); // tracing all instruction by having @begin > @end - uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0); // emulate machine code in infinite time err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32) - 1, 0, 0); @@ -289,10 +289,10 @@ static void test_i386_map_ptr(void) uc_reg_write(uc, UC_X86_REG_EDX, &r_edx); // tracing all basic blocks with customized callback - uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0); // tracing all instruction by having @begin > @end - uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0); // emulate machine code in infinite time err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32) - 1, 0, 0); @@ -345,10 +345,10 @@ static void test_i386_jump(void) } // tracing 1 basic block with customized callback - uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS); + uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, ADDRESS, ADDRESS); // tracing 1 instruction at ADDRESS - uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS); + uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, ADDRESS, ADDRESS); // emulate machine code in infinite time err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_JUMP) - 1, 0, 0); @@ -447,10 +447,10 @@ static void test_i386_invalid_mem_read(void) uc_reg_write(uc, UC_X86_REG_EDX, &r_edx); // tracing all basic blocks with customized callback - uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0); // tracing all instruction by having @begin > @end - uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0); // emulate machine code in infinite time err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_MEM_READ) - 1, 0, 0); @@ -505,13 +505,13 @@ static void test_i386_invalid_mem_write(void) uc_reg_write(uc, UC_X86_REG_EDX, &r_edx); // tracing all basic blocks with customized callback - uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0); // tracing all instruction by having @begin > @end - uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0); // intercept invalid memory events - uc_hook_add(uc, &trace3, UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, hook_mem_invalid, NULL); + uc_hook_add(uc, &trace3, UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, hook_mem_invalid, NULL, 1, 0); // emulate machine code in infinite time err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_MEM_WRITE) - 1, 0, 0); @@ -576,10 +576,10 @@ static void test_i386_jump_invalid(void) uc_reg_write(uc, UC_X86_REG_EDX, &r_edx); // tracing all basic blocks with customized callback - uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0); // tracing all instructions by having @begin > @end - uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0); // emulate machine code in infinite time err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_JMP_INVALID) - 1, 0, 0); @@ -633,15 +633,15 @@ static void test_i386_inout(void) uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx); // tracing all basic blocks with customized callback - uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0); // tracing all instructions - uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0); // uc IN instruction - uc_hook_add(uc, &trace3, UC_HOOK_INSN, hook_in, NULL, UC_X86_INS_IN); + uc_hook_add(uc, &trace3, UC_HOOK_INSN, hook_in, NULL, 1, 0, UC_X86_INS_IN); // uc OUT instruction - uc_hook_add(uc, &trace4, UC_HOOK_INSN, hook_out, NULL, UC_X86_INS_OUT); + uc_hook_add(uc, &trace4, UC_HOOK_INSN, hook_out, NULL, 1, 0, UC_X86_INS_OUT); // emulate machine code in infinite time err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_INOUT) - 1, 0, 0); @@ -722,16 +722,16 @@ static void test_x86_64(void) uc_reg_write(uc, UC_X86_REG_R15, &r15); // tracing all basic blocks with customized callback - uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0); // tracing all instructions in the range [ADDRESS, ADDRESS+20] - uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code64, NULL, (uint64_t)ADDRESS, (uint64_t)(ADDRESS+20)); + uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code64, NULL, ADDRESS, ADDRESS+20); // tracing all memory WRITE access (with @begin > @end) - uc_hook_add(uc, &trace3, UC_HOOK_MEM_WRITE, hook_mem64, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace3, UC_HOOK_MEM_WRITE, hook_mem64, NULL, 1, 0); // tracing all memory READ access (with @begin > @end) - uc_hook_add(uc, &trace4, UC_HOOK_MEM_READ, hook_mem64, NULL, (uint64_t)1, (uint64_t)0); + uc_hook_add(uc, &trace4, UC_HOOK_MEM_READ, hook_mem64, NULL, 1, 0); // emulate machine code in infinite time (last param = 0), or when // finishing all the code. @@ -805,7 +805,7 @@ static void test_x86_64_syscall(void) } // hook interrupts for syscall - uc_hook_add(uc, &trace1, UC_HOOK_INSN, hook_syscall, NULL, UC_X86_INS_SYSCALL); + uc_hook_add(uc, &trace1, UC_HOOK_INSN, hook_syscall, NULL, 1, 0, UC_X86_INS_SYSCALL); // initialize machine registers uc_reg_write(uc, UC_X86_REG_RAX, &rax); diff --git a/samples/shellcode.c b/samples/shellcode.c index a6fd5221..8ad0c69b 100644 --- a/samples/shellcode.c +++ b/samples/shellcode.c @@ -138,7 +138,7 @@ static void test_i386(void) uc_hook_add(uc, &trace1, UC_HOOK_CODE, hook_code, NULL, 1, 0); // handle interrupt ourself - uc_hook_add(uc, &trace2, UC_HOOK_INTR, hook_intr, NULL); + uc_hook_add(uc, &trace2, UC_HOOK_INTR, hook_intr, NULL, 1, 0); printf("\n>>> Start tracing this Linux code\n"); diff --git a/tests/regress/LICENSE b/tests/regress/LICENSE new file mode 100644 index 00000000..dd85900f --- /dev/null +++ b/tests/regress/LICENSE @@ -0,0 +1,30 @@ +This is the software license for Unicorn regression tests. The regression tests +are written by several Unicorn contributors (See CREDITS.TXT) and maintained by +Hoang-Vu Dang + +Copyright (c) 2015, Unicorn contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither the name of the developer(s) nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/tests/regress/jumping.py b/tests/regress/jumping.py new file mode 100755 index 00000000..265ec075 --- /dev/null +++ b/tests/regress/jumping.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python +# Mariano Graziano + +from unicorn import * +from unicorn.x86_const import * + +import regress + +#echo -ne "\x48\x31\xc0\x48\xb8\x04\x00\x00\x00\x00\x00\x00\x00\x48\x3d\x05\x00\x00\x00\x74\x05\xe9\x0f\x00\x00\x00\x48\xba\xbe\xba\x00\x00\x00\x00\x00\x00\xe9\x0f\x00\x00\x00\x48\xba\xca\xc0\x00\x00\x00\x00\x00\x00\xe9\x00\x00\x00\x00\x90" | ndisasm - -b64 +#00000000 4831C0 xor rax,rax +#00000003 48B8040000000000 mov rax,0x4 +# -0000 +#0000000D 483D05000000 cmp rax,0x5 +#00000013 7405 jz 0x1a +#00000015 E90F000000 jmp qword 0x29 +#0000001A 48BABEBA00000000 mov rdx,0xbabe +# -0000 +#00000024 E90F000000 jmp qword 0x38 +#00000029 48BACAC000000000 mov rdx,0xc0ca +# -0000 +#00000033 E900000000 jmp qword 0x38 +#00000038 90 nop + + +mu = 0 +zf = 1 # (0:clear, 1:set) + + +class Init(regress.RegressTest): + def clear_zf(self): + eflags_cur = mu.reg_read(UC_X86_REG_EFLAGS) + eflags = eflags_cur & ~(1 << 6) + #eflags = 0x0 + print "[clear_zf] - eflags from %x to %x" % (eflags_cur, eflags) + if eflags != eflags_cur: + print "[clear_zf] - writing new eflags..." + mu.reg_write(UC_X86_REG_EFLAGS, eflags) + + def set_zf(self): + eflags_cur = mu.reg_read(UC_X86_REG_EFLAGS) + eflags = eflags_cur | (1 << 6) + #eflags = 0xFFFFFFFF + print "[set_zf] - eflags from %x to %x" % (eflags_cur, eflags) + if eflags != eflags_cur: + print "[set_zf] - writing new eflags..." + mu.reg_write(UC_X86_REG_EFLAGS, eflags) + + def handle_zf(self, zf): + print "[handle_zf] - eflags " , zf + if zf == 0: self.clear_zf() + else: self.set_zf() + + def multipath(self): + print "[multipath] - handling ZF (%s) - default" % zf + self.handle_zf(zf) + + # callback for tracing basic blocks + def hook_block(self, uc, address, size, user_data): + print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) + + # callback for tracing instructions + def hook_code(self, uc, address, size, user_data): + print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size)) + rax = mu.reg_read(UC_X86_REG_RAX) + rbx = mu.reg_read(UC_X86_REG_RBX) + rcx = mu.reg_read(UC_X86_REG_RCX) + rdx = mu.reg_read(UC_X86_REG_RDX) + rsi = mu.reg_read(UC_X86_REG_RSI) + rdi = mu.reg_read(UC_X86_REG_RDI) + r8 = mu.reg_read(UC_X86_REG_R8) + r9 = mu.reg_read(UC_X86_REG_R9) + r10 = mu.reg_read(UC_X86_REG_R10) + r11 = mu.reg_read(UC_X86_REG_R11) + r12 = mu.reg_read(UC_X86_REG_R12) + r13 = mu.reg_read(UC_X86_REG_R13) + r14 = mu.reg_read(UC_X86_REG_R14) + r15 = mu.reg_read(UC_X86_REG_R15) + eflags = mu.reg_read(UC_X86_REG_EFLAGS) + + print(">>> RAX = %x" %rax) + print(">>> RBX = %x" %rbx) + print(">>> RCX = %x" %rcx) + print(">>> RDX = %x" %rdx) + print(">>> RSI = %x" %rsi) + print(">>> RDI = %x" %rdi) + print(">>> R8 = %x" %r8) + print(">>> R9 = %x" %r9) + print(">>> R10 = %x" %r10) + print(">>> R11 = %x" %r11) + print(">>> R12 = %x" %r12) + print(">>> R13 = %x" %r13) + print(">>> R14 = %x" %r14) + print(">>> R15 = %x" %r15) + print(">>> ELAGS = %x" %eflags) + print "-"*11 + self.multipath() + print "-"*11 + + # callback for tracing memory access (READ or WRITE) + def hook_mem_access(self, uc, access, address, size, value, user_data): + if access == UC_MEM_WRITE: + print(">>> Memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" \ + %(address, size, value)) + else: # READ + print(">>> Memory is being READ at 0x%x, data size = %u" \ + %(address, size)) + + # callback for tracing invalid memory access (READ or WRITE) + def hook_mem_invalid(self, uc, access, address, size, value, user_data): + print("[ HOOK_MEM_INVALID - Address: %s ]" % hex(address)) + if access == UC_MEM_WRITE_UNMAPPED: + print(">>> Missing memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" %(address, size, value)) + return True + else: + print(">>> Missing memory is being READ at 0x%x, data size = %u, data value = 0x%x" %(address, size, value)) + return True + + + def hook_mem_fetch_unmapped(self, uc, access, address, size, value, user_data): + print("[ HOOK_MEM_FETCH - Address: %s ]" % hex(address)) + print("[ mem_fetch_unmapped: faulting address at %s ]" % hex(address).strip("L")) + return True + + def runTest(self): + global mu + + JUMP = "\x48\x31\xc0\x48\xb8\x04\x00\x00\x00\x00\x00\x00\x00\x48\x3d\x05\x00\x00\x00\x74\x05\xe9\x0f\x00\x00\x00\x48\xba\xbe\xba\x00\x00\x00\x00\x00\x00\xe9\x0f\x00\x00\x00\x48\xba\xca\xc0\x00\x00\x00\x00\x00\x00\xe9\x00\x00\x00\x00\x90" + + ADDRESS = 0x1000000 + + print("Emulate x86_64 code") + # Initialize emulator in X86-64bit mode + mu = Uc(UC_ARCH_X86, UC_MODE_64) + + # map 2MB memory for this emulation + mu.mem_map(ADDRESS, 2 * 1024 * 1024) + + # write machine code to be emulated to memory + mu.mem_write(ADDRESS, JUMP) + + # setup stack + mu.reg_write(UC_X86_REG_RSP, ADDRESS + 0x200000) + + # tracing all basic blocks with customized callback + mu.hook_add(UC_HOOK_BLOCK, self.hook_block) + + # tracing all instructions in range [ADDRESS, ADDRESS+0x60] + mu.hook_add(UC_HOOK_CODE, self.hook_code, None, ADDRESS, ADDRESS+0x60) + + # tracing all memory READ & WRITE access + mu.hook_add(UC_HOOK_MEM_WRITE, self.hook_mem_access) + mu.hook_add(UC_HOOK_MEM_READ, self.hook_mem_access) + mu.hook_add(UC_HOOK_MEM_FETCH_UNMAPPED, self.hook_mem_fetch_unmapped) + mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, self.hook_mem_invalid) + + try: + # emulate machine code in infinite time + mu.emu_start(ADDRESS, ADDRESS + len(JUMP)) + except UcError as e: + print("ERROR: %s" % e) + + rdx = mu.reg_read(UC_X86_REG_RDX) + self.assertEqual(rdx, 0xbabe, "RDX contains the wrong value. Eflags modification failed.") + + +if __name__ == '__main__': + regress.main() diff --git a/tests/unit/Makefile b/tests/unit/Makefile index 67376fd2..d08671d1 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -5,7 +5,7 @@ CFLAGS += -lcmocka -lunicorn CFLAGS += -I ../../include ALL_TESTS = test_sanity test_x86 test_mem_map test_mem_high test_mem_map_ptr \ - test_tb_x86 test_multihook test_pc_change + test_tb_x86 test_multihook test_pc_change test_x86_soft_paging .PHONY: all all: ${ALL_TESTS} @@ -25,6 +25,7 @@ test: ${ALL_TESTS} ./test_tb_x86 ./test_multihook ./test_pc_change + ./test_x86_soft_paging test_sanity: test_sanity.c test_x86: test_x86.c @@ -34,6 +35,7 @@ test_mem_high: test_mem_high.c test_tb_x86: test_tb_x86.c test_multihook: test_multihook.c test_pc_change: test_pc_change.c +test_x86_soft_paging: test_x86_soft_paging.c ${ALL_TESTS}: ${CC} ${CFLAGS} -o $@ $^ diff --git a/tests/unit/test_mem_high.c b/tests/unit/test_mem_high.c index 5e2244e5..c8e58288 100644 --- a/tests/unit/test_mem_high.c +++ b/tests/unit/test_mem_high.c @@ -79,7 +79,7 @@ static void test_high_address_reads(void **state) uint8_t code[] = {0x48,0x8b,0x00,0x90,0x90,0x90,0x90}; // mov rax, [rax], nops uc_assert_success(uc_mem_map(uc, base_addr, 4096, UC_PROT_ALL)); uc_assert_success(uc_mem_write(uc, base_addr, code, 7)); - uc_assert_success(uc_hook_add(uc, &trace2, UC_HOOK_MEM_READ, hook_mem64, NULL, (uint64_t)1, (uint64_t)0)); + uc_assert_success(uc_hook_add(uc, &trace2, UC_HOOK_MEM_READ, hook_mem64, NULL, 1, 0)); uc_assert_success(uc_emu_start(uc, base_addr, base_addr + 3, 0, 0)); if(number_of_memory_reads != 1) { fail_msg("wrong number of memory reads for instruction %i", number_of_memory_reads); diff --git a/tests/unit/test_mem_map.c b/tests/unit/test_mem_map.c index 303a370b..9c2b9892 100644 --- a/tests/unit/test_mem_map.c +++ b/tests/unit/test_mem_map.c @@ -158,6 +158,15 @@ static void test_strange_map(void **state) uc_mem_unmap(uc, 0x0,0x1000); } +static void test_query_page_size(void **state) +{ + uc_engine *uc = *state; + + size_t page_size; + uc_assert_success(uc_query(uc, UC_QUERY_PAGE_SIZE, &page_size)); + assert_int_equal(4096, page_size); +} + void write(uc_engine* uc, uint64_t addr, uint64_t len){ uint8_t* buff = alloca(len); memset(buff,0,len); @@ -220,6 +229,7 @@ int main(void) { test(test_unmap_double_map), test(test_overlap_unmap_double_map), test(test_strange_map), + test(test_query_page_size), }; #undef test return cmocka_run_group_tests(tests, NULL, NULL); diff --git a/tests/unit/test_multihook.c b/tests/unit/test_multihook.c index eb831865..c131481d 100644 --- a/tests/unit/test_multihook.c +++ b/tests/unit/test_multihook.c @@ -96,8 +96,8 @@ static void test_basic_blocks(void **state) 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_hook_add(uc, &trace2, UC_HOOK_BLOCK, test_basic_blocks_hook2, &bbtest, (uint64_t)1, (uint64_t)0)); + OK(uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, test_basic_blocks_hook, &bbtest, 1, 0)); + OK(uc_hook_add(uc, &trace2, UC_HOOK_BLOCK, test_basic_blocks_hook2, &bbtest, 1, 0)); OK(uc_emu_start(uc, address, address+sizeof(code), 0, 0)); } diff --git a/tests/unit/test_pc_change.c b/tests/unit/test_pc_change.c index 7994e772..c2ec73a8 100644 --- a/tests/unit/test_pc_change.c +++ b/tests/unit/test_pc_change.c @@ -83,7 +83,7 @@ static void test_pc_change(void **state) printf("ECX = %u, EDX = %u\n", r_ecx, r_edx); // trace all instructions - OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, test_code_hook, NULL, (uint64_t)1, (uint64_t)0)); + OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, test_code_hook, NULL, 1, 0)); OK(uc_emu_start(uc, address, address+sizeof(code), 0, 0)); diff --git a/tests/unit/test_tb_x86.c b/tests/unit/test_tb_x86.c index bf691e8b..1d6ba7a0 100644 --- a/tests/unit/test_tb_x86.c +++ b/tests/unit/test_tb_x86.c @@ -273,16 +273,16 @@ static void test_tb_x86_64_32_imul_Gv_Ev_Ib(void **state) UC_HOOK_CODE, hook_code32, NULL, - (uint64_t)1, - (uint64_t)0)); + 1, + 0)); uc_assert_success(uc_hook_add(uc, &trace2, UC_HOOK_MEM_VALID, hook_mem32, NULL, - (uint64_t)1, - (uint64_t)0)); + 1, + 0)); uc_assert_success(uc_emu_start(uc, #ifdef RIP_NEXT_TO_THE_SELFMODIFY_OPCODE diff --git a/tests/unit/test_x86.c b/tests/unit/test_x86.c index 04441123..348cd5cd 100644 --- a/tests/unit/test_x86.c +++ b/tests/unit/test_x86.c @@ -85,7 +85,7 @@ static void test_basic_blocks(void **state) 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_hook_add(uc, &trace1, UC_HOOK_BLOCK, test_basic_blocks_hook, &bbtest, 1, 0)); OK(uc_emu_start(uc, address, address+sizeof(code), 0, 0)); } @@ -144,11 +144,11 @@ static void test_i386(void **state) 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); + err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 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); + err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0); uc_assert_success(err); // emulate machine code in infinite time @@ -194,11 +194,11 @@ static void test_i386_jump(void **state) 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); + err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, address, 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); + err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, address, address); uc_assert_success(err); // emulate machine code in infinite time @@ -302,19 +302,19 @@ static void test_i386_inout(void **state) 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); + err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 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); + err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0); uc_assert_success(err); // uc IN instruction - err = uc_hook_add(uc, &trace3, UC_HOOK_INSN, hook_in, NULL, UC_X86_INS_IN); + err = uc_hook_add(uc, &trace3, UC_HOOK_INSN, hook_in, NULL, 1, 0, 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); + err = uc_hook_add(uc, &trace4, UC_HOOK_INSN, hook_out, NULL, 1, 0, UC_X86_INS_OUT); uc_assert_success(err); // emulate machine code in infinite time @@ -566,19 +566,19 @@ static void test_x86_64(void **state) 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); + err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 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)); + err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code64, NULL, address, 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); + err = uc_hook_add(uc, &trace3, UC_HOOK_MEM_WRITE, hook_mem64, NULL, 1, 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); + err = uc_hook_add(uc, &trace4, UC_HOOK_MEM_READ, hook_mem64, NULL, 1, 0); uc_assert_success(err); // emulate machine code in infinite time (last param = 0), or when @@ -662,7 +662,7 @@ static void test_x86_64_syscall(void **state) uc_assert_success(err); // hook interrupts for syscall - err = uc_hook_add(uc, &trace1, UC_HOOK_INSN, hook_syscall, NULL, UC_X86_INS_SYSCALL); + err = uc_hook_add(uc, &trace1, UC_HOOK_INSN, hook_syscall, NULL, 1, 0, UC_X86_INS_SYSCALL); uc_assert_success(err); // initialize machine registers diff --git a/tests/unit/test_x86_soft_paging.c b/tests/unit/test_x86_soft_paging.c new file mode 100644 index 00000000..ffba0155 --- /dev/null +++ b/tests/unit/test_x86_soft_paging.c @@ -0,0 +1,210 @@ +#include "unicorn_test.h" +#include + +/* + Two tests here for software paging + Low paging: Test paging using virtual addresses already mapped by Unicorn + High paging: Test paging using virtual addresses not mapped by Unicorn +*/ + +static void test_low_paging(void **state) { + uc_engine *uc; + uc_err err; + int r_eax; + + /* The following x86 code will map emulated physical memory + to virtual memory using pages and attempt + to read/write from virtual memory + + Specifically, the virtual memory address range + has been mapped by Unicorn (0x7FF000 - 0x7FFFFF) + + Memory area purposes: + 0x1000 = page directory + 0x2000 = page table (identity map first 4 MiB) + 0x3000 = page table (0x007FF000 -> 0x00004000) + 0x4000 = data area (0xBEEF) + */ + const uint8_t code[] = { + /* Zero memory for page directories and page tables */ + 0xBF, 0x00, 0x10, 0x00, 0x00, /* MOV EDI, 0x1000 */ + 0xB9, 0x00, 0x10, 0x00, 0x00, /* MOV ECX, 0x1000 */ + 0x31, 0xC0, /* XOR EAX, EAX */ + 0xF3, 0xAB, /* REP STOSD */ + + /* Load DWORD [0x4000] with 0xDEADBEEF to retrieve later */ + 0xBF, 0x00, 0x40, 0x00, 0x00, /* MOV EDI, 0x4000 */ + 0xB8, 0xEF, 0xBE, 0x00, 0x00, /* MOV EAX, 0xBEEF */ + 0x89, 0x07, /* MOV [EDI], EAX */ + + /* Identity map the first 4MiB of memory */ + 0xB9, 0x00, 0x04, 0x00, 0x00, /* MOV ECX, 0x400 */ + 0xBF, 0x00, 0x20, 0x00, 0x00, /* MOV EDI, 0x2000 */ + 0xB8, 0x03, 0x00, 0x00, 0x00, /* MOV EAX, 3 */ + /* aLoop: */ + 0xAB, /* STOSD */ + 0x05, 0x00, 0x10, 0x00, 0x00, /* ADD EAX, 0x1000 */ + 0xE2, 0xF8, /* LOOP aLoop */ + + /* Map physical address 0x4000 to virtual address 0x7FF000 */ + 0xBF, 0xFC, 0x3F, 0x00, 0x00, /* MOV EDI, 0x3FFC */ + 0xB8, 0x03, 0x40, 0x00, 0x00, /* MOV EAX, 0x4003 */ + 0x89, 0x07, /* MOV [EDI], EAX */ + + /* Add page tables into page directory */ + 0xBF, 0x00, 0x10, 0x00, 0x00, /* MOV EDI, 0x1000 */ + 0xB8, 0x03, 0x20, 0x00, 0x00, /* MOV EAX, 0x2003 */ + 0x89, 0x07, /* MOV [EDI], EAX */ + 0xBF, 0x04, 0x10, 0x00, 0x00, /* MOV EDI, 0x1004 */ + 0xB8, 0x03, 0x30, 0x00, 0x00, /* MOV EAX, 0x3003 */ + 0x89, 0x07, /* MOV [EDI], EAX */ + + /* Load the page directory register */ + 0xB8, 0x00, 0x10, 0x00, 0x00, /* MOV EAX, 0x1000 */ + 0x0F, 0x22, 0xD8, /* MOV CR3, EAX */ + + /* Enable paging */ + 0x0F, 0x20, 0xC0, /* MOV EAX, CR0 */ + 0x0D, 0x00, 0x00, 0x00, 0x80, /* OR EAX, 0x80000000 */ + 0x0F, 0x22, 0xC0, /* MOV CR0, EAX */ + + /* Clear EAX */ + 0x31, 0xC0, /* XOR EAX, EAX */ + + /* Load using virtual memory address; EAX = 0xBEEF */ + 0xBE, 0x00, 0xF0, 0x7F, 0x00, /* MOV ESI, 0x7FF000 */ + 0x8B, 0x06, /* MOV EAX, [ESI] */ + 0xF4, /* HLT */ + }; + + /* Initialise X86-32bit mode */ + err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc); + uc_assert_success(err); + + /* Map 8MB of memory at base address 0 */ + err = uc_mem_map(uc, 0, (8 * 1024 * 1024), UC_PROT_ALL); + uc_assert_success(err); + + /* Write code into memory at address 0 */ + err = uc_mem_write(uc, 0, code, sizeof(code)); + uc_assert_success(err); + + /* Start emulation */ + err = uc_emu_start(uc, 0, sizeof(code), 0, 0); + uc_assert_success(err); + + /* The code should have loaded 0xBEEF into EAX */ + uc_reg_read(uc, UC_X86_REG_EAX, &r_eax); + assert_int_equal(r_eax, 0xBEEF); + + uc_close(uc); +} + + +/****************************************************************************/ + + +static void test_high_paging(void **state) { + uc_engine *uc; + uc_err err; + int r_eax; + + /* The following x86 code will map emulated physical memory + to virtual memory using pages and attempt + to read/write from virtual memory + + Specifically, the virtual memory address range + has not been mapped by UC (0xFFFFF000 - 0xFFFFFFFF) + + Memory area purposes: + 0x1000 = page directory + 0x2000 = page table (identity map first 4 MiB) + 0x3000 = page table (0xFFFFF000 -> 0x00004000) + 0x4000 = data area (0xDEADBEEF) + */ + const uint8_t code[] = { + /* Zero memory for page directories and page tables */ + 0xBF, 0x00, 0x10, 0x00, 0x00, /* MOV EDI, 0x1000 */ + 0xB9, 0x00, 0x10, 0x00, 0x00, /* MOV ECX, 0x1000 */ + 0x31, 0xC0, /* XOR EAX, EAX */ + 0xF3, 0xAB, /* REP STOSD */ + + /* Load DWORD [0x4000] with 0xDEADBEEF to retrieve later */ + 0xBF, 0x00, 0x40, 0x00, 0x00, /* MOV EDI, 0x4000 */ + 0xB8, 0xEF, 0xBE, 0x00, 0x00, /* MOV EAX, 0xBEEF */ + 0x89, 0x07, /* MOV [EDI], EAX */ + + /* Identity map the first 4MiB of memory */ + 0xB9, 0x00, 0x04, 0x00, 0x00, /* MOV ECX, 0x400 */ + 0xBF, 0x00, 0x20, 0x00, 0x00, /* MOV EDI, 0x2000 */ + 0xB8, 0x03, 0x00, 0x00, 0x00, /* MOV EAX, 3 */ + /* aLoop: */ + 0xAB, /* STOSD */ + 0x05, 0x00, 0x10, 0x00, 0x00, /* ADD EAX, 0x1000 */ + 0xE2, 0xF8, /* LOOP aLoop */ + + /* Map physical address 0x4000 to virtual address 0xFFFFF000 */ + 0xBF, 0xFC, 0x3F, 0x00, 0x00, /* MOV EDI, 0x3FFC */ + 0xB8, 0x03, 0x40, 0x00, 0x00, /* MOV EAX, 0x4003 */ + 0x89, 0x07, /* MOV [EDI], EAX */ + + /* Add page tables into page directory */ + 0xBF, 0x00, 0x10, 0x00, 0x00, /* MOV EDI, 0x1000 */ + 0xB8, 0x03, 0x20, 0x00, 0x00, /* MOV EAX, 0x2003 */ + 0x89, 0x07, /* MOV [EDI], EAX */ + 0xBF, 0xFC, 0x1F, 0x00, 0x00, /* MOV EDI, 0x1FFC */ + 0xB8, 0x03, 0x30, 0x00, 0x00, /* MOV EAX, 0x3003 */ + 0x89, 0x07, /* MOV [EDI], EAX */ + + /* Load the page directory register */ + 0xB8, 0x00, 0x10, 0x00, 0x00, /* MOV EAX, 0x1000 */ + 0x0F, 0x22, 0xD8, /* MOV CR3, EAX */ + + /* Enable paging */ + 0x0F, 0x20, 0xC0, /* MOV EAX, CR0 */ + 0x0D, 0x00, 0x00, 0x00, 0x80, /* OR EAX, 0x80000000 */ + 0x0F, 0x22, 0xC0, /* MOV CR0, EAX */ + + /* Clear EAX */ + 0x31, 0xC0, /* XOR EAX, EAX */ + + /* Load using virtual memory address; EAX = 0xBEEF */ + 0xBE, 0x00, 0xF0, 0xFF, 0xFF, /* MOV ESI, 0xFFFFF000 */ + 0x8B, 0x06, /* MOV EAX, [ESI] */ + 0xF4, /* HLT */ + }; + + /* Initialise X86-32bit mode */ + err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc); + uc_assert_success(err); + + /* Map 4MB of memory at base address 0 */ + err = uc_mem_map(uc, 0, (4 * 1024 * 1024), UC_PROT_ALL); + uc_assert_success(err); + + /* Write code into memory at address 0 */ + err = uc_mem_write(uc, 0, code, sizeof(code)); + uc_assert_success(err); + + /* Start emulation */ + err = uc_emu_start(uc, 0, sizeof(code), 0, 0); + uc_assert_success(err); + + /* The code should have loaded 0xBEEF into EAX */ + uc_reg_read(uc, UC_X86_REG_EAX, &r_eax); + assert_int_equal(r_eax, 0xBEEF); + + uc_close(uc); +} + + +/****************************************************************************/ + + +int main(void) { + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_low_paging), + cmocka_unit_test(test_high_paging), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/uc.c b/uc.c index 6841da17..d19d1199 100644 --- a/uc.c +++ b/uc.c @@ -547,7 +547,7 @@ uc_err uc_emu_start(uc_engine* uc, uint64_t begin, uint64_t until, uint64_t time } // set up count hook to count instructions. if (count > 0 && uc->count_hook == 0) { - uc_err err = uc_hook_add(uc, &uc->count_hook, UC_HOOK_CODE, hook_count_cb, NULL); + uc_err err = uc_hook_add(uc, &uc->count_hook, UC_HOOK_CODE, hook_count_cb, NULL, 1, 0); if (err != UC_ERR_OK) { return err; } @@ -561,6 +561,7 @@ uc_err uc_emu_start(uc_engine* uc, uint64_t begin, uint64_t until, uint64_t time if (timeout) enable_emu_timer(uc, timeout * 1000); // microseconds -> nanoseconds + uc->pause_all_vcpus(uc); // emulation is done uc->emulation_done = true; @@ -960,41 +961,42 @@ MemoryRegion *memory_mapping(struct uc_struct* uc, uint64_t address) } UNICORN_EXPORT -uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, ...) +uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, + void *user_data, uint64_t begin, uint64_t end, ...) { - va_list valist; int ret = UC_ERR_OK; - - va_start(valist, user_data); + int i = 0; struct hook *hook = calloc(1, sizeof(struct hook)); if (hook == NULL) { return UC_ERR_NOMEM; } + + hook->begin = begin; + hook->end = end; hook->type = type; hook->callback = callback; hook->user_data = user_data; hook->refs = 0; *hh = (uc_hook)hook; - // everybody but HOOK_INSN gets begin/end, so exit early here. + // UC_HOOK_INSN has an extra argument for instruction ID if (type & UC_HOOK_INSN) { + va_list valist; + + va_start(valist, end); hook->insn = va_arg(valist, int); - hook->begin = 1; - hook->end = 0; + va_end(valist); + if (list_append(&uc->hook[UC_HOOK_INSN_IDX], hook) == NULL) { free(hook); return UC_ERR_NOMEM; } + hook->refs++; return UC_ERR_OK; } - hook->begin = va_arg(valist, uint64_t); - hook->end = va_arg(valist, uint64_t); - va_end(valist); - - int i = 0; while ((type >> i) > 0) { if ((type >> i) & 1) { // TODO: invalid hook error? @@ -1088,6 +1090,11 @@ uint32_t uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count) UNICORN_EXPORT uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result) { + if (type == UC_QUERY_PAGE_SIZE) { + *result = uc->target_page_size; + return UC_ERR_OK; + } + switch(uc->arch) { case UC_ARCH_ARM: return uc->query(uc, type, result);