Merge branch 'master' into m1
This commit is contained in:
commit
fb1ebac000
1
.gitignore
vendored
1
.gitignore
vendored
@ -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
15
.travis.yml
Normal 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
|
@ -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
|
||||
|
@ -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
16
appveyor.yml
Normal 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
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
38
bindings/go/unicorn/x86.go
Normal file
38
bindings/go/unicorn/x86.go
Normal 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)
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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"))
|
||||
|
@ -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))
|
||||
|
@ -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__
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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 =
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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
30
tests/regress/LICENSE
Normal 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
167
tests/regress/jumping.py
Executable 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()
|
@ -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 $@ $^
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
210
tests/unit/test_x86_soft_paging.c
Normal file
210
tests/unit/test_x86_soft_paging.c
Normal 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
33
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);
|
||||
|
Loading…
Reference in New Issue
Block a user