mirror of
https://github.com/yuzu-emu/unicorn.git
synced 2025-01-08 22:35:35 +00:00
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_multihook
|
||||||
test_pc_change
|
test_pc_change
|
||||||
mem_fuzz
|
mem_fuzz
|
||||||
|
test_x86_soft_paging
|
||||||
|
|
||||||
memleak_x86
|
memleak_x86
|
||||||
memleak_arm
|
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
|
Shaun Wheelhouse: Homebrew package
|
||||||
Kamil Rytarowski: Pkgsrc package
|
Kamil Rytarowski: Pkgsrc package
|
||||||
Zak Escano: MSVC binding
|
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
|
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
|
Unicorn is a lightweight, multi-platform, multi-architecture CPU emulator framework
|
||||||
based on [QEMU](http://qemu.org).
|
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>]
|
[<AutoOpen>]
|
||||||
module Common =
|
module Common =
|
||||||
|
let UC_API_MAJOR = 1
|
||||||
|
|
||||||
let UC_API_MAJOR = 0
|
let UC_API_MINOR = 0
|
||||||
let UC_API_MINOR = 9
|
|
||||||
let UC_SECOND_SCALE = 1000000
|
let UC_SECOND_SCALE = 1000000
|
||||||
let UC_MILISECOND_SCALE = 1000
|
let UC_MILISECOND_SCALE = 1000
|
||||||
let UC_ARCH_ARM = 1
|
let UC_ARCH_ARM = 1
|
||||||
|
@ -93,6 +93,7 @@ module Common =
|
||||||
let UC_HOOK_MEM_INVALID = 1008
|
let UC_HOOK_MEM_INVALID = 1008
|
||||||
let UC_HOOK_MEM_VALID = 7168
|
let UC_HOOK_MEM_VALID = 7168
|
||||||
let UC_QUERY_MODE = 1
|
let UC_QUERY_MODE = 1
|
||||||
|
let UC_QUERY_PAGE_SIZE = 2
|
||||||
|
|
||||||
let UC_PROT_NONE = 0
|
let UC_PROT_NONE = 0
|
||||||
let UC_PROT_READ = 1
|
let UC_PROT_READ = 1
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
#include <unicorn/unicorn.h>
|
#include <unicorn/unicorn.h>
|
||||||
#include "_cgo_export.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) {
|
uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback,
|
||||||
return uc_hook_add(handle, h2, type, callback, (void *)user, arg1);
|
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) {
|
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, arg1, arg2);
|
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) {
|
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)
|
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 callback unsafe.Pointer
|
||||||
var iarg1 C.int
|
var insn C.int
|
||||||
var uarg1, uarg2 C.uint64_t
|
var insnMode bool
|
||||||
rangeMode := false
|
|
||||||
switch htype {
|
switch htype {
|
||||||
case HOOK_BLOCK, HOOK_CODE:
|
case HOOK_BLOCK, HOOK_CODE:
|
||||||
rangeMode = true
|
|
||||||
callback = C.hookCode_cgo
|
callback = C.hookCode_cgo
|
||||||
case HOOK_MEM_READ, HOOK_MEM_WRITE, HOOK_MEM_READ | HOOK_MEM_WRITE:
|
case HOOK_MEM_READ, HOOK_MEM_WRITE, HOOK_MEM_READ | HOOK_MEM_WRITE:
|
||||||
rangeMode = true
|
|
||||||
callback = C.hookMemAccess_cgo
|
callback = C.hookMemAccess_cgo
|
||||||
case HOOK_INTR:
|
case HOOK_INTR:
|
||||||
callback = C.hookInterrupt_cgo
|
callback = C.hookInterrupt_cgo
|
||||||
case HOOK_INSN:
|
case HOOK_INSN:
|
||||||
iarg1 = C.int(extra[0])
|
insn = C.int(extra[0])
|
||||||
switch iarg1 {
|
insnMode = true
|
||||||
|
switch insn {
|
||||||
case X86_INS_IN:
|
case X86_INS_IN:
|
||||||
callback = C.hookX86In_cgo
|
callback = C.hookX86In_cgo
|
||||||
case X86_INS_OUT:
|
case X86_INS_OUT:
|
||||||
|
@ -93,7 +91,6 @@ func (u *uc) HookAdd(htype int, cb interface{}, extra ...uint64) (Hook, error) {
|
||||||
// special case for mask
|
// special case for mask
|
||||||
if htype&(HOOK_MEM_READ_UNMAPPED|HOOK_MEM_WRITE_UNMAPPED|HOOK_MEM_FETCH_UNMAPPED|
|
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 {
|
HOOK_MEM_READ_PROT|HOOK_MEM_WRITE_PROT|HOOK_MEM_FETCH_PROT) != 0 {
|
||||||
rangeMode = true
|
|
||||||
callback = C.hookMemInvalid_cgo
|
callback = C.hookMemInvalid_cgo
|
||||||
} else {
|
} else {
|
||||||
return 0, errors.New("Unknown hook type.")
|
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
|
var h2 C.uc_hook
|
||||||
data := &HookData{u, cb}
|
data := &HookData{u, cb}
|
||||||
uptr := uintptr(unsafe.Pointer(data))
|
uptr := uintptr(unsafe.Pointer(data))
|
||||||
if rangeMode {
|
if insnMode {
|
||||||
if len(extra) == 2 {
|
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)
|
||||||
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)
|
|
||||||
} else {
|
} 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
|
hookDataMap[uptr] = data
|
||||||
hookToUintptr[Hook(h2)] = uptr
|
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_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_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_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);
|
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);
|
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);
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MemRegion struct {
|
||||||
|
Begin, End uint64
|
||||||
|
Prot int
|
||||||
|
}
|
||||||
|
|
||||||
type Unicorn interface {
|
type Unicorn interface {
|
||||||
MemMap(addr, size uint64) error
|
MemMap(addr, size uint64) error
|
||||||
MemMapProt(addr, size uint64, prot int) error
|
MemMapProt(addr, size uint64, prot int) error
|
||||||
MemMapPtr(addr, size uint64, prot int, ptr unsafe.Pointer) error
|
MemMapPtr(addr, size uint64, prot int, ptr unsafe.Pointer) error
|
||||||
MemProtect(addr, size uint64, prot int) error
|
MemProtect(addr, size uint64, prot int) error
|
||||||
MemUnmap(addr, size uint64) error
|
MemUnmap(addr, size uint64) error
|
||||||
|
MemRegions() ([]*MemRegion, error)
|
||||||
MemRead(addr, size uint64) ([]byte, error)
|
MemRead(addr, size uint64) ([]byte, error)
|
||||||
MemReadInto(dst []byte, addr uint64) error
|
MemReadInto(dst []byte, addr uint64) error
|
||||||
MemWrite(addr uint64, data []byte) error
|
MemWrite(addr uint64, data []byte) error
|
||||||
RegRead(reg int) (uint64, error)
|
RegRead(reg int) (uint64, error)
|
||||||
RegWrite(reg int, value uint64) error
|
RegWrite(reg int, value uint64) error
|
||||||
|
RegReadMmr(reg int) (*X86Mmr, error)
|
||||||
|
RegWriteMmr(reg int, value *X86Mmr) error
|
||||||
Start(begin, until uint64) error
|
Start(begin, until uint64) error
|
||||||
StartWithOptions(begin, until uint64, options *UcOptions) error
|
StartWithOptions(begin, until uint64, options *UcOptions) error
|
||||||
Stop() 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
|
HookDel(hook Hook) error
|
||||||
|
Query(queryType int) (uint64, error)
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,6 +112,25 @@ func (u *uc) RegRead(reg int) (uint64, error) {
|
||||||
return uint64(val), errReturn(ucerr)
|
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 {
|
func (u *uc) MemWrite(addr uint64, data []byte) error {
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
@ -141,3 +169,9 @@ func (u *uc) MemProtect(addr, size uint64, prot int) error {
|
||||||
func (u *uc) MemUnmap(addr, size uint64) 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)))
|
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
|
package unicorn
|
||||||
// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [unicorn_const.go]
|
// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [unicorn_const.go]
|
||||||
const (
|
const (
|
||||||
|
API_MAJOR = 1
|
||||||
|
|
||||||
API_MAJOR = 0
|
API_MINOR = 0
|
||||||
API_MINOR = 9
|
|
||||||
SECOND_SCALE = 1000000
|
SECOND_SCALE = 1000000
|
||||||
MILISECOND_SCALE = 1000
|
MILISECOND_SCALE = 1000
|
||||||
ARCH_ARM = 1
|
ARCH_ARM = 1
|
||||||
|
@ -88,6 +88,7 @@ const (
|
||||||
HOOK_MEM_INVALID = 1008
|
HOOK_MEM_INVALID = 1008
|
||||||
HOOK_MEM_VALID = 7168
|
HOOK_MEM_VALID = 7168
|
||||||
QUERY_MODE = 1
|
QUERY_MODE = 1
|
||||||
|
QUERY_PAGE_SIZE = 2
|
||||||
|
|
||||||
PROT_NONE = 0
|
PROT_NONE = 0
|
||||||
PROT_READ = 1
|
PROT_READ = 1
|
||||||
|
|
|
@ -37,3 +37,39 @@ func TestDoubleClose(t *testing.T) {
|
||||||
t.Fatal(err)
|
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:
|
default:
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}, X86_INS_IN)
|
}, 1, 0, X86_INS_IN)
|
||||||
mu.HookAdd(HOOK_INSN, func(_ Unicorn, port, size, value uint32) {
|
mu.HookAdd(HOOK_INSN, func(_ Unicorn, port, size, value uint32) {
|
||||||
outCalled = true
|
outCalled = true
|
||||||
var err error
|
var err error
|
||||||
|
@ -111,7 +111,7 @@ func TestX86InOut(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}, X86_INS_OUT)
|
}, 1, 0, X86_INS_OUT)
|
||||||
if err := mu.Start(ADDRESS, ADDRESS+uint64(len(code))); err != nil {
|
if err := mu.Start(ADDRESS, ADDRESS+uint64(len(code))); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,7 @@ func TestX86Syscall(t *testing.T) {
|
||||||
mu.HookAdd(HOOK_INSN, func(_ Unicorn) {
|
mu.HookAdd(HOOK_INSN, func(_ Unicorn) {
|
||||||
rax, _ := mu.RegRead(X86_REG_RAX)
|
rax, _ := mu.RegRead(X86_REG_RAX)
|
||||||
mu.RegWrite(X86_REG_RAX, rax+1)
|
mu.RegWrite(X86_REG_RAX, rax+1)
|
||||||
}, X86_INS_SYSCALL)
|
}, 1, 0, X86_INS_SYSCALL)
|
||||||
mu.RegWrite(X86_REG_RAX, 0x100)
|
mu.RegWrite(X86_REG_RAX, 0x100)
|
||||||
err = mu.Start(ADDRESS, ADDRESS+uint64(len(code)))
|
err = mu.Start(ADDRESS, ADDRESS+uint64(len(code)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -143,3 +143,18 @@ func TestX86Syscall(t *testing.T) {
|
||||||
t.Fatal("Incorrect syscall return value.")
|
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;
|
package unicorn;
|
||||||
|
|
||||||
public interface UnicornConst {
|
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 = 0;
|
||||||
public static final int UC_API_MINOR = 9;
|
|
||||||
public static final int UC_SECOND_SCALE = 1000000;
|
public static final int UC_SECOND_SCALE = 1000000;
|
||||||
public static final int UC_MILISECOND_SCALE = 1000;
|
public static final int UC_MILISECOND_SCALE = 1000;
|
||||||
public static final int UC_ARCH_ARM = 1;
|
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_INVALID = 1008;
|
||||||
public static final int UC_HOOK_MEM_VALID = 7168;
|
public static final int UC_HOOK_MEM_VALID = 7168;
|
||||||
public static final int UC_QUERY_MODE = 1;
|
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_NONE = 0;
|
||||||
public static final int UC_PROT_READ = 1;
|
public static final int UC_PROT_READ = 1;
|
||||||
|
|
|
@ -291,8 +291,8 @@ def test_i386_inout():
|
||||||
mu.hook_add(UC_HOOK_CODE, hook_code)
|
mu.hook_add(UC_HOOK_CODE, hook_code)
|
||||||
|
|
||||||
# handle IN & OUT instruction
|
# handle IN & OUT instruction
|
||||||
mu.hook_add(UC_HOOK_INSN, hook_in, None, UC_X86_INS_IN)
|
mu.hook_add(UC_HOOK_INSN, hook_in, None, 1, 0, UC_X86_INS_IN)
|
||||||
mu.hook_add(UC_HOOK_INSN, hook_out, None, UC_X86_INS_OUT)
|
mu.hook_add(UC_HOOK_INSN, hook_out, None, 1, 0, UC_X86_INS_OUT)
|
||||||
|
|
||||||
# emulate machine code in infinite time
|
# emulate machine code in infinite time
|
||||||
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_INOUT))
|
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)
|
print('ERROR: was not expecting rax=%d in syscall' % rax)
|
||||||
|
|
||||||
# hook interrupts for syscall
|
# 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
|
# syscall handler is expecting rax=0x100
|
||||||
mu.reg_write(UC_X86_REG_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):
|
if os.path.exists(PATH_LIB64) and os.path.exists(PATH_LIB32):
|
||||||
PKG_NAME = 'unicorn-windows'
|
PKG_NAME = 'unicorn-windows'
|
||||||
|
|
||||||
VERSION = '0.9'
|
VERSION = '1.0'
|
||||||
SYSTEM = sys.platform
|
SYSTEM = sys.platform
|
||||||
|
|
||||||
# virtualenv breaks import, but get_python_lib() will work.
|
# 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("../../Makefile"))
|
||||||
src.extend(glob.glob("../../LICENSE*"))
|
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("../../*.TXT"))
|
||||||
src.extend(glob.glob("../../RELEASE_NOTES"))
|
src.extend(glob.glob("../../RELEASE_NOTES"))
|
||||||
src.extend(glob.glob("../../make.sh"))
|
src.extend(glob.glob("../../make.sh"))
|
||||||
|
|
|
@ -97,7 +97,7 @@ def test_i386(mode, code):
|
||||||
mu.hook_add(UC_HOOK_INTR, hook_intr)
|
mu.hook_add(UC_HOOK_INTR, hook_intr)
|
||||||
|
|
||||||
# handle SYSCALL
|
# 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
|
# emulate machine code in infinite time
|
||||||
mu.emu_start(ADDRESS, ADDRESS + len(code))
|
mu.emu_start(ADDRESS, ADDRESS + len(code))
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Unicorn Python bindings, by Nguyen Anh Quynnh <aquynh@gmail.com>
|
# 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 . import arm_const, arm64_const, mips_const, sparc_const, m68k_const, x86_const
|
||||||
from .unicorn_const import *
|
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.")
|
raise ImportError("ERROR: fail to load the dynamic library.")
|
||||||
|
|
||||||
|
|
||||||
|
__version__ = "%s.%s" %(UC_API_MAJOR, UC_API_MINOR)
|
||||||
|
|
||||||
# setup all the function prototype
|
# setup all the function prototype
|
||||||
def _setup_prototype(lib, fname, restype, *argtypes):
|
def _setup_prototype(lib, fname, restype, *argtypes):
|
||||||
getattr(lib, fname).restype = restype
|
getattr(lib, fname).restype = restype
|
||||||
|
@ -315,7 +317,7 @@ class Uc(object):
|
||||||
|
|
||||||
|
|
||||||
# add a hook
|
# 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()
|
_h2 = uc_hook_h()
|
||||||
|
|
||||||
# save callback & user_data
|
# 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
|
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)
|
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, \
|
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:
|
elif htype == UC_HOOK_INTR:
|
||||||
cb = ctypes.cast(UC_HOOK_INTR_CB(self._hook_intr_cb), UC_HOOK_INTR_CB)
|
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, \
|
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:
|
else:
|
||||||
begin = ctypes.c_uint64(arg1)
|
|
||||||
end = ctypes.c_uint64(arg2)
|
|
||||||
if htype in (UC_HOOK_BLOCK, UC_HOOK_CODE):
|
if htype in (UC_HOOK_BLOCK, UC_HOOK_CODE):
|
||||||
# set callback with wrapper, so it can be called
|
# set callback with wrapper, so it can be called
|
||||||
# with this object as param
|
# with this object as param
|
||||||
cb = ctypes.cast(UC_HOOK_CODE_CB(self._hookcode_cb), UC_HOOK_CODE_CB)
|
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, \
|
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 \
|
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_FETCH_UNMAPPED or htype & UC_HOOK_MEM_READ_PROT or \
|
||||||
htype & UC_HOOK_MEM_WRITE_PROT or htype & UC_HOOK_MEM_FETCH_PROT:
|
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)
|
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, \
|
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:
|
else:
|
||||||
cb = ctypes.cast(UC_HOOK_MEM_ACCESS_CB(self._hook_mem_access_cb), UC_HOOK_MEM_ACCESS_CB)
|
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, \
|
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.
|
# save the ctype function so gc will leave it alone.
|
||||||
self._ctype_cbs[self._callback_count] = cb
|
self._ctype_cbs[self._callback_count] = cb
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [unicorn_const.py]
|
# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [unicorn_const.py]
|
||||||
|
UC_API_MAJOR = 1
|
||||||
|
|
||||||
UC_API_MAJOR = 0
|
UC_API_MINOR = 0
|
||||||
UC_API_MINOR = 9
|
|
||||||
UC_SECOND_SCALE = 1000000
|
UC_SECOND_SCALE = 1000000
|
||||||
UC_MILISECOND_SCALE = 1000
|
UC_MILISECOND_SCALE = 1000
|
||||||
UC_ARCH_ARM = 1
|
UC_ARCH_ARM = 1
|
||||||
|
@ -86,6 +86,7 @@ UC_HOOK_MEM_FETCH_INVALID = 576
|
||||||
UC_HOOK_MEM_INVALID = 1008
|
UC_HOOK_MEM_INVALID = 1008
|
||||||
UC_HOOK_MEM_VALID = 7168
|
UC_HOOK_MEM_VALID = 7168
|
||||||
UC_QUERY_MODE = 1
|
UC_QUERY_MODE = 1
|
||||||
|
UC_QUERY_PAGE_SIZE = 2
|
||||||
|
|
||||||
UC_PROT_NONE = 0
|
UC_PROT_NONE = 0
|
||||||
UC_PROT_READ = 1
|
UC_PROT_READ = 1
|
||||||
|
|
|
@ -22,6 +22,17 @@
|
||||||
|
|
||||||
#define ARR_SIZE(a) (sizeof(a)/sizeof(a[0]))
|
#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);
|
QTAILQ_HEAD(CPUTailQ, CPUState);
|
||||||
|
|
||||||
typedef struct ModuleEntry {
|
typedef struct ModuleEntry {
|
||||||
|
|
|
@ -57,8 +57,8 @@ typedef size_t uc_hook;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Unicorn API version
|
// Unicorn API version
|
||||||
#define UC_API_MAJOR 0
|
#define UC_API_MAJOR 1
|
||||||
#define UC_API_MINOR 9
|
#define UC_API_MINOR 0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Macro to create combined version which can be compared to
|
Macro to create combined version which can be compared to
|
||||||
|
@ -262,6 +262,7 @@ typedef struct uc_mem_region {
|
||||||
typedef enum uc_query_type {
|
typedef enum uc_query_type {
|
||||||
// Dynamically query current hardware mode.
|
// Dynamically query current hardware mode.
|
||||||
UC_QUERY_MODE = 1,
|
UC_QUERY_MODE = 1,
|
||||||
|
UC_QUERY_PAGE_SIZE,
|
||||||
} uc_query_type;
|
} uc_query_type;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -457,13 +458,19 @@ uc_err uc_emu_stop(uc_engine *uc);
|
||||||
@callback: callback to be run when instruction is hit
|
@callback: callback to be run when instruction is hit
|
||||||
@user_data: user-defined data. This will be passed to callback function in its
|
@user_data: user-defined data. This will be passed to callback function in its
|
||||||
last argument @user_data
|
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)
|
@...: 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
|
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||||
for detailed error).
|
for detailed error).
|
||||||
*/
|
*/
|
||||||
UNICORN_EXPORT
|
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.
|
Unregister (remove) a hook callback.
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
# To be used to generate unicorn.pc for pkg-config
|
# To be used to generate unicorn.pc for pkg-config
|
||||||
|
|
||||||
# version major & minor
|
# version major & minor
|
||||||
PKG_MAJOR = 0
|
PKG_MAJOR = 1
|
||||||
PKG_MINOR = 9
|
PKG_MINOR = 0
|
||||||
|
|
||||||
# version bugfix level. Example: PKG_EXTRA = 1
|
# version bugfix level. Example: PKG_EXTRA = 1
|
||||||
PKG_EXTRA =
|
PKG_EXTRA =
|
||||||
|
|
|
@ -3,21 +3,11 @@
|
||||||
|
|
||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
#include "hw/arm/arm.h"
|
#include "hw/arm/arm.h"
|
||||||
|
|
||||||
#include "sysemu/cpus.h"
|
#include "sysemu/cpus.h"
|
||||||
|
|
||||||
#include "unicorn.h"
|
#include "unicorn.h"
|
||||||
|
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
|
|
||||||
#include "unicorn_common.h"
|
#include "unicorn_common.h"
|
||||||
|
#include "uc_priv.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)
|
|
||||||
|
|
||||||
|
|
||||||
static void arm64_set_pc(struct uc_struct *uc, uint64_t address)
|
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;
|
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)
|
int arm64_reg_write(struct uc_struct *uc, unsigned int regid, const void *value)
|
||||||
{
|
{
|
||||||
CPUState *mycpu = first_cpu;
|
CPUState *mycpu = first_cpu;
|
||||||
|
|
|
@ -3,20 +3,11 @@
|
||||||
|
|
||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
#include "hw/arm/arm.h"
|
#include "hw/arm/arm.h"
|
||||||
|
|
||||||
#include "sysemu/cpus.h"
|
#include "sysemu/cpus.h"
|
||||||
|
|
||||||
#include "unicorn.h"
|
#include "unicorn.h"
|
||||||
|
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "unicorn_common.h"
|
#include "unicorn_common.h"
|
||||||
|
#include "uc_priv.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)
|
|
||||||
|
|
||||||
|
|
||||||
static void arm_set_pc(struct uc_struct *uc, uint64_t address)
|
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;
|
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)
|
int arm_reg_write(struct uc_struct *uc, unsigned int regid, const void *value)
|
||||||
{
|
{
|
||||||
CPUState *mycpu = first_cpu;
|
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));
|
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
|
/* convert one instruction. s->is_jmp is set if the translation must
|
||||||
be stopped. Return the next pc value */
|
be stopped. Return the next pc value */
|
||||||
static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
|
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;
|
changed_cc_op = true;
|
||||||
}
|
}
|
||||||
gen_uc_tracecode(tcg_ctx, 0xf1f1f1f1, UC_HOOK_CODE_IDX, env->uc, pc_start);
|
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
|
// the callback might want to stop emulation immediately
|
||||||
check_exit_request(tcg_ctx);
|
check_exit_request(tcg_ctx);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,20 +2,14 @@
|
||||||
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2015 */
|
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2015 */
|
||||||
|
|
||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
#include "sysemu/cpus.h"
|
|
||||||
#include "hw/i386/pc.h"
|
#include "hw/i386/pc.h"
|
||||||
|
#include "sysemu/cpus.h"
|
||||||
#include "unicorn.h"
|
#include "unicorn.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "tcg.h"
|
#include "tcg.h"
|
||||||
|
|
||||||
#include "unicorn_common.h"
|
#include "unicorn_common.h"
|
||||||
#include <unicorn/x86.h> /* needed for uc_x86_mmr */
|
#include <unicorn/x86.h> /* needed for uc_x86_mmr */
|
||||||
|
#include "uc_priv.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)
|
|
||||||
|
|
||||||
|
|
||||||
static void x86_set_pc(struct uc_struct *uc, uint64_t address)
|
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;
|
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)
|
int x86_reg_write(struct uc_struct *uc, unsigned int regid, const void *value)
|
||||||
{
|
{
|
||||||
CPUState *mycpu = first_cpu;
|
CPUState *mycpu = first_cpu;
|
||||||
|
|
|
@ -6,14 +6,8 @@
|
||||||
#include "sysemu/cpus.h"
|
#include "sysemu/cpus.h"
|
||||||
#include "unicorn.h"
|
#include "unicorn.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
|
|
||||||
#include "unicorn_common.h"
|
#include "unicorn_common.h"
|
||||||
|
#include "uc_priv.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)
|
|
||||||
|
|
||||||
|
|
||||||
static void m68k_set_pc(struct uc_struct *uc, uint64_t address)
|
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;
|
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)
|
int m68k_reg_write(struct uc_struct *uc, unsigned int regid, const void *value)
|
||||||
{
|
{
|
||||||
CPUState *mycpu = first_cpu;
|
CPUState *mycpu = first_cpu;
|
||||||
|
|
|
@ -6,15 +6,8 @@
|
||||||
#include "sysemu/cpus.h"
|
#include "sysemu/cpus.h"
|
||||||
#include "unicorn.h"
|
#include "unicorn.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
|
|
||||||
#include "unicorn_common.h"
|
#include "unicorn_common.h"
|
||||||
|
#include "uc_priv.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)
|
|
||||||
|
|
||||||
|
|
||||||
static uint64_t mips_mem_redirect(uint64_t address)
|
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;
|
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)
|
int mips_reg_write(struct uc_struct *uc, unsigned int regid, const void *value)
|
||||||
{
|
{
|
||||||
CPUState *mycpu = first_cpu;
|
CPUState *mycpu = first_cpu;
|
||||||
|
|
|
@ -7,13 +7,7 @@
|
||||||
#include "unicorn.h"
|
#include "unicorn.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "unicorn_common.h"
|
#include "unicorn_common.h"
|
||||||
|
#include "uc_priv.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)
|
|
||||||
|
|
||||||
|
|
||||||
static bool sparc_stop_interrupt(int intno)
|
static bool sparc_stop_interrupt(int intno)
|
||||||
|
|
|
@ -7,13 +7,7 @@
|
||||||
#include "unicorn.h"
|
#include "unicorn.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "unicorn_common.h"
|
#include "unicorn_common.h"
|
||||||
|
#include "uc_priv.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)
|
|
||||||
|
|
||||||
|
|
||||||
static bool sparc_stop_interrupt(int intno)
|
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
|
// 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,
|
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");
|
printf("not ok - Failed to install hooks\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -248,10 +248,10 @@ static void do_perms_demo(bool change_perms)
|
||||||
}
|
}
|
||||||
|
|
||||||
// intercept code and invalid memory events
|
// 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_add(uc, &trace1,
|
||||||
UC_HOOK_MEM_INVALID,
|
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");
|
printf("not ok - Failed to install hooks\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -326,10 +326,10 @@ static void do_unmap_demo(bool do_unmap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// intercept code and invalid memory events
|
// 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_add(uc, &trace1,
|
||||||
UC_HOOK_MEM_INVALID,
|
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");
|
printf("not ok - Failed to install hooks\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,10 +77,10 @@ static void test_arm(void)
|
||||||
uc_reg_write(uc, UC_ARM_REG_R3, &r3);
|
uc_reg_write(uc, UC_ARM_REG_R3, &r3);
|
||||||
|
|
||||||
// tracing all basic blocks with customized callback
|
// 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
|
// 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
|
// emulate machine code in infinite time (last param = 0), or when
|
||||||
// finishing all the code.
|
// finishing all the code.
|
||||||
|
@ -128,10 +128,10 @@ static void test_thumb(void)
|
||||||
uc_reg_write(uc, UC_ARM_REG_SP, &sp);
|
uc_reg_write(uc, UC_ARM_REG_SP, &sp);
|
||||||
|
|
||||||
// tracing all basic blocks with customized callback
|
// 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
|
// 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
|
// emulate machine code in infinite time (last param = 0), or when
|
||||||
// finishing all the code.
|
// finishing all the code.
|
||||||
|
|
|
@ -75,10 +75,10 @@ static void test_arm64(void)
|
||||||
uc_reg_write(uc, UC_ARM64_REG_X15, &x15);
|
uc_reg_write(uc, UC_ARM64_REG_X15, &x15);
|
||||||
|
|
||||||
// tracing all basic blocks with customized callback
|
// 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
|
// 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
|
// emulate machine code in infinite time (last param = 0), or when
|
||||||
// finishing all the code.
|
// finishing all the code.
|
||||||
|
|
|
@ -108,10 +108,10 @@ static void test_m68k(void)
|
||||||
uc_reg_write(uc, UC_M68K_REG_SR, &sr);
|
uc_reg_write(uc, UC_M68K_REG_SR, &sr);
|
||||||
|
|
||||||
// tracing all basic blocks with customized callback
|
// 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
|
// 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
|
// emulate machine code in infinite time (last param = 0), or when
|
||||||
// finishing all the code.
|
// finishing all the code.
|
||||||
|
|
|
@ -72,10 +72,10 @@ static void test_mips_eb(void)
|
||||||
uc_reg_write(uc, UC_MIPS_REG_1, &r1);
|
uc_reg_write(uc, UC_MIPS_REG_1, &r1);
|
||||||
|
|
||||||
// tracing all basic blocks with customized callback
|
// 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
|
// 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
|
// emulate machine code in infinite time (last param = 0), or when
|
||||||
// finishing all the code.
|
// finishing all the code.
|
||||||
|
@ -122,10 +122,10 @@ static void test_mips_el(void)
|
||||||
uc_reg_write(uc, UC_MIPS_REG_1, &r1);
|
uc_reg_write(uc, UC_MIPS_REG_1, &r1);
|
||||||
|
|
||||||
// tracing all basic blocks with customized callback
|
// 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
|
// 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
|
// emulate machine code in infinite time (last param = 0), or when
|
||||||
// finishing all the code.
|
// finishing all the code.
|
||||||
|
|
|
@ -76,10 +76,10 @@ static void test_sparc(void)
|
||||||
uc_reg_write(uc, UC_SPARC_REG_G3, &g3);
|
uc_reg_write(uc, UC_SPARC_REG_G3, &g3);
|
||||||
|
|
||||||
// tracing all basic blocks with customized callback
|
// 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
|
// 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
|
// emulate machine code in infinite time (last param = 0), or when
|
||||||
// finishing all the code.
|
// finishing all the code.
|
||||||
|
|
|
@ -219,10 +219,10 @@ static void test_i386(void)
|
||||||
uc_reg_write(uc, UC_X86_REG_EDX, &r_edx);
|
uc_reg_write(uc, UC_X86_REG_EDX, &r_edx);
|
||||||
|
|
||||||
// tracing all basic blocks with customized callback
|
// 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
|
// 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
|
// emulate machine code in infinite time
|
||||||
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32) - 1, 0, 0);
|
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);
|
uc_reg_write(uc, UC_X86_REG_EDX, &r_edx);
|
||||||
|
|
||||||
// tracing all basic blocks with customized callback
|
// 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
|
// 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
|
// emulate machine code in infinite time
|
||||||
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32) - 1, 0, 0);
|
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
|
// 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
|
// 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
|
// emulate machine code in infinite time
|
||||||
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_JUMP) - 1, 0, 0);
|
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);
|
uc_reg_write(uc, UC_X86_REG_EDX, &r_edx);
|
||||||
|
|
||||||
// tracing all basic blocks with customized callback
|
// 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
|
// 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
|
// emulate machine code in infinite time
|
||||||
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_MEM_READ) - 1, 0, 0);
|
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);
|
uc_reg_write(uc, UC_X86_REG_EDX, &r_edx);
|
||||||
|
|
||||||
// tracing all basic blocks with customized callback
|
// 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
|
// 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
|
// 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
|
// emulate machine code in infinite time
|
||||||
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_MEM_WRITE) - 1, 0, 0);
|
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);
|
uc_reg_write(uc, UC_X86_REG_EDX, &r_edx);
|
||||||
|
|
||||||
// tracing all basic blocks with customized callback
|
// 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
|
// 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
|
// emulate machine code in infinite time
|
||||||
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_JMP_INVALID) - 1, 0, 0);
|
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);
|
uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx);
|
||||||
|
|
||||||
// tracing all basic blocks with customized callback
|
// 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
|
// 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 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 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
|
// emulate machine code in infinite time
|
||||||
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_INOUT) - 1, 0, 0);
|
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);
|
uc_reg_write(uc, UC_X86_REG_R15, &r15);
|
||||||
|
|
||||||
// tracing all basic blocks with customized callback
|
// 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]
|
// 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)
|
// 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)
|
// 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
|
// emulate machine code in infinite time (last param = 0), or when
|
||||||
// finishing all the code.
|
// finishing all the code.
|
||||||
|
@ -805,7 +805,7 @@ static void test_x86_64_syscall(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
// hook interrupts for syscall
|
// 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
|
// initialize machine registers
|
||||||
uc_reg_write(uc, UC_X86_REG_RAX, &rax);
|
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);
|
uc_hook_add(uc, &trace1, UC_HOOK_CODE, hook_code, NULL, 1, 0);
|
||||||
|
|
||||||
// handle interrupt ourself
|
// 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");
|
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
|
CFLAGS += -I ../../include
|
||||||
|
|
||||||
ALL_TESTS = test_sanity test_x86 test_mem_map test_mem_high test_mem_map_ptr \
|
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
|
.PHONY: all
|
||||||
all: ${ALL_TESTS}
|
all: ${ALL_TESTS}
|
||||||
|
@ -25,6 +25,7 @@ test: ${ALL_TESTS}
|
||||||
./test_tb_x86
|
./test_tb_x86
|
||||||
./test_multihook
|
./test_multihook
|
||||||
./test_pc_change
|
./test_pc_change
|
||||||
|
./test_x86_soft_paging
|
||||||
|
|
||||||
test_sanity: test_sanity.c
|
test_sanity: test_sanity.c
|
||||||
test_x86: test_x86.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_tb_x86: test_tb_x86.c
|
||||||
test_multihook: test_multihook.c
|
test_multihook: test_multihook.c
|
||||||
test_pc_change: test_pc_change.c
|
test_pc_change: test_pc_change.c
|
||||||
|
test_x86_soft_paging: test_x86_soft_paging.c
|
||||||
|
|
||||||
${ALL_TESTS}:
|
${ALL_TESTS}:
|
||||||
${CC} ${CFLAGS} -o $@ $^
|
${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
|
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_map(uc, base_addr, 4096, UC_PROT_ALL));
|
||||||
uc_assert_success(uc_mem_write(uc, base_addr, code, 7));
|
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));
|
uc_assert_success(uc_emu_start(uc, base_addr, base_addr + 3, 0, 0));
|
||||||
if(number_of_memory_reads != 1) {
|
if(number_of_memory_reads != 1) {
|
||||||
fail_msg("wrong number of memory reads for instruction %i", number_of_memory_reads);
|
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);
|
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){
|
void write(uc_engine* uc, uint64_t addr, uint64_t len){
|
||||||
uint8_t* buff = alloca(len);
|
uint8_t* buff = alloca(len);
|
||||||
memset(buff,0,len);
|
memset(buff,0,len);
|
||||||
|
@ -220,6 +229,7 @@ int main(void) {
|
||||||
test(test_unmap_double_map),
|
test(test_unmap_double_map),
|
||||||
test(test_overlap_unmap_double_map),
|
test(test_overlap_unmap_double_map),
|
||||||
test(test_strange_map),
|
test(test_strange_map),
|
||||||
|
test(test_query_page_size),
|
||||||
};
|
};
|
||||||
#undef test
|
#undef test
|
||||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
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)));
|
OK(uc_mem_write(uc, address, code, sizeof(code)));
|
||||||
|
|
||||||
// trace all basic blocks
|
// 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_hook_add(uc, &trace2, UC_HOOK_BLOCK, test_basic_blocks_hook2, &bbtest, (uint64_t)1, (uint64_t)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));
|
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);
|
printf("ECX = %u, EDX = %u\n", r_ecx, r_edx);
|
||||||
|
|
||||||
// trace all instructions
|
// 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));
|
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,
|
UC_HOOK_CODE,
|
||||||
hook_code32,
|
hook_code32,
|
||||||
NULL,
|
NULL,
|
||||||
(uint64_t)1,
|
1,
|
||||||
(uint64_t)0));
|
0));
|
||||||
|
|
||||||
uc_assert_success(uc_hook_add(uc,
|
uc_assert_success(uc_hook_add(uc,
|
||||||
&trace2,
|
&trace2,
|
||||||
UC_HOOK_MEM_VALID,
|
UC_HOOK_MEM_VALID,
|
||||||
hook_mem32,
|
hook_mem32,
|
||||||
NULL,
|
NULL,
|
||||||
(uint64_t)1,
|
1,
|
||||||
(uint64_t)0));
|
0));
|
||||||
|
|
||||||
uc_assert_success(uc_emu_start(uc,
|
uc_assert_success(uc_emu_start(uc,
|
||||||
#ifdef RIP_NEXT_TO_THE_SELFMODIFY_OPCODE
|
#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)));
|
OK(uc_mem_write(uc, address, code, sizeof(code)));
|
||||||
|
|
||||||
// trace all basic blocks
|
// 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));
|
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);
|
uc_assert_success(err);
|
||||||
|
|
||||||
// tracing all basic blocks with customized callback
|
// 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);
|
uc_assert_success(err);
|
||||||
|
|
||||||
// tracing all instruction by having @begin > @end
|
// 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);
|
uc_assert_success(err);
|
||||||
|
|
||||||
// emulate machine code in infinite time
|
// emulate machine code in infinite time
|
||||||
|
@ -194,11 +194,11 @@ static void test_i386_jump(void **state)
|
||||||
uc_assert_success(err);
|
uc_assert_success(err);
|
||||||
|
|
||||||
// tracing 1 basic block with customized callback
|
// 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);
|
uc_assert_success(err);
|
||||||
|
|
||||||
// tracing 1 instruction at address
|
// 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);
|
uc_assert_success(err);
|
||||||
|
|
||||||
// emulate machine code in infinite time
|
// emulate machine code in infinite time
|
||||||
|
@ -302,19 +302,19 @@ static void test_i386_inout(void **state)
|
||||||
uc_assert_success(err);
|
uc_assert_success(err);
|
||||||
|
|
||||||
// tracing all basic blocks with customized callback
|
// 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);
|
uc_assert_success(err);
|
||||||
|
|
||||||
// tracing all instructions
|
// 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_assert_success(err);
|
||||||
|
|
||||||
// uc IN instruction
|
// 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_assert_success(err);
|
||||||
|
|
||||||
// uc OUT instruction
|
// 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);
|
uc_assert_success(err);
|
||||||
|
|
||||||
// emulate machine code in infinite time
|
// 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));
|
uc_assert_success(uc_reg_write(uc, UC_X86_REG_R15, &r15));
|
||||||
|
|
||||||
// tracing all basic blocks with customized callback
|
// 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);
|
uc_assert_success(err);
|
||||||
|
|
||||||
// tracing all instructions in the range [address, address+20]
|
// 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);
|
uc_assert_success(err);
|
||||||
|
|
||||||
// tracing all memory WRITE access (with @begin > @end)
|
// 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);
|
uc_assert_success(err);
|
||||||
|
|
||||||
// tracing all memory READ access (with @begin > @end)
|
// 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);
|
uc_assert_success(err);
|
||||||
|
|
||||||
// emulate machine code in infinite time (last param = 0), or when
|
// 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);
|
uc_assert_success(err);
|
||||||
|
|
||||||
// hook interrupts for syscall
|
// 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);
|
uc_assert_success(err);
|
||||||
|
|
||||||
// initialize machine registers
|
// 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.
|
// set up count hook to count instructions.
|
||||||
if (count > 0 && uc->count_hook == 0) {
|
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) {
|
if (err != UC_ERR_OK) {
|
||||||
return err;
|
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)
|
if (timeout)
|
||||||
enable_emu_timer(uc, timeout * 1000); // microseconds -> nanoseconds
|
enable_emu_timer(uc, timeout * 1000); // microseconds -> nanoseconds
|
||||||
|
|
||||||
uc->pause_all_vcpus(uc);
|
uc->pause_all_vcpus(uc);
|
||||||
// emulation is done
|
// emulation is done
|
||||||
uc->emulation_done = true;
|
uc->emulation_done = true;
|
||||||
|
@ -960,41 +961,42 @@ MemoryRegion *memory_mapping(struct uc_struct* uc, uint64_t address)
|
||||||
}
|
}
|
||||||
|
|
||||||
UNICORN_EXPORT
|
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;
|
int ret = UC_ERR_OK;
|
||||||
|
int i = 0;
|
||||||
va_start(valist, user_data);
|
|
||||||
|
|
||||||
struct hook *hook = calloc(1, sizeof(struct hook));
|
struct hook *hook = calloc(1, sizeof(struct hook));
|
||||||
if (hook == NULL) {
|
if (hook == NULL) {
|
||||||
return UC_ERR_NOMEM;
|
return UC_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hook->begin = begin;
|
||||||
|
hook->end = end;
|
||||||
hook->type = type;
|
hook->type = type;
|
||||||
hook->callback = callback;
|
hook->callback = callback;
|
||||||
hook->user_data = user_data;
|
hook->user_data = user_data;
|
||||||
hook->refs = 0;
|
hook->refs = 0;
|
||||||
*hh = (uc_hook)hook;
|
*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) {
|
if (type & UC_HOOK_INSN) {
|
||||||
|
va_list valist;
|
||||||
|
|
||||||
|
va_start(valist, end);
|
||||||
hook->insn = va_arg(valist, int);
|
hook->insn = va_arg(valist, int);
|
||||||
hook->begin = 1;
|
va_end(valist);
|
||||||
hook->end = 0;
|
|
||||||
if (list_append(&uc->hook[UC_HOOK_INSN_IDX], hook) == NULL) {
|
if (list_append(&uc->hook[UC_HOOK_INSN_IDX], hook) == NULL) {
|
||||||
free(hook);
|
free(hook);
|
||||||
return UC_ERR_NOMEM;
|
return UC_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
hook->refs++;
|
hook->refs++;
|
||||||
return UC_ERR_OK;
|
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) {
|
while ((type >> i) > 0) {
|
||||||
if ((type >> i) & 1) {
|
if ((type >> i) & 1) {
|
||||||
// TODO: invalid hook error?
|
// 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
|
UNICORN_EXPORT
|
||||||
uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result)
|
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) {
|
switch(uc->arch) {
|
||||||
case UC_ARCH_ARM:
|
case UC_ARCH_ARM:
|
||||||
return uc->query(uc, type, result);
|
return uc->query(uc, type, result);
|
||||||
|
|
Loading…
Reference in a new issue