Merge branch 'master' into m1

This commit is contained in:
Nguyen Anh Quynh 2016-03-09 15:13:42 +08:00
commit fb1ebac000
51 changed files with 760 additions and 221 deletions

1
.gitignore vendored
View file

@ -138,6 +138,7 @@ test_tb_x86
test_multihook
test_pc_change
mem_fuzz
test_x86_soft_paging
memleak_x86
memleak_arm

15
.travis.yml Normal file
View file

@ -0,0 +1,15 @@
language: c
sudo: false
before_install:
- export LD_LIBRARY_PATH=`pwd`/samples/:$LD_LIBRARY_PATH
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install glib; fi
script:
- ./make.sh
compiler:
- clang
- gcc
os:
- linux
- osx

View file

@ -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

View file

@ -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).

16
appveyor.yml Normal file
View file

@ -0,0 +1,16 @@
version: 1.0-{build}
platform:
- x64
environment:
global:
MSYS2_ARCH: x86_64
matrix:
- HOST_ARCH_ARG: --host=x86_64-w64-mingw32
ADD_PATH: /mingw64/bin
- HOST_ARCH_ARG: --host=i686-w64-mingw32
ADD_PATH: /mingw32/bin
build_script:
- make.sh cross-win64

View file

@ -6,9 +6,9 @@ open System
[<AutoOpen>]
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

View file

@ -1,12 +1,16 @@
#include <unicorn/unicorn.h>
#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) {

View file

@ -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

View file

@ -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);

View file

@ -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, &regions, &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)
}

View file

@ -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

View file

@ -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)
}
}

View file

@ -0,0 +1,38 @@
package unicorn
import (
"unsafe"
)
// #include <unicorn/unicorn.h>
// #include <unicorn/x86.h>
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)
}

View file

@ -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)
}
}

View file

@ -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;

View file

@ -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)

View file

@ -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"))

View file

@ -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))

View file

@ -1,4 +1,4 @@
# Unicorn Python bindings, by Nguyen Anh Quynnh <aquynh@gmail.com>
from . import arm_const, arm64_const, mips_const, sparc_const, m68k_const, x86_const
from .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__

View file

@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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.

View file

@ -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 =

View file

@ -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;

View file

@ -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;

View file

@ -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);
}

View file

@ -2,20 +2,14 @@
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 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 <unicorn/x86.h> /* 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;

View file

@ -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;

View file

@ -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;

View file

@ -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)

View file

@ -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)

View file

@ -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;
}

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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);

View file

@ -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");

30
tests/regress/LICENSE Normal file
View file

@ -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 <dang.hvu@gmail.com>
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.

167
tests/regress/jumping.py Executable file
View file

@ -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()

View file

@ -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 $@ $^

View file

@ -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);

View file

@ -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);

View file

@ -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));
}

View file

@ -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));

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,210 @@
#include "unicorn_test.h"
#include <inttypes.h>
/*
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);
}

33
uc.c
View file

@ -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);