Merge pull request #2 from unicorn-engine/master

Pulling in from unicorn-engine to my private  repo
This commit is contained in:
Egbert 2016-03-16 11:52:35 -04:00
commit 7cb8e889ad
117 changed files with 3174 additions and 1315 deletions

16
.appveyor.yml Normal file
View file

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

5
.gitignore vendored
View file

@ -134,6 +134,11 @@ test_mem_high
rw_hookstack
hook_extrainvoke
sysenter_hook_x86
test_tb_x86
test_multihook
test_pc_change
mem_fuzz
test_x86_soft_paging
#################

15
.travis.yml Normal file
View file

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

View file

@ -54,3 +54,8 @@ Nguyen Tan Cong
Loi Anh Tuan
Shaun Wheelhouse: Homebrew package
Kamil Rytarowski: Pkgsrc package
Zak Escano: MSVC binding
Chris Eagle: Java binding
Ryan Hileman: Go binding
Antonio Parata: .NET binding
Jonathon Reinhart: C unit test

View file

@ -228,7 +228,7 @@ compile_lib: config qemu/config-host.h-timestamp
unicorn: $(LIBRARY) $(ARCHIVE)
$(LIBRARY): $(UC_TARGET_OBJ) uc.o hook.o
$(LIBRARY): $(UC_TARGET_OBJ) uc.o list.o
ifeq ($(UNICORN_SHARED),yes)
ifeq ($(V),0)
$(call log,GEN,$(LIBRARY))
@ -241,7 +241,7 @@ ifneq (,$(LIBRARY_SYMLINK))
endif
endif
$(ARCHIVE): $(UC_TARGET_OBJ) uc.o hook.o
$(ARCHIVE): $(UC_TARGET_OBJ) uc.o list.o
ifeq ($(UNICORN_STATIC),yes)
ifeq ($(V),0)
$(call log,GEN,$(ARCHIVE))

View file

@ -1,6 +1,9 @@
Unicorn Engine
==============
[![Build Status](https://travis-ci.org/unicorn-engine/unicorn.svg?branch=master)](https://travis-ci.org/unicorn-engine/unicorn)
[![Build status](https://ci.appveyor.com/api/projects/status/kojr7bald748ba2x/branch/master?svg=true)](https://ci.appveyor.com/project/aquynh/unicorn/branch/master)
Unicorn is a lightweight, multi-platform, multi-architecture CPU emulator framework
based on [QEMU](http://qemu.org).

View file

@ -116,7 +116,8 @@ def gen(lang):
if f[0].startswith("UC_" + prefix.upper()):
if len(f) > 1 and f[1] not in ('//', '='):
print("Error: Unable to convert %s" % f)
print("WARNING: Unable to convert %s" % f)
print(" Line =", line)
continue
elif len(f) > 1 and f[1] == '=':
rhs = ''.join(f[2:])

View file

@ -6,9 +6,9 @@ open System
[<AutoOpen>]
module Common =
let UC_API_MAJOR = 1
let UC_API_MAJOR = 0
let UC_API_MINOR = 9
let UC_API_MINOR = 0
let UC_SECOND_SCALE = 1000000
let UC_MILISECOND_SCALE = 1000
let UC_ARCH_ARM = 1
@ -21,22 +21,26 @@ module Common =
let UC_ARCH_MAX = 8
let UC_MODE_LITTLE_ENDIAN = 0
let UC_MODE_BIG_ENDIAN = 1073741824
let UC_MODE_ARM = 0
let UC_MODE_16 = 2
let UC_MODE_32 = 4
let UC_MODE_64 = 8
let UC_MODE_THUMB = 16
let UC_MODE_MCLASS = 32
let UC_MODE_V8 = 64
let UC_MODE_MICRO = 16
let UC_MODE_MIPS3 = 32
let UC_MODE_MIPS32R6 = 64
let UC_MODE_V9 = 16
let UC_MODE_QPX = 16
let UC_MODE_BIG_ENDIAN = 1073741824
let UC_MODE_MIPS32 = 4
let UC_MODE_MIPS64 = 8
let UC_MODE_16 = 2
let UC_MODE_32 = 4
let UC_MODE_64 = 8
let UC_MODE_PPC32 = 4
let UC_MODE_PPC64 = 8
let UC_MODE_QPX = 16
let UC_MODE_SPARC32 = 4
let UC_MODE_SPARC64 = 8
let UC_MODE_V9 = 16
let UC_ERR_OK = 0
let UC_ERR_NOMEM = 1
@ -87,6 +91,9 @@ module Common =
let UC_HOOK_MEM_WRITE_INVALID = 288
let UC_HOOK_MEM_FETCH_INVALID = 576
let UC_HOOK_MEM_INVALID = 1008
let UC_HOOK_MEM_VALID = 7168
let UC_QUERY_MODE = 1
let UC_QUERY_PAGE_SIZE = 2
let UC_PROT_NONE = 0
let UC_PROT_READ = 1

View file

@ -251,7 +251,13 @@ module X86 =
let UC_X86_REG_R13W = 239
let UC_X86_REG_R14W = 240
let UC_X86_REG_R15W = 241
let UC_X86_REG_ENDING = 242
let UC_X86_REG_IDTR = 242
let UC_X86_REG_GDTR = 243
let UC_X86_REG_LDTR = 244
let UC_X86_REG_TR = 245
let UC_X86_REG_FPCW = 246
let UC_X86_REG_FPTAG = 247
let UC_X86_REG_ENDING = 248
// X86 instructions

View file

@ -1,12 +1,16 @@
#include <unicorn/unicorn.h>
#include "_cgo_export.h"
uc_err uc_hook_add_i1(uc_engine *handle, uc_hook *h2, uc_hook_type type, void *callback, uintptr_t user, int arg1) {
return uc_hook_add(handle, h2, type, callback, (void *)user, arg1);
uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback,
void *user_data, uint64_t begin, uint64_t end, ...);
uc_err uc_hook_add_wrap(uc_engine *handle, uc_hook *h2, uc_hook_type type, void *callback, uintptr_t user, uint64_t begin, uint64_t end) {
return uc_hook_add(handle, h2, type, callback, (void *)user, begin, end);
}
uc_err uc_hook_add_u2(uc_engine *handle, uc_hook *h2, uc_hook_type type, void *callback, uintptr_t user, uint64_t arg1, uint64_t arg2) {
return uc_hook_add(handle, h2, type, callback, (void *)user, arg1, arg2);
uc_err uc_hook_add_insn(uc_engine *handle, uc_hook *h2, uc_hook_type type, void *callback, uintptr_t user, uint64_t begin, uint64_t end, int insn) {
return uc_hook_add(handle, h2, type, callback, (void *)user, begin, end, insn);
}
void hookCode_cgo(uc_engine *handle, uint64_t addr, uint32_t size, uintptr_t user) {

View file

@ -63,23 +63,21 @@ func hookX86Syscall(handle unsafe.Pointer, user unsafe.Pointer) {
hook.Callback.(func(Unicorn))(hook.Uc)
}
func (u *uc) HookAdd(htype int, cb interface{}, extra ...uint64) (Hook, error) {
func (u *uc) HookAdd(htype int, cb interface{}, begin, end uint64, extra ...int) (Hook, error) {
var callback unsafe.Pointer
var iarg1 C.int
var uarg1, uarg2 C.uint64_t
rangeMode := false
var insn C.int
var insnMode bool
switch htype {
case HOOK_BLOCK, HOOK_CODE:
rangeMode = true
callback = C.hookCode_cgo
case HOOK_MEM_READ, HOOK_MEM_WRITE, HOOK_MEM_READ | HOOK_MEM_WRITE:
rangeMode = true
callback = C.hookMemAccess_cgo
case HOOK_INTR:
callback = C.hookInterrupt_cgo
case HOOK_INSN:
iarg1 = C.int(extra[0])
switch iarg1 {
insn = C.int(extra[0])
insnMode = true
switch insn {
case X86_INS_IN:
callback = C.hookX86In_cgo
case X86_INS_OUT:
@ -93,7 +91,6 @@ func (u *uc) HookAdd(htype int, cb interface{}, extra ...uint64) (Hook, error) {
// special case for mask
if htype&(HOOK_MEM_READ_UNMAPPED|HOOK_MEM_WRITE_UNMAPPED|HOOK_MEM_FETCH_UNMAPPED|
HOOK_MEM_READ_PROT|HOOK_MEM_WRITE_PROT|HOOK_MEM_FETCH_PROT) != 0 {
rangeMode = true
callback = C.hookMemInvalid_cgo
} else {
return 0, errors.New("Unknown hook type.")
@ -102,16 +99,10 @@ func (u *uc) HookAdd(htype int, cb interface{}, extra ...uint64) (Hook, error) {
var h2 C.uc_hook
data := &HookData{u, cb}
uptr := uintptr(unsafe.Pointer(data))
if rangeMode {
if len(extra) == 2 {
uarg1 = C.uint64_t(extra[0])
uarg2 = C.uint64_t(extra[1])
} else {
uarg1, uarg2 = 1, 0
}
C.uc_hook_add_u2(u.handle, &h2, C.uc_hook_type(htype), callback, C.uintptr_t(uptr), uarg1, uarg2)
if insnMode {
C.uc_hook_add_insn(u.handle, &h2, C.uc_hook_type(htype), callback, C.uintptr_t(uptr), C.uint64_t(begin), C.uint64_t(end), insn)
} else {
C.uc_hook_add_i1(u.handle, &h2, C.uc_hook_type(htype), callback, C.uintptr_t(uptr), iarg1)
C.uc_hook_add_wrap(u.handle, &h2, C.uc_hook_type(htype), callback, C.uintptr_t(uptr), C.uint64_t(begin), C.uint64_t(end))
}
hookDataMap[uptr] = data
hookToUintptr[Hook(h2)] = uptr

View file

@ -1,5 +1,5 @@
uc_err uc_hook_add_i1(uc_engine *handle, uc_hook *h2, uc_hook_type type, void *callback, uintptr_t user, int arg1);
uc_err uc_hook_add_u2(uc_engine *handle, uc_hook *h2, uc_hook_type type, void *callback, uintptr_t user, uint64_t arg1, uint64_t arg2);
uc_err uc_hook_add_wrap(uc_engine *handle, uc_hook *h2, uc_hook_type type, void *callback, uintptr_t user, uint64_t begin, uint64_t end);
uc_err uc_hook_add_insn(uc_engine *handle, uc_hook *h2, uc_hook_type type, void *callback, uintptr_t user, uint64_t begin, uint64_t end, int insn);
void hookCode_cgo(uc_engine *handle, uint64_t addr, uint32_t size, uintptr_t user);
bool hookMemInvalid_cgo(uc_engine *handle, uc_mem_type type, uint64_t addr, int size, int64_t value, uintptr_t user);
void hookMemAccess_cgo(uc_engine *handle, uc_mem_type type, uint64_t addr, int size, int64_t value, uintptr_t user);

View file

@ -25,22 +25,31 @@ func errReturn(err C.uc_err) error {
return nil
}
type MemRegion struct {
Begin, End uint64
Prot int
}
type Unicorn interface {
MemMap(addr, size uint64) error
MemMapProt(addr, size uint64, prot int) error
MemMapPtr(addr, size uint64, prot int, ptr unsafe.Pointer) error
MemProtect(addr, size uint64, prot int) error
MemUnmap(addr, size uint64) error
MemRegions() ([]*MemRegion, error)
MemRead(addr, size uint64) ([]byte, error)
MemReadInto(dst []byte, addr uint64) error
MemWrite(addr uint64, data []byte) error
RegRead(reg int) (uint64, error)
RegWrite(reg int, value uint64) error
RegReadMmr(reg int) (*X86Mmr, error)
RegWriteMmr(reg int, value *X86Mmr) error
Start(begin, until uint64) error
StartWithOptions(begin, until uint64, options *UcOptions) error
Stop() error
HookAdd(htype int, cb interface{}, extra ...uint64) (Hook, error)
HookAdd(htype int, cb interface{}, begin, end uint64, extra ...int) (Hook, error)
HookDel(hook Hook) error
Query(queryType int) (uint64, error)
Close() error
}
@ -103,6 +112,25 @@ func (u *uc) RegRead(reg int) (uint64, error) {
return uint64(val), errReturn(ucerr)
}
func (u *uc) MemRegions() ([]*MemRegion, error) {
var regions *C.struct_uc_mem_region
var count C.uint32_t
ucerr := C.uc_mem_regions(u.handle, &regions, &count)
if ucerr != C.UC_ERR_OK {
return nil, errReturn(ucerr)
}
ret := make([]*MemRegion, count)
tmp := (*[1 << 24]C.struct_uc_mem_region)(unsafe.Pointer(regions))[:count]
for i, v := range tmp {
ret[i] = &MemRegion{
Begin: uint64(v.begin),
End: uint64(v.end),
Prot: int(v.perms),
}
}
return ret, nil
}
func (u *uc) MemWrite(addr uint64, data []byte) error {
if len(data) == 0 {
return nil
@ -141,3 +169,9 @@ func (u *uc) MemProtect(addr, size uint64, prot int) error {
func (u *uc) MemUnmap(addr, size uint64) error {
return errReturn(C.uc_mem_unmap(u.handle, C.uint64_t(addr), C.size_t(size)))
}
func (u *uc) Query(queryType int) (uint64, error) {
var ret C.size_t
ucerr := C.uc_query(u.handle, C.uc_query_type(queryType), &ret)
return uint64(ret), errReturn(ucerr)
}

View file

@ -1,9 +1,9 @@
package unicorn
// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [unicorn_const.go]
const (
API_MAJOR = 1
API_MAJOR = 0
API_MINOR = 9
API_MINOR = 0
SECOND_SCALE = 1000000
MILISECOND_SCALE = 1000
ARCH_ARM = 1
@ -16,22 +16,26 @@ const (
ARCH_MAX = 8
MODE_LITTLE_ENDIAN = 0
MODE_BIG_ENDIAN = 1073741824
MODE_ARM = 0
MODE_16 = 2
MODE_32 = 4
MODE_64 = 8
MODE_THUMB = 16
MODE_MCLASS = 32
MODE_V8 = 64
MODE_MICRO = 16
MODE_MIPS3 = 32
MODE_MIPS32R6 = 64
MODE_V9 = 16
MODE_QPX = 16
MODE_BIG_ENDIAN = 1073741824
MODE_MIPS32 = 4
MODE_MIPS64 = 8
MODE_16 = 2
MODE_32 = 4
MODE_64 = 8
MODE_PPC32 = 4
MODE_PPC64 = 8
MODE_QPX = 16
MODE_SPARC32 = 4
MODE_SPARC64 = 8
MODE_V9 = 16
ERR_OK = 0
ERR_NOMEM = 1
@ -82,6 +86,9 @@ const (
HOOK_MEM_WRITE_INVALID = 288
HOOK_MEM_FETCH_INVALID = 576
HOOK_MEM_INVALID = 1008
HOOK_MEM_VALID = 7168
QUERY_MODE = 1
QUERY_PAGE_SIZE = 2
PROT_NONE = 0
PROT_READ = 1

View file

@ -37,3 +37,39 @@ func TestDoubleClose(t *testing.T) {
t.Fatal(err)
}
}
func TestMemRegions(t *testing.T) {
mu, err := NewUnicorn(ARCH_X86, MODE_32)
if err != nil {
t.Fatal(err)
}
err = mu.MemMap(0x1000, 0x1000)
if err != nil {
t.Fatal(err)
}
regions, err := mu.MemRegions()
if err != nil {
t.Fatal(err)
}
if len(regions) != 1 {
t.Fatalf("returned wrong number of regions: %d != 1", len(regions))
}
r := regions[0]
if r.Begin != 0x1000 || r.End != 0x1fff || r.Prot != 7 {
t.Fatalf("incorrect region: %#v", r)
}
}
func TestQuery(t *testing.T) {
mu, err := NewUnicorn(ARCH_ARM, MODE_THUMB)
if err != nil {
t.Fatal(err)
}
mode, err := mu.Query(QUERY_MODE)
if err != nil {
t.Fatal(err)
}
if mode != MODE_THUMB {
t.Fatal("query returned invalid mode: %d != %d", mode, MODE_THUMB)
}
}

View file

@ -0,0 +1,38 @@
package unicorn
import (
"unsafe"
)
// #include <unicorn/unicorn.h>
// #include <unicorn/x86.h>
import "C"
type X86Mmr struct {
Selector uint16
Base uint64
Limit uint32
Flags uint32
}
func (u *uc) RegWriteMmr(reg int, value *X86Mmr) error {
var val C.uc_x86_mmr
val.selector = C.uint16_t(value.Selector)
val.base = C.uint64_t(value.Base)
val.limit = C.uint32_t(value.Limit)
val.flags = C.uint32_t(value.Flags)
ucerr := C.uc_reg_write(u.handle, C.int(reg), unsafe.Pointer(&val))
return errReturn(ucerr)
}
func (u *uc) RegReadMmr(reg int) (*X86Mmr, error) {
var val C.uc_x86_mmr
ucerr := C.uc_reg_read(u.handle, C.int(reg), unsafe.Pointer(&val))
ret := &X86Mmr{
Selector: uint16(val.selector),
Base: uint64(val.base),
Limit: uint32(val.limit),
Flags: uint32(val.flags),
}
return ret, errReturn(ucerr)
}

View file

@ -246,7 +246,13 @@ const (
X86_REG_R13W = 239
X86_REG_R14W = 240
X86_REG_R15W = 241
X86_REG_ENDING = 242
X86_REG_IDTR = 242
X86_REG_GDTR = 243
X86_REG_LDTR = 244
X86_REG_TR = 245
X86_REG_FPCW = 246
X86_REG_FPTAG = 247
X86_REG_ENDING = 248
// X86 instructions

View file

@ -96,7 +96,7 @@ func TestX86InOut(t *testing.T) {
default:
return 0
}
}, X86_INS_IN)
}, 1, 0, X86_INS_IN)
mu.HookAdd(HOOK_INSN, func(_ Unicorn, port, size, value uint32) {
outCalled = true
var err error
@ -111,7 +111,7 @@ func TestX86InOut(t *testing.T) {
if err != nil {
t.Fatal(err)
}
}, X86_INS_OUT)
}, 1, 0, X86_INS_OUT)
if err := mu.Start(ADDRESS, ADDRESS+uint64(len(code))); err != nil {
t.Fatal(err)
}
@ -132,7 +132,7 @@ func TestX86Syscall(t *testing.T) {
mu.HookAdd(HOOK_INSN, func(_ Unicorn) {
rax, _ := mu.RegRead(X86_REG_RAX)
mu.RegWrite(X86_REG_RAX, rax+1)
}, X86_INS_SYSCALL)
}, 1, 0, X86_INS_SYSCALL)
mu.RegWrite(X86_REG_RAX, 0x100)
err = mu.Start(ADDRESS, ADDRESS+uint64(len(code)))
if err != nil {
@ -143,3 +143,18 @@ func TestX86Syscall(t *testing.T) {
t.Fatal("Incorrect syscall return value.")
}
}
func TestX86Mmr(t *testing.T) {
mu, err := MakeUc(MODE_64, "")
if err != nil {
t.Fatal(err)
}
err = mu.RegWriteMmr(X86_REG_GDTR, &X86Mmr{Selector: 0, Base: 0x1000, Limit: 0x1fff, Flags: 0})
if err != nil {
t.Fatal(err)
}
mmr, err := mu.RegReadMmr(X86_REG_GDTR)
if mmr.Selector != 0 || mmr.Base != 0x1000 || mmr.Limit != 0x1fff || mmr.Flags != 0 {
t.Fatalf("mmr read failed: %#v", mmr)
}
}

View file

@ -113,7 +113,7 @@ public class Sample_mips {
System.out.print("Emulate MIPS code (little-endian)\n");
// Initialize emulator in MIPS mode
Unicorn u = new Unicorn(Unicorn.UC_ARCH_MIPS, Unicorn.UC_MODE_MIPS32);
Unicorn u = new Unicorn(Unicorn.UC_ARCH_MIPS, Unicorn.UC_MODE_MIPS32 + Unicorn.UC_MODE_LITTLE_ENDIAN);
// map 2MB memory for this emulation
u.mem_map(ADDRESS, 2 * 1024 * 1024, Unicorn.UC_PROT_ALL);

View file

@ -76,7 +76,7 @@ public class Sample_sparc {
System.out.print("Emulate SPARC code\n");
// Initialize emulator in Sparc mode
Unicorn u = new Unicorn(Unicorn.UC_ARCH_SPARC, Unicorn.UC_MODE_32);
Unicorn u = new Unicorn(Unicorn.UC_ARCH_SPARC, Unicorn.UC_MODE_32 + Unicorn.UC_MODE_BIG_ENDIAN);
// map 2MB memory for this emulation
u.mem_map(ADDRESS, 2 * 1024 * 1024, Unicorn.UC_PROT_ALL);

View file

@ -0,0 +1,77 @@
/*
Java bindings for the Unicorn Emulator Engine
Copyright(c) 2016 Chris Eagle
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* Sample code to demonstrate how to register read/write API */
import unicorn.*;
public class Sample_x86_mmr {
static void test_x86_mmr() {
// Initialize emulator in X86-32bit mode
Unicorn uc;
try {
uc = new Unicorn(Unicorn.UC_ARCH_X86, Unicorn.UC_MODE_32);
} catch (UnicornException uex) {
System.out.println("Failed on uc_open() with error returned: " + uex);
return;
}
// map 4k
uc.mem_map(ADDRESS, 0x1000, Unicorn.UC_PROT_ALL);
X86_MMR ldtr1 = new X86_MMR(0x1111111122222222L, 0x33333333, 0x44444444, (short)0x5555);
X86_MMR ldtr2;
X86_MMR gdtr1 = new X86_MMR(0x6666666677777777L, 0x88888888, 0x99999999, (short)0xaaaa);
X86_MMR gdtr2, gdtr3, gdtr4;
int eax;
// initialize machine registers
uc.reg_write(Unicorn.UC_X86_REG_LDTR, ldtr1);
uc.reg_write(Unicorn.UC_X86_REG_GDTR, gdtr1);
uc.reg_write(Unicorn.UC_X86_REG_EAX, new Long(0xdddddddd));
// read the registers back out
eax = (int)((Long)uc.reg_read(Unicorn.UC_X86_REG_EAX)).longValue();
ldtr2 = (X86_MMR)uc.reg_read(Unicorn.UC_X86_REG_LDTR);
gdtr2 = (X86_MMR)uc.reg_read(Unicorn.UC_X86_REG_GDTR);
System.out.printf(">>> EAX = 0x%x\n", eax);
System.out.printf(">>> LDTR.base = 0x%x\n", ldtr2.base);
System.out.printf(">>> LDTR.limit = 0x%x\n", ldtr2.limit);
System.out.printf(">>> LDTR.flags = 0x%x\n", ldtr2.flags);
System.out.printf(">>> LDTR.selector = 0x%x\n\n", ldtr2.selector);
System.out.printf(">>> GDTR.base = 0x%x\n", gdtr2.base);
System.out.printf(">>> GDTR.limit = 0x%x\n", gdtr2.limit);
uc.close();
}
public static void main(String args[])
{
test_x86_mmr();
}
}

View file

@ -0,0 +1,37 @@
/*
Java bindings for the Unicorn Emulator Engine
Copyright(c) 2016 Chris Eagle
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package unicorn;
public class MemRegion {
public long begin;
public long end;
public int perms;
public MemRegion(long begin, long end, int perms) {
this.begin = begin;
this.end = end;
this.perms = perms;
}
}

View file

@ -26,6 +26,8 @@ import java.util.*;
public class Unicorn implements UnicornConst, ArmConst, Arm64Const, M68kConst, SparcConst, MipsConst, X86Const {
private long eng;
private int arch;
private int mode;
private long blockHandle = 0;
private long interruptHandle = 0;
@ -275,6 +277,38 @@ public class Unicorn implements UnicornConst, ArmConst, Arm64Const, M68kConst, S
}
}
/**
* Write to register.
*
* @param regid Register ID that is to be modified.
* @param value Number containing the new register value
*/
private native void reg_write_num(int regid, Number value) throws UnicornException;
/**
* Write to register.
*
* @param regid Register ID that is to be modified.
* @param value X86 specific memory management register containing the new register value
*/
private native void reg_write_mmr(int regid, X86_MMR value) throws UnicornException;
/**
* Read register value.
*
* @param regid Register ID that is to be retrieved.
* @return Number containing the requested register value.
*/
private native Number reg_read_num(int regid) throws UnicornException;
/**
* Read register value.
*
* @param regid Register ID that is to be retrieved.
* @return X86_MMR containing the requested register value.
*/
private native Number reg_read_mmr(int regid) throws UnicornException;
/**
* Native access to uc_open
*
@ -288,10 +322,13 @@ public class Unicorn implements UnicornConst, ArmConst, Arm64Const, M68kConst, S
*
* @param arch Architecture type (UC_ARCH_*)
* @param mode Hardware mode. This is combined of UC_MODE_*
* @see unicorn.UnicornArchs, unicorn.UnicornModes
* @see unicorn.UnicornConst
*
*/
public Unicorn(int arch, int mode) throws UnicornException {
//remember these in case we need arch specific code
this.arch = arch;
this.mode = mode;
eng = open(arch, mode);
unicorns.put(eng, this);
allLists.add(blockList);
@ -327,7 +364,7 @@ public class Unicorn implements UnicornConst, ArmConst, Arm64Const, M68kConst, S
*
* @param arch Architecture type (UC_ARCH_*)
* @return true if this library supports the given arch.
* @see unicorn.UnicornArchs
* @see unicorn.UnicornConst
*/
public native static boolean arch_supported(int arch);
@ -337,12 +374,23 @@ public class Unicorn implements UnicornConst, ArmConst, Arm64Const, M68kConst, S
*/
public native void close() throws UnicornException;
/**
* Query internal status of engine.
*
* @param type query type. See UC_QUERY_*
* @param result save the internal status queried
*
* @return: error code. see UC_ERR_*
* @see unicorn.UnicornConst
*/
public native int query(int type) throws UnicornException;
/**
* Report the last error number when some API function fail.
* Like glibc's errno, uc_errno might not retain its old value once accessed.
*
* @return Error code of uc_err enum type (UC_ERR_*, see above)
* @see unicorn.UnicornErrors
* @see unicorn.UnicornConst
*/
public native int errno();
@ -351,26 +399,67 @@ public class Unicorn implements UnicornConst, ArmConst, Arm64Const, M68kConst, S
*
* @param code Error code (see UC_ERR_* above)
* @return Returns a String that describes the error code
* @see unicorn.UnicornErrors
* @see unicorn.UnicornConst
*/
public native static String strerror(int code);
/**
* Write to register.
*
* @deprecated use reg_write(int regid, Object value) instead
* @param regid Register ID that is to be modified.
* @param value Array containing value that will be written into register @regid
*/
@Deprecated
public native void reg_write(int regid, byte[] value) throws UnicornException;
/**
* Write to register.
*
* @param regid Register ID that is to be modified.
* @param value Object containing the new register value. Long, BigInteger, or
* other custom class used to represent register values
*/
public void reg_write(int regid, Object value) throws UnicornException {
if (value instanceof Number) {
reg_write_num(regid, (Number)value);
}
else if (arch == UC_ARCH_X86 && value instanceof X86_MMR) {
if (regid >= UC_X86_REG_IDTR && regid <= UC_X86_REG_TR) {
reg_write_mmr(regid, (X86_MMR)value);
}
}
else {
throw new ClassCastException("Invalid value type");
}
}
/**
* Read register value.
*
* @deprecated use Object reg_write(int regid) instead
* @param regid Register ID that is to be retrieved.
* @param regsz Size of the register being retrieved.
* @return Byte array containing the requested register value.
*/
@Deprecated
public native byte[] reg_read(int regid, int regsz) throws UnicornException;
/**
* Read register value.
*
* @param regid Register ID that is to be retrieved.
* @param regsz Size of the register being retrieved.
* @return Byte array containing the requested register value.
* @return Object containing the requested register value. Long, BigInteger, or
* other custom class used to represent register values
*/
public native byte[] reg_read(int regid, int regsz) throws UnicornException;
public Object reg_read(int regid) throws UnicornException {
if (arch == UC_ARCH_X86 && regid >= UC_X86_REG_IDTR && regid <= UC_X86_REG_TR) {
return reg_read_mmr(regid);
}
else {
return reg_read_num(regid);
}
}
/**
* Write to memory.
@ -625,6 +714,19 @@ public class Unicorn implements UnicornConst, ArmConst, Arm64Const, M68kConst, S
*/
public native void mem_map(long address, long size, int perms) throws UnicornException;
/**
* Map existing host memory in for emulation.
* This API adds a memory region that can be used by emulation.
*
* @param address Base address of the memory range
* @param size Size of the memory block.
* @param perms Permissions on the memory block. A combination of UC_PROT_READ, UC_PROT_WRITE, UC_PROT_EXEC
* @param ptr Block of host memory backing the newly mapped memory. This block is
* expected to be an equal or larger size than provided, and be mapped with at
* least PROT_READ | PROT_WRITE. If it is not, the resulting behavior is undefined.
*/
public native void mem_map_ptr(long address, long size, int perms, byte[] block) throws UnicornException;
/**
* Unmap a range of memory.
*
@ -642,5 +744,13 @@ public class Unicorn implements UnicornConst, ArmConst, Arm64Const, M68kConst, S
*/
public native void mem_protect(long address, long size, int perms) throws UnicornException;
/**
* Retrieve all memory regions mapped by mem_map() and mem_map_ptr()
* NOTE: memory regions may be split by mem_unmap()
*
* @return list of mapped regions.
*/
public native MemRegion[] mem_regions() throws UnicornException;
}

View file

@ -3,9 +3,9 @@
package unicorn;
public interface UnicornConst {
public static final int UC_API_MAJOR = 1;
public static final int UC_API_MAJOR = 0;
public static final int UC_API_MINOR = 9;
public static final int UC_API_MINOR = 0;
public static final int UC_SECOND_SCALE = 1000000;
public static final int UC_MILISECOND_SCALE = 1000;
public static final int UC_ARCH_ARM = 1;
@ -18,22 +18,26 @@ public interface UnicornConst {
public static final int UC_ARCH_MAX = 8;
public static final int UC_MODE_LITTLE_ENDIAN = 0;
public static final int UC_MODE_BIG_ENDIAN = 1073741824;
public static final int UC_MODE_ARM = 0;
public static final int UC_MODE_16 = 2;
public static final int UC_MODE_32 = 4;
public static final int UC_MODE_64 = 8;
public static final int UC_MODE_THUMB = 16;
public static final int UC_MODE_MCLASS = 32;
public static final int UC_MODE_V8 = 64;
public static final int UC_MODE_MICRO = 16;
public static final int UC_MODE_MIPS3 = 32;
public static final int UC_MODE_MIPS32R6 = 64;
public static final int UC_MODE_V9 = 16;
public static final int UC_MODE_QPX = 16;
public static final int UC_MODE_BIG_ENDIAN = 1073741824;
public static final int UC_MODE_MIPS32 = 4;
public static final int UC_MODE_MIPS64 = 8;
public static final int UC_MODE_16 = 2;
public static final int UC_MODE_32 = 4;
public static final int UC_MODE_64 = 8;
public static final int UC_MODE_PPC32 = 4;
public static final int UC_MODE_PPC64 = 8;
public static final int UC_MODE_QPX = 16;
public static final int UC_MODE_SPARC32 = 4;
public static final int UC_MODE_SPARC64 = 8;
public static final int UC_MODE_V9 = 16;
public static final int UC_ERR_OK = 0;
public static final int UC_ERR_NOMEM = 1;
@ -84,6 +88,9 @@ public interface UnicornConst {
public static final int UC_HOOK_MEM_WRITE_INVALID = 288;
public static final int UC_HOOK_MEM_FETCH_INVALID = 576;
public static final int UC_HOOK_MEM_INVALID = 1008;
public static final int UC_HOOK_MEM_VALID = 7168;
public static final int UC_QUERY_MODE = 1;
public static final int UC_QUERY_PAGE_SIZE = 2;
public static final int UC_PROT_NONE = 0;
public static final int UC_PROT_READ = 1;

View file

@ -1,38 +0,0 @@
/*
Java bindings for the Unicorn Emulator Engine
Copyright(c) 2015 Chris Eagle
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package unicorn;
public interface UnicornErrors {
public static final int UC_ERR_OK = 0; // No error: everything was fine
public static final int UC_ERR_OOM = 1; // Out-Of-Memory error: uc_open(), uc_emulate()
public static final int UC_ERR_ARCH = 2; // Unsupported architecture: uc_open()
public static final int UC_ERR_HANDLE = 3; // Invalid handle
public static final int UC_ERR_UCH = 4; // Invalid handle (uch)
public static final int UC_ERR_MODE = 5; // Invalid/unsupported mode: uc_open()
public static final int UC_ERR_VERSION = 6; // Unsupported version (bindings)
public static final int UC_ERR_MEM_READ = 7; // Quit emulation due to invalid memory READ: uc_emu_start()
public static final int UC_ERR_MEM_WRITE = 8; // Quit emulation due to invalid memory WRITE: uc_emu_start()
public static final int UC_ERR_HOOK = 9; // Invalid hook type: uc_hook_add()
public static final int UC_ERR_INSN_INVALID = 10; // Quit emulation due to invalid instruction: uc_emu_start()
public static final int UC_ERR_MAP = 11; // Invalid memory mapping: uc_mem_map()
}

View file

@ -248,7 +248,13 @@ public interface X86Const {
public static final int UC_X86_REG_R13W = 239;
public static final int UC_X86_REG_R14W = 240;
public static final int UC_X86_REG_R15W = 241;
public static final int UC_X86_REG_ENDING = 242;
public static final int UC_X86_REG_IDTR = 242;
public static final int UC_X86_REG_GDTR = 243;
public static final int UC_X86_REG_LDTR = 244;
public static final int UC_X86_REG_TR = 245;
public static final int UC_X86_REG_FPCW = 246;
public static final int UC_X86_REG_FPTAG = 247;
public static final int UC_X86_REG_ENDING = 248;
// X86 instructions

View file

@ -0,0 +1,46 @@
/*
Java bindings for the Unicorn Emulator Engine
Copyright(c) 2016 Chris Eagle
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package unicorn;
public class X86_MMR {
public long base;
public int limit;
public int flags;
public short selector;
public X86_MMR(long base, int limit, int flags, short selector) {
this.base = base;
this.limit = limit;
this.flags = flags;
this.selector = selector;
}
public X86_MMR(long base, int limit) {
this.base = base;
this.limit = limit;
selector = 0;
flags = 0;
}
}

View file

@ -24,8 +24,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <stdlib.h>
#include <string.h>
#include <unicorn/unicorn.h>
#include <unicorn/x86.h>
#include "unicorn_Unicorn.h"
//cache jmethodID values as we look them up
@ -201,6 +201,117 @@ static uc_engine *getEngine(JNIEnv *env, jobject self) {
return (uc_engine *)(*env)->GetLongField(env, self, fid);
}
/*
* Class: unicorn_Unicorn
* Method: reg_write_num
* Signature: (ILjava/lang/Number;)V
*/
JNIEXPORT void JNICALL Java_unicorn_Unicorn_reg_1write_1num
(JNIEnv *env, jobject self, jint regid, jobject value) {
uc_engine *eng = getEngine(env, self);
jclass clz = (*env)->FindClass(env, "java/lang/Number");
if ((*env)->ExceptionCheck(env)) {
return;
}
jmethodID longValue = (*env)->GetMethodID(env, clz, "longValue", "()J");
jlong longVal = (*env)->CallLongMethod(env, value, longValue);
uc_err err = uc_reg_write(eng, regid, &longVal);
if (err != UC_ERR_OK) {
throwException(env, err);
}
}
/*
* Class: unicorn_Unicorn
* Method: reg_write_mmr
* Signature: (ILunicorn/X86_MMR;)V
*/
JNIEXPORT void JNICALL Java_unicorn_Unicorn_reg_1write_1mmr
(JNIEnv *env, jobject self, jint regid, jobject value) {
uc_engine *eng = getEngine(env, self);
uc_x86_mmr mmr;
jclass clz = (*env)->FindClass(env, "unicorn/X86_MMR");
if ((*env)->ExceptionCheck(env)) {
return;
}
jfieldID fid = (*env)->GetFieldID(env, clz, "base", "J");
mmr.base = (uint64_t)(*env)->GetLongField(env, value, fid);
fid = (*env)->GetFieldID(env, clz, "limit", "I");
mmr.limit = (uint32_t)(*env)->GetLongField(env, value, fid);
fid = (*env)->GetFieldID(env, clz, "flags", "I");
mmr.flags = (uint32_t)(*env)->GetLongField(env, value, fid);
fid = (*env)->GetFieldID(env, clz, "selector", "S");
mmr.selector = (uint16_t)(*env)->GetLongField(env, value, fid);
uc_err err = uc_reg_write(eng, regid, &mmr);
if (err != UC_ERR_OK) {
throwException(env, err);
}
}
/*
* Class: unicorn_Unicorn
* Method: reg_read_num
* Signature: (I)Ljava/lang/Number;
*/
JNIEXPORT jobject JNICALL Java_unicorn_Unicorn_reg_1read_1num
(JNIEnv *env, jobject self, jint regid) {
uc_engine *eng = getEngine(env, self);
jclass clz = (*env)->FindClass(env, "java/lang/Long");
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
jlong longVal;
uc_err err = uc_reg_read(eng, regid, &longVal);
if (err != UC_ERR_OK) {
throwException(env, err);
}
jmethodID cons = (*env)->GetMethodID(env, clz, "<init>", "(J)V");
jobject result = (*env)->NewObject(env, clz, cons, longVal);
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
return result;
}
/*
* Class: unicorn_Unicorn
* Method: reg_read_mmr
* Signature: (I)Ljava/lang/Number;
*/
JNIEXPORT jobject JNICALL Java_unicorn_Unicorn_reg_1read_1mmr
(JNIEnv *env, jobject self, jint regid) {
uc_engine *eng = getEngine(env, self);
jclass clz = (*env)->FindClass(env, "unicorn/X86_MMR");
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
uc_x86_mmr mmr;
uc_err err = uc_reg_read(eng, regid, &mmr);
if (err != UC_ERR_OK) {
throwException(env, err);
}
jmethodID cons = (*env)->GetMethodID(env, clz, "<init>", "(JIIS)V");
jobject result = (*env)->NewObject(env, clz, cons, mmr.base, mmr.limit, mmr.flags, mmr.selector);
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
return result;
}
/*
* Class: unicorn_Unicorn
* Method: open
@ -244,7 +355,28 @@ JNIEXPORT jboolean JNICALL Java_unicorn_Unicorn_arch_1supported
JNIEXPORT void JNICALL Java_unicorn_Unicorn_close
(JNIEnv *env, jobject self) {
uc_engine *eng = getEngine(env, self);
uc_close(eng);
uc_err err = uc_close(eng);
if (err != UC_ERR_OK) {
throwException(env, err);
}
//We also need to ReleaseByteArrayElements for any regions that
//were mapped with uc_mem_map_ptr
}
/*
* Class: unicorn_Unicorn
* Method: query
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_unicorn_Unicorn_query
(JNIEnv *env, jobject self, jint type) {
uc_engine *eng = getEngine(env, self);
size_t result;
uc_err err = uc_query(eng, type, &result);
if (err != UC_ERR_OK) {
throwException(env, err);
}
return (jint)result;
}
/*
@ -508,6 +640,24 @@ JNIEXPORT void JNICALL Java_unicorn_Unicorn_mem_1map
}
}
/*
* Class: unicorn_Unicorn
* Method: mem_map_ptr
* Signature: (JJI[B)V
*/
JNIEXPORT void JNICALL Java_unicorn_Unicorn_mem_1map_1ptr
(JNIEnv *env, jobject self, jlong address, jlong size, jint perms, jbyteArray block) {
uc_engine *eng = getEngine(env, self);
jbyte *array = (*env)->GetByteArrayElements(env, block, NULL);
uc_err err = uc_mem_map_ptr(eng, (uint64_t)address, (size_t)size, (uint32_t)perms, (void*)array);
if (err != UC_ERR_OK) {
throwException(env, err);
}
//Need to track address/block/array so that we can ReleaseByteArrayElements when the
//block gets unmapped or when uc_close gets called
//(*env)->ReleaseByteArrayElements(env, block, array, JNI_ABORT);
}
/*
* Class: unicorn_Unicorn
* Method: mem_unmap
@ -521,6 +671,9 @@ JNIEXPORT void JNICALL Java_unicorn_Unicorn_mem_1unmap
if (err != UC_ERR_OK) {
throwException(env, err);
}
//If a region was mapped using uc_mem_map_ptr, we also need to
//ReleaseByteArrayElements for that region
}
/*
@ -537,3 +690,35 @@ JNIEXPORT void JNICALL Java_unicorn_Unicorn_mem_1protect
throwException(env, err);
}
}
/*
* Class: unicorn_Unicorn
* Method: mem_regions
* Signature: ()[Lunicorn/MemRegion;
*/
JNIEXPORT jobjectArray JNICALL Java_unicorn_Unicorn_mem_1regions
(JNIEnv *env, jobject self) {
uc_engine *eng = getEngine(env, self);
uc_mem_region *regions = NULL;
uint32_t count = 0;
uint32_t i;
uc_err err = uc_mem_regions(eng, &regions, &count);
if (err != UC_ERR_OK) {
throwException(env, err);
}
jclass clz = (*env)->FindClass(env, "unicorn/MemRegion");
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
jobjectArray result = (*env)->NewObjectArray(env, (jsize)count, clz, NULL);
jmethodID cons = (*env)->GetMethodID(env, clz, "<init>", "(JJI)V");
for (i = 0; i < count; i++) {
jobject mr = (*env)->NewObject(env, clz, cons, regions[i].begin, regions[i].end, regions[i].perms);
(*env)->SetObjectArrayElement(env, result, (jsize)i, mr);
}
free(regions);
return result;
}

View file

@ -75,7 +75,7 @@ int main(int argc, char **argv, char **envp)
#endif
// Initialize emulator in MIPS 32bit little endian mode
err = uc_open(UC_ARCH_MIPS, UC_MODE_MIPS32, &uc);
err = uc_open(UC_ARCH_MIPS, UC_MODE_MIPS32 | UC_MODE_LITTLE_ENDIAN, &uc);
if (err)
{
printf("Failed on uc_open() with error returned: %u\n", err);

View file

@ -3,6 +3,7 @@ uc_version
uc_arch_supported
uc_open
uc_close
uc_query
uc_errno
uc_strerror
uc_reg_write
@ -14,5 +15,7 @@ uc_emu_stop
uc_hook_add
uc_hook_del
uc_mem_map
uc_mem_map_ptr
uc_mem_unmap
uc_mem_protect
uc_mem_regions

View file

@ -1,6 +1,6 @@
//
// Dynamic loader for unicorn shared library in windows and linux.
// This was made for v0.9 of unicorn.
// This was made for v1.0 of unicorn.
// Newer versions of unicorn may require changes to these files.
//
// Windows Notes:
@ -62,6 +62,7 @@ typedef unsigned int (*uc_version_t)(unsigned int *major, unsigned int *minor);
typedef bool (*uc_arch_supported_t)(uc_arch arch);
typedef uc_err (*uc_open_t)(uc_arch arch, uc_mode mode, uc_engine **uc);
typedef uc_err (*uc_close_t)(uc_engine *uc);
typedef uc_err (*uc_query_t)(uc_engine *uc, uc_query_type type, size_t *result);
typedef uc_err (*uc_errno_t)(uc_engine *uc);
typedef const char* (*uc_strerror_t)(uc_err code);
typedef uc_err (*uc_reg_write_t)(uc_engine *uc, int regid, const void *value);
@ -70,17 +71,20 @@ typedef uc_err (*uc_mem_write_t)(uc_engine *uc, uint64_t address, const void *by
typedef uc_err (*uc_mem_read_t)(uc_engine *uc, uint64_t address, void *bytes, size_t size);
typedef uc_err (*uc_emu_start_t)(uc_engine *uc, uint64_t begin, uint64_t until, uint64_t timeout, size_t count);
typedef uc_err (*uc_emu_stop_t)(uc_engine *uc);
typedef uc_err (*uc_hook_add_t)(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, ...);
typedef uc_err (*uc_hook_add_t)(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, uint64_t begin, uint64_t end, ...);
typedef uc_err (*uc_hook_del_t)(uc_engine *uc, uc_hook hh);
typedef uc_err (*uc_mem_map_t)(uc_engine *uc, uint64_t address, size_t size, uint32_t perms);
typedef uc_err (*uc_mem_map_ptr_t)(uc_engine *uc, uint64_t address, size_t size, uint32_t perms, void *ptr);
typedef uc_err (*uc_mem_unmap_t)(uc_engine *uc, uint64_t address, size_t size);
typedef uc_err (*uc_mem_protect_t)(uc_engine *uc, uint64_t address, size_t size, uint32_t perms);
typedef uc_err (*uc_mem_regions_t)(uc_engine *uc, uc_mem_region **regions, uint32_t *count);
static uc_version_t gp_uc_version = NULL;
static uc_arch_supported_t gp_uc_arch_supported = NULL;
static uc_open_t gp_uc_open = NULL;
static uc_close_t gp_uc_close = NULL;
static uc_query_t gp_uc_query = NULL;
static uc_errno_t gp_uc_errno = NULL;
static uc_strerror_t gp_uc_strerror = NULL;
static uc_reg_write_t gp_uc_reg_write = NULL;
@ -92,8 +96,10 @@ static uc_emu_stop_t gp_uc_emu_stop = NULL;
static uc_hook_add_t gp_uc_hook_add = NULL;
static uc_hook_del_t gp_uc_hook_del = NULL;
static uc_mem_map_t gp_uc_mem_map = NULL;
static uc_mem_map_ptr_t gp_uc_mem_map_ptr = NULL;
static uc_mem_unmap_t gp_uc_mem_unmap = NULL;
static uc_mem_protect_t gp_uc_mem_protect = NULL;
static uc_mem_regions_t gp_uc_mem_regions = NULL;
bool uc_dyn_load(const char* path, int flags)
@ -118,6 +124,7 @@ bool uc_dyn_load(const char* path, int flags)
gp_uc_arch_supported = (uc_arch_supported_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_arch_supported");
gp_uc_open = (uc_open_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_open");
gp_uc_close = (uc_close_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_close");
gp_uc_query = (uc_query_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_query");
gp_uc_errno = (uc_errno_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_errno");
gp_uc_strerror = (uc_strerror_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_strerror");
gp_uc_reg_write = (uc_reg_write_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_reg_write");
@ -129,8 +136,10 @@ bool uc_dyn_load(const char* path, int flags)
gp_uc_hook_add = (uc_hook_add_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_hook_add");
gp_uc_hook_del = (uc_hook_del_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_hook_del");
gp_uc_mem_map = (uc_mem_map_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_mem_map");
gp_uc_mem_map_ptr = (uc_mem_map_ptr_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_mem_map_ptr");
gp_uc_mem_unmap = (uc_mem_unmap_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_mem_unmap");
gp_uc_mem_protect = (uc_mem_protect_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_mem_protect");
gp_uc_mem_regions = (uc_mem_regions_t)DYNLOAD_GETFUNC(g_dyn_handle, "uc_mem_regions");
return true;
}
@ -146,6 +155,7 @@ bool uc_dyn_free(void)
gp_uc_arch_supported = NULL;
gp_uc_open = NULL;
gp_uc_close = NULL;
gp_uc_query = NULL;
gp_uc_errno = NULL;
gp_uc_strerror = NULL;
gp_uc_reg_write = NULL;
@ -157,8 +167,10 @@ bool uc_dyn_free(void)
gp_uc_hook_add = NULL;
gp_uc_hook_del = NULL;
gp_uc_mem_map = NULL;
gp_uc_mem_map_ptr = NULL;
gp_uc_mem_unmap = NULL;
gp_uc_mem_protect = NULL;
gp_uc_mem_regions = NULL;
return true;
}
@ -183,6 +195,11 @@ uc_err uc_close(uc_engine *uc)
return gp_uc_close(uc);
}
uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result)
{
return gp_uc_query(uc, type, result);
}
uc_err uc_errno(uc_engine *uc)
{
return gp_uc_errno(uc);
@ -223,43 +240,40 @@ uc_err uc_emu_stop(uc_engine *uc)
return gp_uc_emu_stop(uc);
}
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;
uc_err ret = UC_ERR_OK;
int id;
uint64_t begin, end;
va_start(valist, user_data);
switch(type) {
// note this default case will capture any combinations of
// UC_HOOK_MEM_*_PROT and UC_HOOK_MEM_*_UNMAPPED
// as well as any combination of
// UC_HOOK_MEM_READ, UC_HOOK_MEM_WRITE and UC_HOOK_MEM_FETCH
default:
case UC_HOOK_INTR:
case UC_HOOK_CODE:
case UC_HOOK_BLOCK:
// all combinations of UC_HOOK_MEM_*_PROT and UC_HOOK_MEM_*_UNMAPPED are caught by 'default'
case UC_HOOK_MEM_READ_UNMAPPED:
case UC_HOOK_MEM_WRITE_UNMAPPED:
case UC_HOOK_MEM_FETCH_UNMAPPED:
case UC_HOOK_MEM_READ_PROT:
case UC_HOOK_MEM_WRITE_PROT:
case UC_HOOK_MEM_FETCH_PROT:
// all combinations of read/write/fetch are caught by 'default'
case UC_HOOK_MEM_READ:
case UC_HOOK_MEM_WRITE:
case UC_HOOK_MEM_FETCH:
// 0 extra args
ret = gp_uc_hook_add(uc, hh, type, callback, user_data);
ret = gp_uc_hook_add(uc, hh, type, callback, user_data, begin, end);
break;
case UC_HOOK_INSN:
// 1 extra arg
id = va_arg(valist, int);
ret = gp_uc_hook_add(uc, hh, type, callback, user_data, id);
break;
case UC_HOOK_CODE:
case UC_HOOK_BLOCK:
case UC_HOOK_MEM_READ:
case UC_HOOK_MEM_WRITE:
case UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE:
// 2 extra args
begin = va_arg(valist, uint64_t);
end = va_arg(valist, uint64_t);
ret = gp_uc_hook_add(uc, hh, type, callback, user_data, begin, end);
ret = gp_uc_hook_add(uc, hh, type, callback, user_data, begin, end, id);
break;
}
@ -277,6 +291,11 @@ uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms)
return gp_uc_mem_map(uc, address, size, perms);
}
uc_err uc_mem_map_ptr(uc_engine *uc, uint64_t address, size_t size, uint32_t perms, void *ptr)
{
return gp_uc_mem_map_ptr(uc, address, size, perms, ptr);
}
uc_err uc_mem_unmap(uc_engine *uc, uint64_t address, size_t size)
{
return gp_uc_mem_unmap(uc, address, size);
@ -287,4 +306,9 @@ uc_err uc_mem_protect(uc_engine *uc, uint64_t address, size_t size, uint32_t per
return gp_uc_mem_protect(uc, address, size, perms);
}
uc_err uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count)
{
return gp_uc_mem_regions(uc, regions, count);
}
#endif // DYNLOAD

View file

@ -1,6 +1,6 @@
//
// Dynamic loader for unicorn shared library in windows and linux.
// This was made for v0.9 of unicorn.
// This was made for v1.0 of unicorn.
// Newer versions of unicorn may require changes to these files.
//
// Windows Notes:

View file

@ -7,7 +7,6 @@ from unicorn import *
from unicorn.x86_const import *
import struct
import uuid
import random
SIZE_REG = 4
SOCKETCALL_MAX_ARGS = 3
@ -51,10 +50,11 @@ X86_REVERSE_TCP_2 = b"\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66\xb3\x01\x51\x6a\x
# memory address where emulation starts
ADDRESS = 0x1000000
# supported classes
class IdGenerator:
def __init__(self):
self.__next_id = 3 # exclude sdtin, stdout, stderr
self.__next_id = 3 # exclude sdtin, stdout, stderr
def next(self):
next_id = self.__next_id
@ -63,6 +63,7 @@ class IdGenerator:
return next_id
class LogChain:
def __init__(self):
self.__chains = {}
@ -72,11 +73,11 @@ class LogChain:
self.__chains = {}
self.__linking_fds = {}
def create_chain(self, id):
if not self.__chains.has_key(id):
self.__chains[id] = []
def create_chain(self, my_id):
if not my_id in self.__chains:
self.__chains[my_id] = []
else:
print("LogChain: id %d existed" % id)
print("LogChain: id %d existed" % my_id)
def add_log(self, id, msg):
fd = self.get_original_fd(id)
@ -87,20 +88,20 @@ class LogChain:
print("LogChain: id %d doesn't exist" % id)
def link_fd(self, from_fd, to_fd):
if not self.__linking_fds.has_key(to_fd):
if not to_fd in self.__linking_fds:
self.__linking_fds[to_fd] = []
self.__linking_fds[to_fd].append(from_fd)
def get_original_fd(self, fd):
if self.__chains.has_key(fd):
if fd in self.__chains:
return fd
for orig_fd, links in self.__linking_fds.iteritems():
for orig_fd, links in self.__linking_fds.items():
if fd in links:
return orig_fd
return None
return None
def print_report(self):
print("""
@ -108,10 +109,11 @@ class LogChain:
| START REPORT |
----------------
""")
for id, logs in self.__chains.iteritems():
print("---- START FD(%d) ----" % id)
for my_id, logs in self.__chains.items():
print("---- START FD(%d) ----" % my_id)
print("\n".join(logs))
print("---- END FD(%d) ----" % id)
print("---- END FD(%d) ----" % my_id)
print("""
--------------
@ -119,10 +121,9 @@ class LogChain:
--------------
""")
# end supported classes
id_gen = IdGenerator()
fd_chains = LogChain()
# utilities
def bin_to_ipv4(ip):
@ -132,6 +133,7 @@ def bin_to_ipv4(ip):
(ip & 0xff00) >> 8,
(ip & 0xff))
def read_string(uc, addr):
ret = ""
@ -140,36 +142,43 @@ def read_string(uc, addr):
while c != 0x0:
ret += chr(c)
c = uc.mem_read(addr+read_bytes, 1)[0]
c = uc.mem_read(addr + read_bytes, 1)[0]
read_bytes += 1
return ret
def parse_sock_address(sock_addr):
sin_family, = struct.unpack("<h", sock_addr[:2])
if sin_family == 2: # AF_INET
port, host = struct.unpack(">HI", sock_addr[2:8])
if sin_family == 2: # AF_INET
port, host = struct.unpack(">HI", sock_addr[2:8])
return "%s:%d" % (bin_to_ipv4(host), port)
elif sin_family == 6: # AF_INET6
elif sin_family == 6: # AF_INET6
return ""
def print_sockcall(msg):
print(">>> SOCKCALL %s" % msg)
# end utilities
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# read this instruction code from memory
tmp = uc.mem_read(address, size)
print(">>> Instruction code at [0x%x] =" %(address), end="")
print(">>> Instruction code at [0x%x] =" % (address), end="")
for i in tmp:
print(" %x" %i, end="")
print(" %x" % i, end="")
print("")
# callback for tracing Linux interrupt
def hook_intr(uc, intno, user_data):
global id_gen
# only handle Linux syscall
if intno != 0x80:
return
@ -182,17 +191,17 @@ def hook_intr(uc, intno, user_data):
# print(">>> INTERRUPT %d" % eax)
if eax == 1: # sys_exit
if eax == 1: # sys_exit
print(">>> SYS_EXIT")
uc.emu_stop()
elif eax == 3: # sys_read
elif eax == 3: # sys_read
fd = ebx
buf = ecx
count = edx
dummy_content = str(uuid.uuid1())[:32]
if len(dummy_content) > count:
dummy_content = dummy_content[:count]
dummy_content = dummy_content[:count]
uc.mem_write(buf, dummy_content)
@ -200,7 +209,7 @@ def hook_intr(uc, intno, user_data):
fd_chains.add_log(fd, msg)
print(">>> %s" % msg)
elif eax == 4: # sys_write
elif eax == 4: # sys_write
fd = ebx
buf = ecx
count = edx
@ -211,13 +220,13 @@ def hook_intr(uc, intno, user_data):
print(">>> %s" % msg)
fd_chains.add_log(fd, msg)
elif eax == 5: # sys_open
elif eax == 5: # sys_open
filename_addr = ebx
flags = ecx
mode = edx
filename = read_string(uc, filename_addr)
dummy_fd = id_gen.next()
dummy_fd = id_gen.next()
uc.reg_write(UC_X86_REG_EAX, dummy_fd)
msg = "open file (filename=%s flags=%d mode=%d) with fd(%d)" % (filename, flags, mode, dummy_fd)
@ -225,42 +234,42 @@ def hook_intr(uc, intno, user_data):
fd_chains.create_chain(dummy_fd)
fd_chains.add_log(dummy_fd, msg)
print(">>> %s" % msg)
elif eax == 11: # sys_execv
elif eax == 11: # sys_execv
# print(">>> ebx=0x%x, ecx=0x%x, edx=0x%x" % (ebx, ecx, edx))
filename = read_string(uc, ebx)
print(">>> SYS_EXECV filename=%s" % filename)
elif eax == 63: # sys_dup2
elif eax == 63: # sys_dup2
fd_chains.link_fd(ecx, ebx)
print(">>> SYS_DUP2 oldfd=%d newfd=%d" % (ebx, ecx))
elif eax == 102: # sys_socketcall
elif eax == 102: # sys_socketcall
# ref: http://www.skyfree.org/linux/kernel_network/socket.html
call = uc.reg_read(UC_X86_REG_EBX)
args = uc.reg_read(UC_X86_REG_ECX)
SOCKETCALL_NUM_ARGS = {
1: 3, # sys_socket
2: 3, # sys_bind
3: 3, # sys_connect
4: 2, # sys_listen
5: 3, # sys_accept
9: 4, # sys_send
1: 3, # sys_socket
2: 3, # sys_bind
3: 3, # sys_connect
4: 2, # sys_listen
5: 3, # sys_accept
9: 4, # sys_send
11: 4, # sys_receive
13: 2 # sys_shutdown
13: 2 # sys_shutdown
}
buf = uc.mem_read(args, SOCKETCALL_NUM_ARGS[call]*SIZE_REG)
args = struct.unpack("<" + "I"*SOCKETCALL_NUM_ARGS[call], buf)
buf = uc.mem_read(args, SOCKETCALL_NUM_ARGS[call] * SIZE_REG)
args = struct.unpack("<" + "I" * SOCKETCALL_NUM_ARGS[call], buf)
# int sys_socketcall(int call, unsigned long *args)
if call == 1: # sys_socket
if call == 1: # sys_socket
# err = sys_socket(a0,a1,a[2])
# int sys_socket(int family, int type, int protocol)
family = args[0]
sock_type = args[1]
protocol = args[2]
dummy_fd = id_gen.next()
dummy_fd = id_gen.next()
uc.reg_write(UC_X86_REG_EAX, dummy_fd)
if family == 2: # AF_INET
@ -269,10 +278,10 @@ def hook_intr(uc, intno, user_data):
fd_chains.create_chain(dummy_fd)
fd_chains.add_log(dummy_fd, msg)
print_sockcall(msg)
elif family == 3: # AF_INET6
elif family == 3: # AF_INET6
pass
elif call == 2: # sys_bind
elif call == 2: # sys_bind
fd = args[0]
umyaddr = args[1]
addrlen = args[2]
@ -283,19 +292,19 @@ def hook_intr(uc, intno, user_data):
fd_chains.add_log(fd, msg)
print_sockcall(msg)
elif call == 3: # sys_connect
elif call == 3: # sys_connect
# err = sys_connect(a0, (struct sockaddr *)a1, a[2])
# int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen)
fd = args[0]
uservaddr = args[1]
addrlen = args[2]
sock_addr = uc.mem_read(uservaddr, addrlen)
sock_addr = uc.mem_read(uservaddr, addrlen)
msg = "fd(%d) connect to %s" % (fd, parse_sock_address(sock_addr))
fd_chains.add_log(fd, msg)
print_sockcall(msg)
elif call == 4: # sys_listen
elif call == 4: # sys_listen
fd = args[0]
backlog = args[1]
@ -303,7 +312,7 @@ def hook_intr(uc, intno, user_data):
fd_chains.add_log(fd, msg)
print_sockcall(msg)
elif call == 5: # sys_accept
elif call == 5: # sys_accept
fd = args[0]
upeer_sockaddr = args[1]
upeer_addrlen = args[2]
@ -321,7 +330,7 @@ def hook_intr(uc, intno, user_data):
fd_chains.add_log(fd, msg)
print_sockcall(msg)
elif call == 9: # sys_send
elif call == 9: # sys_send
fd = args[0]
buff = args[1]
length = args[2]
@ -332,7 +341,7 @@ def hook_intr(uc, intno, user_data):
fd_chains.add_log(fd, msg)
print_sockcall(msg)
elif call == 11: # sys_receive
elif call == 11: # sys_receive
fd = args[0]
ubuf = args[1]
size = args[2]
@ -342,7 +351,7 @@ def hook_intr(uc, intno, user_data):
fd_chains.add_log(fd, msg)
print_sockcall(msg)
elif call == 13: # sys_shutdown
elif call == 13: # sys_shutdown
fd = args[0]
how = args[1]
@ -350,8 +359,11 @@ def hook_intr(uc, intno, user_data):
fd_chains.add_log(fd, msg)
print_sockcall(msg)
# Test X86 32 bit
def test_i386(code):
global fd_chains
fd_chains.clean()
print("Emulate i386 code")
try:
@ -366,7 +378,7 @@ def test_i386(code):
# initialize stack
mu.reg_write(UC_X86_REG_ESP, ADDRESS + 0x200000)
# tracing all instructions with customized callback
# mu.hook_add(UC_HOOK_CODE, hook_code)
@ -384,9 +396,13 @@ def test_i386(code):
fd_chains.print_report()
# Globals
fd_chains = LogChain()
id_gen = IdGenerator()
if __name__ == '__main__':
test_i386(X86_SEND_ETCPASSWD)
test_i386(X86_SEND_ETCPASSWD)
test_i386(X86_BIND_TCP)
test_i386(X86_REVERSE_TCP)
test_i386(X86_REVERSE_TCP_2)

View file

@ -28,7 +28,7 @@ def test_sparc():
print("Emulate SPARC code")
try:
# Initialize emulator in SPARC EB mode
mu = Uc(UC_ARCH_SPARC, UC_MODE_32)
mu = Uc(UC_ARCH_SPARC, UC_MODE_SPARC32|UC_MODE_BIG_ENDIAN)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)

View file

@ -291,8 +291,8 @@ def test_i386_inout():
mu.hook_add(UC_HOOK_CODE, hook_code)
# handle IN & OUT instruction
mu.hook_add(UC_HOOK_INSN, hook_in, None, UC_X86_INS_IN)
mu.hook_add(UC_HOOK_INSN, hook_out, None, UC_X86_INS_OUT)
mu.hook_add(UC_HOOK_INSN, hook_in, None, 1, 0, UC_X86_INS_IN)
mu.hook_add(UC_HOOK_INSN, hook_out, None, 1, 0, UC_X86_INS_OUT)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_INOUT))
@ -417,7 +417,7 @@ def test_x86_64_syscall():
print('ERROR: was not expecting rax=%d in syscall' % rax)
# hook interrupts for syscall
mu.hook_add(UC_HOOK_INSN, hook_syscall, None, UC_X86_INS_SYSCALL)
mu.hook_add(UC_HOOK_INSN, hook_syscall, None, 1, 0, UC_X86_INS_SYSCALL)
# syscall handler is expecting rax=0x100
mu.reg_write(UC_X86_REG_RAX, 0x100)

View file

@ -24,7 +24,7 @@ PKG_NAME = 'unicorn'
if os.path.exists(PATH_LIB64) and os.path.exists(PATH_LIB32):
PKG_NAME = 'unicorn-windows'
VERSION = '0.9'
VERSION = '1.0'
SYSTEM = sys.platform
# virtualenv breaks import, but get_python_lib() will work.
@ -63,7 +63,7 @@ def copy_sources():
src.extend(glob.glob("../../Makefile"))
src.extend(glob.glob("../../LICENSE*"))
src.extend(glob.glob("../../README"))
src.extend(glob.glob("../../README.md"))
src.extend(glob.glob("../../*.TXT"))
src.extend(glob.glob("../../RELEASE_NOTES"))
src.extend(glob.glob("../../make.sh"))

View file

@ -97,7 +97,7 @@ def test_i386(mode, code):
mu.hook_add(UC_HOOK_INTR, hook_intr)
# handle SYSCALL
mu.hook_add(UC_HOOK_INSN, hook_syscall, None, UC_X86_INS_SYSCALL)
mu.hook_add(UC_HOOK_INSN, hook_syscall, None, 1, 0, UC_X86_INS_SYSCALL)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(code))

View file

@ -1,4 +1,4 @@
# Unicorn Python bindings, by Nguyen Anh Quynnh <aquynh@gmail.com>
from . import arm_const, arm64_const, mips_const, sparc_const, m68k_const, x86_const
from .unicorn_const import *
from .unicorn import Uc, uc_version, uc_arch_supported, version_bind, debug, UcError
from .unicorn import Uc, uc_version, uc_arch_supported, version_bind, debug, UcError, __version__

View file

@ -81,6 +81,8 @@ if _found == False:
raise ImportError("ERROR: fail to load the dynamic library.")
__version__ = "%s.%s" %(UC_API_MAJOR, UC_API_MINOR)
# setup all the function prototype
def _setup_prototype(lib, fname, restype, *argtypes):
getattr(lib, fname).restype = restype
@ -107,6 +109,7 @@ _setup_prototype(_uc, "uc_mem_map", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_
_setup_prototype(_uc, "uc_mem_map_ptr", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32, ctypes.c_void_p)
_setup_prototype(_uc, "uc_mem_unmap", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t)
_setup_prototype(_uc, "uc_mem_protect", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32)
_setup_prototype(_uc, "uc_query", ucerr, uc_engine, ctypes.c_uint32, ctypes.POINTER(ctypes.c_size_t))
# uc_hook_add is special due to variable number of arguments
_uc.uc_hook_add = getattr(_uc, "uc_hook_add")
@ -153,6 +156,24 @@ def uc_arch_supported(query):
return _uc.uc_arch_supported(query)
class uc_x86_mmr(ctypes.Structure):
'''Memory-Management Register for instructions IDTR, GDTR, LDTR, TR.'''
_fields_ = [
("selector", ctypes.c_uint16), # not used by GDTR and IDTR
("base", ctypes.c_uint64), # handle 32 or 64 bit CPUs
("limit", ctypes.c_uint32),
("flags", ctypes.c_uint32), # not used by GDTR and IDTR
]
class uc_x86_float80(ctypes.Structure):
'''Float80'''
_fields_ = [
("mantissa", ctypes.c_uint64),
("exponent", ctypes.c_uint16),
]
class Uc(object):
def __init__(self, arch, mode):
# verify version compatibility with the core before doing anything
@ -202,6 +223,20 @@ class Uc(object):
# return the value of a register
def reg_read(self, reg_id):
if self._arch == UC_ARCH_X86:
if reg_id in [ x86_const.UC_X86_REG_IDTR, x86_const.UC_X86_REG_GDTR, x86_const.UC_X86_REG_LDTR, x86_const.UC_X86_REG_TR]:
reg = uc_x86_mmr()
status = _uc.uc_reg_read(self._uch, reg_id, ctypes.byref(reg))
if status != UC_ERR_OK:
raise UcError(status)
return (reg.selector,reg.base, reg.limits, reg.flags)
if reg_id in range(x86_const.UC_X86_REG_FP0,x86_const.UC_X86_REG_FP0+8):
reg = uc_x86_float80()
status = _uc.uc_reg_read(self._uch, reg_id, ctypes.byref(reg))
if status != UC_ERR_OK:
raise UcError(status)
return (reg.mantissa, reg.exponent)
# read to 64bit number to be safe
reg = ctypes.c_int64(0)
status = _uc.uc_reg_read(self._uch, reg_id, ctypes.byref(reg))
@ -212,8 +247,25 @@ class Uc(object):
# write to a register
def reg_write(self, reg_id, value):
# convert to 64bit number to be safe
reg = ctypes.c_int64(value)
reg = None
if self._arch == UC_ARCH_X86:
if reg_id in [ x86_const.UC_X86_REG_IDTR, x86_const.UC_X86_REG_GDTR, x86_const.UC_X86_REG_LDTR, x86_const.UC_X86_REG_TR]:
assert isinstance(value, tuple) and len(value)==4
reg = uc_x86_mmr()
reg.selector=value[0]
reg.base=value[1]
reg.limits=value[2]
reg.flags=value[3]
if reg_id in range(x86_const.UC_X86_REG_FP0, x86_const.UC_X86_REG_FP0+8):
reg = uc_x86_float80()
reg.mantissa = value[0]
reg.exponent = value[1]
if reg is None:
# convert to 64bit number to be safe
reg = ctypes.c_int64(value)
status = _uc.uc_reg_write(self._uch, reg_id, ctypes.byref(reg))
if status != UC_ERR_OK:
raise UcError(status)
@ -262,6 +314,14 @@ class Uc(object):
if status != UC_ERR_OK:
raise UcError(status)
# return CPU mode at runtime
def query(self, query_mode):
result = ctypes.c_size_t(0)
status = _uc.uc_query(self._uch, query_mode, ctypes.byref(result))
if status != UC_ERR_OK:
raise UcError(status)
return result.value
def _hookcode_cb(self, handle, address, size, user_data):
# call user's callback with self object
@ -306,7 +366,7 @@ class Uc(object):
# add a hook
def hook_add(self, htype, callback, user_data=None, arg1=1, arg2=0):
def hook_add(self, htype, callback, user_data=None, begin=1, end=0, arg1=0):
_h2 = uc_hook_h()
# save callback & user_data
@ -314,25 +374,7 @@ class Uc(object):
self._callbacks[self._callback_count] = (callback, user_data)
cb = None
if htype in (UC_HOOK_BLOCK, UC_HOOK_CODE):
begin = ctypes.c_uint64(arg1)
end = ctypes.c_uint64(arg2)
# set callback with wrapper, so it can be called
# with this object as param
cb = ctypes.cast(UC_HOOK_CODE_CB(self._hookcode_cb), UC_HOOK_CODE_CB)
status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, cb, \
ctypes.cast(self._callback_count, ctypes.c_void_p), begin, end)
elif htype & UC_HOOK_MEM_READ_UNMAPPED or htype & UC_HOOK_MEM_WRITE_UNMAPPED or \
htype & UC_HOOK_MEM_FETCH_UNMAPPED or htype & UC_HOOK_MEM_READ_PROT or \
htype & UC_HOOK_MEM_WRITE_PROT or htype & UC_HOOK_MEM_FETCH_PROT:
cb = ctypes.cast(UC_HOOK_MEM_INVALID_CB(self._hook_mem_invalid_cb), UC_HOOK_MEM_INVALID_CB)
status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \
cb, ctypes.cast(self._callback_count, ctypes.c_void_p))
elif htype in (UC_HOOK_MEM_READ, UC_HOOK_MEM_WRITE, UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE):
cb = ctypes.cast(UC_HOOK_MEM_ACCESS_CB(self._hook_mem_access_cb), UC_HOOK_MEM_ACCESS_CB)
status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \
cb, ctypes.cast(self._callback_count, ctypes.c_void_p))
elif htype == UC_HOOK_INSN:
if htype == UC_HOOK_INSN:
insn = ctypes.c_int(arg1)
if arg1 == x86_const.UC_X86_INS_IN: # IN instruction
cb = ctypes.cast(UC_HOOK_INSN_IN_CB(self._hook_insn_in_cb), UC_HOOK_INSN_IN_CB)
@ -341,11 +383,28 @@ class Uc(object):
if arg1 in (x86_const.UC_X86_INS_SYSCALL, x86_const.UC_X86_INS_SYSENTER): # SYSCALL/SYSENTER instruction
cb = ctypes.cast(UC_HOOK_INSN_SYSCALL_CB(self._hook_insn_syscall_cb), UC_HOOK_INSN_SYSCALL_CB)
status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \
cb, ctypes.cast(self._callback_count, ctypes.c_void_p), insn)
cb, ctypes.cast(self._callback_count, ctypes.c_void_p), ctypes.c_uint64(begin), ctypes.c_uint64(end), insn)
elif htype == UC_HOOK_INTR:
cb = ctypes.cast(UC_HOOK_INTR_CB(self._hook_intr_cb), UC_HOOK_INTR_CB)
status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \
cb, ctypes.cast(self._callback_count, ctypes.c_void_p))
cb, ctypes.cast(self._callback_count, ctypes.c_void_p), ctypes.c_uint64(begin), ctypes.c_uint64(end))
else:
if htype in (UC_HOOK_BLOCK, UC_HOOK_CODE):
# set callback with wrapper, so it can be called
# with this object as param
cb = ctypes.cast(UC_HOOK_CODE_CB(self._hookcode_cb), UC_HOOK_CODE_CB)
status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, cb, \
ctypes.cast(self._callback_count, ctypes.c_void_p), ctypes.c_uint64(begin), ctypes.c_uint64(end))
elif htype & UC_HOOK_MEM_READ_UNMAPPED or htype & UC_HOOK_MEM_WRITE_UNMAPPED or \
htype & UC_HOOK_MEM_FETCH_UNMAPPED or htype & UC_HOOK_MEM_READ_PROT or \
htype & UC_HOOK_MEM_WRITE_PROT or htype & UC_HOOK_MEM_FETCH_PROT:
cb = ctypes.cast(UC_HOOK_MEM_INVALID_CB(self._hook_mem_invalid_cb), UC_HOOK_MEM_INVALID_CB)
status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \
cb, ctypes.cast(self._callback_count, ctypes.c_void_p), ctypes.c_uint64(begin), ctypes.c_uint64(end))
else:
cb = ctypes.cast(UC_HOOK_MEM_ACCESS_CB(self._hook_mem_access_cb), UC_HOOK_MEM_ACCESS_CB)
status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \
cb, ctypes.cast(self._callback_count, ctypes.c_void_p), ctypes.c_uint64(begin), ctypes.c_uint64(end))
# save the ctype function so gc will leave it alone.
self._ctype_cbs[self._callback_count] = cb
@ -380,3 +439,4 @@ def debug():
(major, minor, _combined) = uc_version()
return "python-%s-c%u.%u-b%u.%u" % (all_archs, major, minor, UC_API_MAJOR, UC_API_MINOR)

View file

@ -1,7 +1,7 @@
# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [unicorn_const.py]
UC_API_MAJOR = 1
UC_API_MAJOR = 0
UC_API_MINOR = 9
UC_API_MINOR = 0
UC_SECOND_SCALE = 1000000
UC_MILISECOND_SCALE = 1000
UC_ARCH_ARM = 1
@ -14,22 +14,26 @@ UC_ARCH_M68K = 7
UC_ARCH_MAX = 8
UC_MODE_LITTLE_ENDIAN = 0
UC_MODE_BIG_ENDIAN = 1073741824
UC_MODE_ARM = 0
UC_MODE_16 = 2
UC_MODE_32 = 4
UC_MODE_64 = 8
UC_MODE_THUMB = 16
UC_MODE_MCLASS = 32
UC_MODE_V8 = 64
UC_MODE_MICRO = 16
UC_MODE_MIPS3 = 32
UC_MODE_MIPS32R6 = 64
UC_MODE_V9 = 16
UC_MODE_QPX = 16
UC_MODE_BIG_ENDIAN = 1073741824
UC_MODE_MIPS32 = 4
UC_MODE_MIPS64 = 8
UC_MODE_16 = 2
UC_MODE_32 = 4
UC_MODE_64 = 8
UC_MODE_PPC32 = 4
UC_MODE_PPC64 = 8
UC_MODE_QPX = 16
UC_MODE_SPARC32 = 4
UC_MODE_SPARC64 = 8
UC_MODE_V9 = 16
UC_ERR_OK = 0
UC_ERR_NOMEM = 1
@ -80,6 +84,9 @@ UC_HOOK_MEM_READ_INVALID = 144
UC_HOOK_MEM_WRITE_INVALID = 288
UC_HOOK_MEM_FETCH_INVALID = 576
UC_HOOK_MEM_INVALID = 1008
UC_HOOK_MEM_VALID = 7168
UC_QUERY_MODE = 1
UC_QUERY_PAGE_SIZE = 2
UC_PROT_NONE = 0
UC_PROT_READ = 1

View file

@ -244,7 +244,13 @@ UC_X86_REG_R12W = 238
UC_X86_REG_R13W = 239
UC_X86_REG_R14W = 240
UC_X86_REG_R15W = 241
UC_X86_REG_ENDING = 242
UC_X86_REG_IDTR = 242
UC_X86_REG_GDTR = 243
UC_X86_REG_LDTR = 244
UC_X86_REG_TR = 245
UC_X86_REG_FPCW = 246
UC_X86_REG_FPTAG = 247
UC_X86_REG_ENDING = 248
# X86 instructions

279
hook.c
View file

@ -1,279 +0,0 @@
/* Unicorn Emulator Engine */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2015 */
#include "uc_priv.h"
#include "hook.h"
// return index for a new hook entry in hook_callbacks[] array.
// this realloc memory if needed.
size_t hook_find_new(struct uc_struct *uc)
{
size_t i;
struct hook_struct *new;
// find the first free slot. skip slot 0, so index > 0
for(i = 1; i < uc->hook_size; i++) {
if (uc->hook_callbacks[i].callback == NULL) {
return i;
}
}
// not found, so the array is full.
// we have to realloc hook_callbacks[] to contain new hooks
new = realloc(uc->hook_callbacks,
(uc->hook_size + HOOK_SIZE) * sizeof(uc->hook_callbacks[0]));
if (!new) // OOM ?
return 0;
// reset the newly added slots
memset(new + uc->hook_size, 0, HOOK_SIZE * sizeof(uc->hook_callbacks[0]));
uc->hook_callbacks = new;
uc->hook_size += HOOK_SIZE;
// return the first newly allocated slot
return uc->hook_size - HOOK_SIZE;
}
// return -1 on failure, index to hook_callbacks[] on success.
size_t hook_add(struct uc_struct *uc, int type, uint64_t begin, uint64_t end, void *callback, void *user_data)
{
int i;
// find the first free slot. skip slot 0, so index > 0
i = hook_find_new(uc);
if (i) {
uc->hook_callbacks[i].hook_type = type;
uc->hook_callbacks[i].begin = begin;
uc->hook_callbacks[i].end = end;
uc->hook_callbacks[i].callback = callback;
uc->hook_callbacks[i].user_data = user_data;
switch(type) {
default: break;
case UC_HOOK_BLOCK:
uc->hook_block = true;
if (begin > end)
uc->hook_block_idx = i;
break;
case UC_HOOK_CODE:
uc->hook_insn = true;
if (begin > end)
uc->hook_insn_idx = i;
break;
case UC_HOOK_MEM_READ:
uc->hook_mem_read = true;
if (begin > end)
uc->hook_read_idx = i;
break;
case UC_HOOK_MEM_WRITE:
uc->hook_mem_write = true;
if (begin > end)
uc->hook_write_idx = i;
break;
case UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE:
uc->hook_mem_read = true;
uc->hook_mem_write = true;
if (begin > end) {
uc->hook_read_idx = i;
uc->hook_write_idx = i;
}
break;
}
return i;
}
// not found
return 0;
}
// return 0 on success, -1 on failure
uc_err hook_del(struct uc_struct *uc, uc_hook hh)
{
if (hh == uc->hook_block_idx) {
uc->hook_block_idx = 0;
}
if (hh == uc->hook_insn_idx) {
uc->hook_insn_idx = 0;
}
if (hh == uc->hook_read_idx) {
uc->hook_read_idx = 0;
}
if (hh == uc->hook_write_idx) {
uc->hook_write_idx = 0;
}
if (hh == uc->hook_mem_read_idx) {
uc->hook_mem_read_idx = 0;
}
if (hh == uc->hook_mem_write_idx) {
uc->hook_mem_write_idx = 0;
}
if (hh == uc->hook_mem_fetch_idx) {
uc->hook_mem_fetch_idx = 0;
}
if (hh == uc->hook_mem_read_prot_idx) {
uc->hook_mem_read_prot_idx = 0;
}
if (hh == uc->hook_mem_write_prot_idx) {
uc->hook_mem_write_prot_idx = 0;
}
if (hh == uc->hook_mem_fetch_prot_idx) {
uc->hook_mem_fetch_prot_idx = 0;
}
if (hh == uc->hook_intr_idx) {
uc->hook_intr_idx = 0;
}
if (hh == uc->hook_out_idx) {
uc->hook_out_idx = 0;
}
if (hh == uc->hook_in_idx) {
uc->hook_in_idx = 0;
}
if(hh == uc->hook_syscall_idx) {
uc->hook_syscall_idx = 0;
}
uc->hook_callbacks[hh].callback = NULL;
uc->hook_callbacks[hh].user_data = NULL;
uc->hook_callbacks[hh].hook_type = 0;
uc->hook_callbacks[hh].begin = 0;
uc->hook_callbacks[hh].end = 0;
return UC_ERR_OK;
}
// return NULL on failure
static struct hook_struct *_hook_find(struct uc_struct *uc, int type, uint64_t address)
{
int i;
switch(type) {
default: break;
case UC_HOOK_BLOCK:
// already hooked all blocks?
if (uc->hook_block_idx)
return &uc->hook_callbacks[uc->hook_block_idx];
break;
case UC_HOOK_CODE:
// already hooked all the code?
if (uc->hook_insn_idx)
return &uc->hook_callbacks[uc->hook_insn_idx];
break;
case UC_HOOK_MEM_READ:
// already hooked all memory read?
if (uc->hook_read_idx) {
return &uc->hook_callbacks[uc->hook_read_idx];
}
break;
case UC_HOOK_MEM_WRITE:
// already hooked all memory write?
if (uc->hook_write_idx)
return &uc->hook_callbacks[uc->hook_write_idx];
break;
}
// no trace-all callback
for(i = 1; i < uc->hook_size; i++) {
switch(type) {
default: break;
case UC_HOOK_BLOCK:
case UC_HOOK_CODE:
if (uc->hook_callbacks[i].hook_type == type) {
if (uc->hook_callbacks[i].begin <= address && address <= uc->hook_callbacks[i].end)
return &uc->hook_callbacks[i];
}
break;
case UC_HOOK_MEM_READ:
if (uc->hook_callbacks[i].hook_type & UC_HOOK_MEM_READ) {
if (uc->hook_callbacks[i].begin <= address && address <= uc->hook_callbacks[i].end)
return &uc->hook_callbacks[i];
}
break;
case UC_HOOK_MEM_WRITE:
if (uc->hook_callbacks[i].hook_type & UC_HOOK_MEM_WRITE) {
if (uc->hook_callbacks[i].begin <= address && address <= uc->hook_callbacks[i].end)
return &uc->hook_callbacks[i];
}
break;
}
}
// not found
return NULL;
}
static void hook_count_cb(struct uc_struct *uc, uint64_t address, uint32_t size, void *user_data)
{
// count this instruction
uc->emu_counter++;
if (uc->emu_counter > uc->emu_count)
uc_emu_stop(uc);
else if (uc->hook_count_callback)
uc->hook_count_callback(uc, address, size, user_data);
}
struct hook_struct *hook_find(struct uc_struct *uc, int type, uint64_t address)
{
// stop executing callbacks if we already got stop request
if (uc->stop_request)
return NULL;
// UC_HOOK_CODE is special because we may need to count instructions
if (type == UC_HOOK_CODE && uc->emu_count > 0) {
struct hook_struct *st = _hook_find(uc, type, address);
if (st) {
// prepare this struct to pass back to caller
uc->hook_count.hook_type = UC_HOOK_CODE;
uc->hook_count.begin = st->begin;
uc->hook_count.end = st->end;
uc->hook_count.callback = hook_count_cb;
uc->hook_count.user_data = st->user_data;
// save this hook callback so we can call it later
uc->hook_count_callback = st->callback;
} else {
// there is no callback, but we still need to
// handle instruction count
uc->hook_count.hook_type = UC_HOOK_CODE;
uc->hook_count.begin = 1;
uc->hook_count.end = 0;
uc->hook_count.callback = hook_count_cb;
uc->hook_count.user_data = NULL;
uc->hook_count_callback = NULL; // no callback
}
return &(uc->hook_count);
} else
return _hook_find(uc, type, address);
}
// TCG helper
void helper_uc_tracecode(int32_t size, void *callback, void *handle, int64_t address, void *user_data);
void helper_uc_tracecode(int32_t size, void *callback, void *handle, int64_t address, void *user_data)
{
struct uc_struct *uc = handle;
// sync PC in CPUArchState with address
if (uc->set_pc) {
uc->set_pc(uc, address);
}
((uc_cb_hookcode_t)callback)(uc, address, size, user_data);
}

View file

@ -1,20 +0,0 @@
/* Unicorn Emulator Engine */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2015 */
#ifndef UC_HOOK_H
#define UC_HOOK_H
// return -1 on failure, index to traces[] on success.
size_t hook_add(struct uc_struct *uc, int type, uint64_t begin, uint64_t end, void *callback, void *user_data);
// return 0 on success, -1 on failure
uc_err hook_del(struct uc_struct *uc, uc_hook hh);
// return NULL on failure
struct hook_struct *hook_find(struct uc_struct *uc, int type, uint64_t address);
// return index of an free hook entry in hook_callbacks[] array.
// this realloc memory if needed.
size_t hook_find_new(struct uc_struct *uc);
#endif

20
include/list.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef UC_LLIST_H
#define UC_LLIST_H
#include <stdbool.h>
struct list_item {
struct list_item *next;
void *data;
};
struct list {
struct list_item *head, *tail;
};
struct list *list_new(void);
void list_clear(struct list *list);
void *list_append(struct list *list, void *data);
bool list_remove(struct list *list, void *data);
#endif

View file

@ -9,10 +9,30 @@
#include "qemu.h"
#include "unicorn/unicorn.h"
#include "hook.h"
#include "list.h"
// These are masks of supported modes for each cpu/arch.
// They should be updated when changes are made to the uc_mode enum typedef.
#define UC_MODE_ARM_MASK (UC_MODE_ARM|UC_MODE_THUMB|UC_MODE_LITTLE_ENDIAN)
#define UC_MODE_MIPS_MASK (UC_MODE_MIPS32|UC_MODE_MIPS64|UC_MODE_LITTLE_ENDIAN|UC_MODE_BIG_ENDIAN)
#define UC_MODE_X86_MASK (UC_MODE_16|UC_MODE_32|UC_MODE_64|UC_MODE_LITTLE_ENDIAN)
#define UC_MODE_PPC_MASK (UC_MODE_PPC64|UC_MODE_BIG_ENDIAN)
#define UC_MODE_SPARC_MASK (UC_MODE_SPARC32|UC_MODE_SPARC64|UC_MODE_BIG_ENDIAN)
#define UC_MODE_M68K_MASK (UC_MODE_BIG_ENDIAN)
#define ARR_SIZE(a) (sizeof(a)/sizeof(a[0]))
#define READ_QWORD(x) ((uint64)x)
#define READ_DWORD(x) (x & 0xffffffff)
#define READ_WORD(x) (x & 0xffff)
#define READ_BYTE_H(x) ((x & 0xffff) >> 8)
#define READ_BYTE_L(x) (x & 0xff)
#define WRITE_DWORD(x, w) (x = (x & ~0xffffffff) | (w & 0xffffffff))
#define WRITE_WORD(x, w) (x = (x & ~0xffff) | (w & 0xffff))
#define WRITE_BYTE_H(x, b) (x = (x & ~0xff00) | ((b & 0xff) << 8))
#define WRITE_BYTE_L(x, b) (x = (x & ~0xff) | (b & 0xff))
QTAILQ_HEAD(CPUTailQ, CPUState);
typedef struct ModuleEntry {
@ -23,6 +43,8 @@ typedef struct ModuleEntry {
typedef QTAILQ_HEAD(, ModuleEntry) ModuleTypeList;
typedef uc_err (*query_t)(struct uc_struct *uc, uc_query_type type, size_t *result);
// return 0 on success, -1 on failure
typedef int (*reg_read_t)(struct uc_struct *uc, unsigned int regid, void *value);
typedef int (*reg_write_t)(struct uc_struct *uc, unsigned int regid, const void *value);
@ -60,16 +82,62 @@ typedef bool (*uc_args_int_t)(int intno);
// some architecture redirect virtual memory to physical memory like Mips
typedef uint64_t (*uc_mem_redirect_t)(uint64_t address);
struct hook_struct {
int hook_type; // uc_tracecode_type & uc_tracemem_type
uint64_t begin, end; // range of address to be monitored
void *callback; // either uc_cb_tracecode_t or uc_cb_tracemem_t
struct hook {
int type; // UC_HOOK_*
int insn; // instruction for HOOK_INSN
int refs; // reference count to free hook stored in multiple lists
uint64_t begin, end; // only trigger if PC or memory access is in this address (depends on hook type)
void *callback; // a uc_cb_* type
void *user_data;
};
// extend memory to keep 32 more hooks each time
#define HOOK_SIZE 32
// hook list offsets
// mirrors the order of uc_hook_type from include/unicorn/unicorn.h
enum uc_hook_idx {
UC_HOOK_INTR_IDX,
UC_HOOK_INSN_IDX,
UC_HOOK_CODE_IDX,
UC_HOOK_BLOCK_IDX,
UC_HOOK_MEM_READ_UNMAPPED_IDX,
UC_HOOK_MEM_WRITE_UNMAPPED_IDX,
UC_HOOK_MEM_FETCH_UNMAPPED_IDX,
UC_HOOK_MEM_READ_PROT_IDX,
UC_HOOK_MEM_WRITE_PROT_IDX,
UC_HOOK_MEM_FETCH_PROT_IDX,
UC_HOOK_MEM_READ_IDX,
UC_HOOK_MEM_WRITE_IDX,
UC_HOOK_MEM_FETCH_IDX,
UC_HOOK_MAX,
};
// for loop macro to loop over hook lists
#define HOOK_FOREACH(uc, hh, idx) \
struct list_item *cur; \
for ( \
cur = (uc)->hook[idx##_IDX].head; \
cur != NULL && ((hh) = (struct hook *)cur->data) \
/* stop excuting callbacks on stop request */ \
&& !uc->stop_request; \
cur = cur->next)
// if statement to check hook bounds
#define HOOK_BOUND_CHECK(hh, addr) \
((((addr) >= (hh)->begin && (addr) <= (hh)->end) \
|| (hh)->begin > (hh)->end))
#define HOOK_EXISTS(uc, idx) ((uc)->hook[idx##_IDX].head != NULL)
#define HOOK_EXISTS_BOUNDED(uc, idx, addr) _hook_exists_bounded((uc)->hook[idx##_IDX].head, addr)
static inline bool _hook_exists_bounded(struct list_item *cur, uint64_t addr)
{
while (cur != NULL) {
if (HOOK_BOUND_CHECK((struct hook *)cur->data, addr))
return true;
cur = cur->next;
}
return false;
}
//relloc increment, KEEP THIS A POWER OF 2!
#define MEM_BLOCK_INCR 32
@ -84,6 +152,7 @@ struct uc_struct {
struct CPUTailQ cpus; // qemu/cpu-exec.c
uc_err errnum; // qemu/cpu-exec.c
AddressSpace as;
query_t query;
reg_read_t reg_read;
reg_write_t reg_write;
reg_reset_t reg_reset;
@ -144,38 +213,20 @@ struct uc_struct {
bool apic_report_tpr_access;
CPUState *current_cpu;
// all the hook callbacks
size_t hook_size;
struct hook_struct *hook_callbacks;
// linked lists containing hooks per type
struct list hook[UC_HOOK_MAX];
// hook to count number of instructions for uc_emu_start()
struct hook_struct hook_count;
uc_cb_hookcode_t hook_count_callback;
uc_hook count_hook;
size_t emu_counter; // current counter of uc_emu_start()
size_t emu_count; // save counter of uc_emu_start()
// indexes if hooking ALL block/code/read/write events
unsigned int hook_block_idx, hook_insn_idx, hook_read_idx, hook_write_idx;
// boolean variables for quick check on hooking block, code, memory accesses
bool hook_block, hook_insn, hook_mem_read, hook_mem_write;
uint64_t block_addr; // save the last block address we hooked
// indexes to event callbacks
int hook_mem_read_idx; // for handling invalid memory read access on unmapped memory
int hook_mem_write_idx; // for handling invalid memory write access on unmapped memory
int hook_mem_fetch_idx; // for handling invalid memory fetch access on unmapped memory
int hook_mem_read_prot_idx; // for handling invalid memory read access on read-protected memory
int hook_mem_write_prot_idx; // for handling invalid memory write access on write-protected memory
int hook_mem_fetch_prot_idx; // for handling invalid memory fetch access on non-executable memory
int hook_intr_idx; // for handling interrupt
int hook_out_idx; // for handling OUT instruction (X86)
int hook_in_idx; // for handling IN instruction (X86)
int hook_syscall_idx; // for handling SYSCALL/SYSENTER (X86)
bool init_tcg; // already initialized local TCGv variables?
bool stop_request; // request to immediately stop emulation - for uc_emu_stop()
bool quit_request; // request to quit the current TB, but continue to emulate - for uc_mem_protect()
bool emulation_done; // emulation is done by uc_emu_start()
QemuThread timer; // timer for emulation timeout
uint64_t timeout; // timeout for uc_emu_start()

View file

@ -57,8 +57,8 @@ typedef size_t uc_hook;
#endif
// Unicorn API version
#define UC_API_MAJOR 0
#define UC_API_MINOR 9
#define UC_API_MAJOR 1
#define UC_API_MINOR 0
/*
Macro to create combined version which can be compared to
@ -78,7 +78,7 @@ typedef enum uc_arch {
UC_ARCH_ARM64, // ARM-64, also called AArch64
UC_ARCH_MIPS, // Mips architecture
UC_ARCH_X86, // X86 architecture (including x86 & x86-64)
UC_ARCH_PPC, // PowerPC architecture
UC_ARCH_PPC, // PowerPC architecture (currently unsupported)
UC_ARCH_SPARC, // Sparc architecture
UC_ARCH_M68K, // M68K architecture
UC_ARCH_MAX,
@ -86,22 +86,32 @@ typedef enum uc_arch {
// Mode type
typedef enum uc_mode {
UC_MODE_LITTLE_ENDIAN = 0, // little-endian mode (default mode)
UC_MODE_ARM = 0, // 32-bit ARM
UC_MODE_16 = 1 << 1, // 16-bit mode (X86)
UC_MODE_32 = 1 << 2, // 32-bit mode (X86)
UC_MODE_64 = 1 << 3, // 64-bit mode (X86, PPC)
UC_MODE_THUMB = 1 << 4, // ARM's Thumb mode, including Thumb-2
UC_MODE_MCLASS = 1 << 5, // ARM's Cortex-M series
UC_MODE_V8 = 1 << 6, // ARMv8 A32 encodings for ARM
UC_MODE_MICRO = 1 << 4, // MicroMips mode (MIPS)
UC_MODE_MIPS3 = 1 << 5, // Mips III ISA
UC_MODE_MIPS32R6 = 1 << 6, // Mips32r6 ISA
UC_MODE_V9 = 1 << 4, // SparcV9 mode (Sparc)
UC_MODE_QPX = 1 << 4, // Quad Processing eXtensions mode (PPC)
UC_MODE_BIG_ENDIAN = 1 << 30, // big-endian mode
UC_MODE_MIPS32 = UC_MODE_32, // Mips32 ISA (Mips)
UC_MODE_MIPS64 = UC_MODE_64, // Mips64 ISA (Mips)
UC_MODE_LITTLE_ENDIAN = 0, // little-endian mode (default mode)
UC_MODE_BIG_ENDIAN = 1 << 30, // big-endian mode
// arm / arm64
UC_MODE_ARM = 0, // ARM mode
UC_MODE_THUMB = 1 << 4, // THUMB mode (including Thumb-2)
UC_MODE_MCLASS = 1 << 5, // ARM's Cortex-M series (currently unsupported)
UC_MODE_V8 = 1 << 6, // ARMv8 A32 encodings for ARM (currently unsupported)
// mips
UC_MODE_MICRO = 1 << 4, // MicroMips mode (currently unsupported)
UC_MODE_MIPS3 = 1 << 5, // Mips III ISA (currently unsupported)
UC_MODE_MIPS32R6 = 1 << 6, // Mips32r6 ISA (currently unsupported)
UC_MODE_MIPS32 = 1 << 2, // Mips32 ISA
UC_MODE_MIPS64 = 1 << 3, // Mips64 ISA
// x86 / x64
UC_MODE_16 = 1 << 1, // 16-bit mode
UC_MODE_32 = 1 << 2, // 32-bit mode
UC_MODE_64 = 1 << 3, // 64-bit mode
// ppc
UC_MODE_PPC32 = 1 << 2, // 32-bit mode (currently unsupported)
UC_MODE_PPC64 = 1 << 3, // 64-bit mode (currently unsupported)
UC_MODE_QPX = 1 << 4, // Quad Processing eXtensions mode (currently unsupported)
// sparc
UC_MODE_SPARC32 = 1 << 2, // 32-bit mode
UC_MODE_SPARC64 = 1 << 3, // 64-bit mode
UC_MODE_V9 = 1 << 4, // SparcV9 mode (currently unsupported)
// m68k
} uc_mode;
// All type of errors encountered by Unicorn API.
@ -208,6 +218,8 @@ typedef enum uc_hook_type {
#define UC_HOOK_MEM_FETCH_INVALID (UC_HOOK_MEM_FETCH_PROT + UC_HOOK_MEM_FETCH_UNMAPPED)
// hook type for all events of illegal memory access
#define UC_HOOK_MEM_INVALID (UC_HOOK_MEM_UNMAPPED + UC_HOOK_MEM_PROT)
// hook type for all events of valid memory access
#define UC_HOOK_MEM_VALID (UC_HOOK_MEM_READ + UC_HOOK_MEM_WRITE + UC_HOOK_MEM_FETCH)
/*
Callback function for hooking memory (UC_MEM_READ, UC_MEM_WRITE & UC_MEM_FETCH)
@ -246,6 +258,13 @@ typedef struct uc_mem_region {
uint32_t perms; // memory permissions of the region
} uc_mem_region;
// All type of queries for uc_query() API.
typedef enum uc_query_type {
// Dynamically query current hardware mode.
UC_QUERY_MODE = 1,
UC_QUERY_PAGE_SIZE,
} uc_query_type;
/*
Return combined API version & major and minor version numbers.
@ -306,6 +325,19 @@ uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **uc);
UNICORN_EXPORT
uc_err uc_close(uc_engine *uc);
/*
Query internal status of engine.
@uc: handle returned by uc_open()
@type: query type. See uc_query_type
@result: save the internal status queried
@return: error code of uc_err enum type (UC_ERR_*, see above)
*/
UNICORN_EXPORT
uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result);
/*
Report the last error number when some API function fail.
Like glibc's errno, uc_errno might not retain its old value once accessed.
@ -426,13 +458,19 @@ uc_err uc_emu_stop(uc_engine *uc);
@callback: callback to be run when instruction is hit
@user_data: user-defined data. This will be passed to callback function in its
last argument @user_data
@begin: start address of the area where the callback is effect (inclusive)
@end: end address of the area where the callback is effect (inclusive)
NOTE 1: the callback is called only if related address is in range [@begin, @end]
NOTE 2: if @begin > @end, callback is called whenever this hook type is triggered
@...: variable arguments (depending on @type)
NOTE: if @type = UC_HOOK_INSN, this is the instruction ID (ex: UC_X86_INS_OUT)
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, ...);
uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback,
void *user_data, uint64_t begin, uint64_t end, ...);
/*
Unregister (remove) a hook callback.

View file

@ -8,6 +8,15 @@
extern "C" {
#endif
// Memory-Management Register for instructions IDTR, GDTR, LDTR, TR.
// Borrow from SegmentCache in qemu/target-i386/cpu.h
typedef struct uc_x86_mmr {
uint16_t selector; /* not used by GDTR and IDTR */
uint64_t base; /* handle 32 or 64 bit CPUs */
uint32_t limit;
uint32_t flags; /* not used by GDTR and IDTR */
} uc_x86_mmr;
// Callback function for tracing SYSCALL/SYSENTER (for uc_hook_intr())
// @user_data: user data passed to tracing APIs.
typedef void (*uc_cb_insn_syscall_t)(struct uc_struct *uc, void *user_data);
@ -64,6 +73,8 @@ typedef enum uc_x86_reg {
UC_X86_REG_R9D, UC_X86_REG_R10D, UC_X86_REG_R11D, UC_X86_REG_R12D, UC_X86_REG_R13D,
UC_X86_REG_R14D, UC_X86_REG_R15D, UC_X86_REG_R8W, UC_X86_REG_R9W, UC_X86_REG_R10W,
UC_X86_REG_R11W, UC_X86_REG_R12W, UC_X86_REG_R13W, UC_X86_REG_R14W, UC_X86_REG_R15W,
UC_X86_REG_IDTR, UC_X86_REG_GDTR, UC_X86_REG_LDTR, UC_X86_REG_TR, UC_X86_REG_FPCW,
UC_X86_REG_FPTAG,
UC_X86_REG_ENDING // <-- mark the end of the list of registers
} uc_x86_reg;

68
list.c Normal file
View file

@ -0,0 +1,68 @@
#include <stdlib.h>
#include <stdint.h>
#include "list.h"
// simple linked list implementation
struct list *list_new(void)
{
return calloc(1, sizeof(struct list));
}
// removed linked list nodes but does not free their content
void list_clear(struct list *list)
{
struct list_item *next, *cur = list->head;
while (cur != NULL) {
next = cur->next;
free(cur);
cur = next;
}
list->head = NULL;
list->tail = NULL;
}
// returns generated linked list node, or NULL on failure
void *list_append(struct list *list, void *data)
{
struct list_item *item = malloc(sizeof(struct list_item));
if (item == NULL) {
return NULL;
}
item->next = NULL;
item->data = data;
if (list->head == NULL) {
list->head = item;
} else {
list->tail->next = item;
}
list->tail = item;
return item;
}
// returns true if entry was removed, false otherwise
bool list_remove(struct list *list, void *data)
{
struct list_item *next, *cur, *prev = NULL;
// is list empty?
if (list->head == NULL) {
return false;
}
cur = list->head;
while (cur != NULL) {
next = cur->next;
if (cur->data == data) {
if (cur == list->head) {
list->head = next;
}
if (cur == list->tail) {
list->tail = prev;
}
free(cur);
return true;
}
prev = cur;
cur = next;
}
return false;
}

View file

@ -2,8 +2,8 @@
# To be used to generate unicorn.pc for pkg-config
# version major & minor
PKG_MAJOR = 0
PKG_MINOR = 9
PKG_MAJOR = 1
PKG_MINOR = 0
# version bugfix level. Example: PKG_EXTRA = 1
PKG_EXTRA =

View file

@ -6,7 +6,7 @@ util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o
# block-obj-y is code used by both qemu system emulation and qemu-img
block-obj-y =
block-obj-y += ../uc.o ../hook.o
block-obj-y += ../uc.o ../list.o
#block-obj-$(CONFIG_POSIX) += aio-posix.o
#block-obj-$(CONFIG_WIN32) += aio-win32.o

View file

@ -1,6 +1,7 @@
/* Autogen header for Unicorn Engine - DONOT MODIFY */
#ifndef UNICORN_AUTOGEN_AARCH64_H
#define UNICORN_AUTOGEN_AARCH64_H
#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_aarch64
#define use_idiv_instructions_rt use_idiv_instructions_rt_aarch64
#define tcg_target_deposit_valid tcg_target_deposit_valid_aarch64
#define helper_power_down helper_power_down_aarch64

View file

@ -1,6 +1,7 @@
/* Autogen header for Unicorn Engine - DONOT MODIFY */
#ifndef UNICORN_AUTOGEN_ARM_H
#define UNICORN_AUTOGEN_ARM_H
#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_arm
#define use_idiv_instructions_rt use_idiv_instructions_rt_arm
#define tcg_target_deposit_valid tcg_target_deposit_valid_arm
#define helper_power_down helper_power_down_arm

View file

@ -64,6 +64,8 @@ int cpu_exec(struct uc_struct *uc, CPUArchState *env) // qq
TranslationBlock *tb;
uint8_t *tc_ptr;
uintptr_t next_tb;
struct hook *hook;
/* This must be volatile so it is not trashed by longjmp() */
volatile bool have_tb_lock = false;
@ -99,6 +101,7 @@ int cpu_exec(struct uc_struct *uc, CPUArchState *env) // qq
if (sigsetjmp(cpu->jmp_env, 0) == 0) {
if (uc->stop_request || uc->invalid_error)
break;
/* if an exception is pending, we execute it here */
if (cpu->exception_index >= 0) {
//printf(">>> GOT INTERRUPT. exception idx = %x\n", cpu->exception_index); // qq
@ -127,11 +130,10 @@ int cpu_exec(struct uc_struct *uc, CPUArchState *env) // qq
ret = cpu->exception_index;
break;
#else
// Unicorn: call interrupt callback if registered
if (uc->hook_intr_idx)
((uc_cb_hookintr_t)uc->hook_callbacks[uc->hook_intr_idx].callback)(
uc, cpu->exception_index,
uc->hook_callbacks[uc->hook_intr_idx].user_data);
// Unicorn: call registered interrupt callbacks
HOOK_FOREACH(uc, hook, UC_HOOK_INTR) {
((uc_cb_hookintr_t)hook->callback)(uc, cpu->exception_index, hook->user_data);
}
cpu->exception_index = -1;
#if defined(TARGET_X86_64)
// point EIP to the next instruction after INT
@ -234,6 +236,7 @@ int cpu_exec(struct uc_struct *uc, CPUArchState *env) // qq
tc_ptr = tb->tc_ptr;
/* execute the generated code */
next_tb = cpu_tb_exec(cpu, tc_ptr); // qq
switch (next_tb & TB_EXIT_MASK) {
case TB_EXIT_REQUESTED:
/* Something asked us to stop executing
@ -300,12 +303,13 @@ static tcg_target_ulong cpu_tb_exec(CPUState *cpu, uint8_t *tb_ptr)
TranslationBlock *tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK);
if (cc->synchronize_from_tb) {
// avoid sync twice when helper_uc_tracecode() already did this.
if (env->uc->emu_counter <= env->uc->emu_count && !env->uc->stop_request)
if (env->uc->emu_counter <= env->uc->emu_count &&
!env->uc->stop_request && !env->uc->quit_request)
cc->synchronize_from_tb(cpu, tb);
} else {
assert(cc->set_pc);
// avoid sync twice when helper_uc_tracecode() already did this.
if (env->uc->emu_counter <= env->uc->emu_count)
if (env->uc->emu_counter <= env->uc->emu_count && !env->uc->quit_request)
cc->set_pc(cpu, tb->pc);
}
}

View file

@ -231,8 +231,14 @@ static bool tcg_exec_all(struct uc_struct* uc)
//qemu_clock_enable(QEMU_CLOCK_VIRTUAL,
// (cpu->singlestep_enabled & SSTEP_NOTIMER) == 0);
if (cpu_can_run(cpu)) {
uc->quit_request = false;
r = tcg_cpu_exec(uc, env);
if (uc->stop_request) {
// quit current TB but continue emulating?
if (uc->quit_request) {
// reset stop_request
uc->stop_request = false;
} else if (uc->stop_request) {
//printf(">>> got STOP request!!!\n");
finish = true;
break;

View file

@ -7,6 +7,7 @@
import sys
symbols = (
'aarch64_tb_set_jmp_target',
'use_idiv_instructions_rt',
'tcg_target_deposit_valid',
'helper_power_down',

View file

@ -66,39 +66,45 @@ const MemoryRegionOps unassigned_io_ops = {
void cpu_outb(struct uc_struct *uc, pio_addr_t addr, uint8_t val)
{
//LOG_IOPORT("outb: %04"FMT_pioaddr" %02"PRIx8"\n", addr, val);
// Unicorn: call interrupt callback if registered
if (uc->hook_out_idx)
((uc_cb_insn_out_t)uc->hook_callbacks[uc->hook_out_idx].callback)(
uc, addr, 1, val,
uc->hook_callbacks[uc->hook_out_idx].user_data);
// Unicorn: call registered OUT callbacks
struct hook *hook;
HOOK_FOREACH(uc, hook, UC_HOOK_INSN) {
if (hook->insn == UC_X86_INS_OUT)
((uc_cb_insn_out_t)hook->callback)(uc, addr, 1, val, hook->user_data);
}
}
void cpu_outw(struct uc_struct *uc, pio_addr_t addr, uint16_t val)
{
//LOG_IOPORT("outw: %04"FMT_pioaddr" %04"PRIx16"\n", addr, val);
// Unicorn: call interrupt callback if registered
if (uc->hook_out_idx)
((uc_cb_insn_out_t)uc->hook_callbacks[uc->hook_out_idx].callback)(
uc, addr, 2, val,
uc->hook_callbacks[uc->hook_out_idx].user_data);
// Unicorn: call registered OUT callbacks
struct hook *hook;
HOOK_FOREACH(uc, hook, UC_HOOK_INSN) {
if (hook->insn == UC_X86_INS_OUT)
((uc_cb_insn_out_t)hook->callback)(uc, addr, 2, val, hook->user_data);
}
}
void cpu_outl(struct uc_struct *uc, pio_addr_t addr, uint32_t val)
{
//LOG_IOPORT("outl: %04"FMT_pioaddr" %08"PRIx32"\n", addr, val);
if (uc->hook_out_idx)
((uc_cb_insn_out_t)uc->hook_callbacks[uc->hook_out_idx].callback)(
uc, addr, 4, val,
uc->hook_callbacks[uc->hook_out_idx].user_data);
// Unicorn: call registered OUT callbacks
struct hook *hook;
HOOK_FOREACH(uc, hook, UC_HOOK_INSN) {
if (hook->insn == UC_X86_INS_OUT)
((uc_cb_insn_out_t)hook->callback)(uc, addr, 4, val, hook->user_data);
}
}
uint8_t cpu_inb(struct uc_struct *uc, pio_addr_t addr)
{
//LOG_IOPORT("inb : %04"FMT_pioaddr" %02"PRIx8"\n", addr, val);
if (uc->hook_in_idx)
return ((uc_cb_insn_in_t)uc->hook_callbacks[uc->hook_in_idx].callback)(
uc, addr, 1,
uc->hook_callbacks[uc->hook_in_idx].user_data);
// Unicorn: call registered IN callbacks
struct hook *hook;
HOOK_FOREACH(uc, hook, UC_HOOK_INSN) {
if (hook->insn == UC_X86_INS_IN)
return ((uc_cb_insn_in_t)hook->callback)(uc, addr, 1, hook->user_data);
}
return 0;
}
@ -106,10 +112,12 @@ uint8_t cpu_inb(struct uc_struct *uc, pio_addr_t addr)
uint16_t cpu_inw(struct uc_struct *uc, pio_addr_t addr)
{
//LOG_IOPORT("inw : %04"FMT_pioaddr" %04"PRIx16"\n", addr, val);
if (uc->hook_in_idx)
return ((uc_cb_insn_in_t)uc->hook_callbacks[uc->hook_in_idx].callback)(
uc, addr, 2,
uc->hook_callbacks[uc->hook_in_idx].user_data);
// Unicorn: call registered IN callbacks
struct hook *hook;
HOOK_FOREACH(uc, hook, UC_HOOK_INSN) {
if (hook->insn == UC_X86_INS_IN)
return ((uc_cb_insn_in_t)hook->callback)(uc, addr, 2, hook->user_data);
}
return 0;
}
@ -117,10 +125,12 @@ uint16_t cpu_inw(struct uc_struct *uc, pio_addr_t addr)
uint32_t cpu_inl(struct uc_struct *uc, pio_addr_t addr)
{
//LOG_IOPORT("inl : %04"FMT_pioaddr" %08"PRIx32"\n", addr, val);
if (uc->hook_in_idx)
return ((uc_cb_insn_in_t)uc->hook_callbacks[uc->hook_in_idx].callback)(
uc, addr, 4,
uc->hook_callbacks[uc->hook_in_idx].user_data);
// Unicorn: call registered IN callbacks
struct hook *hook;
HOOK_FOREACH(uc, hook, UC_HOOK_INSN) {
if (hook->insn == UC_X86_INS_IN)
return ((uc_cb_insn_in_t)hook->callback)(uc, addr, 4, hook->user_data);
}
return 0;
}

View file

@ -1,6 +1,7 @@
/* Autogen header for Unicorn Engine - DONOT MODIFY */
#ifndef UNICORN_AUTOGEN_M68K_H
#define UNICORN_AUTOGEN_M68K_H
#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_m68k
#define use_idiv_instructions_rt use_idiv_instructions_rt_m68k
#define tcg_target_deposit_valid tcg_target_deposit_valid_m68k
#define helper_power_down helper_power_down_m68k

View file

@ -85,7 +85,7 @@ void memory_unmap(struct uc_struct *uc, MemoryRegion *mr)
if (uc->mapped_blocks[i] == mr) {
uc->mapped_block_count--;
//shift remainder of array down over deleted pointer
memcpy(&uc->mapped_blocks[i], &uc->mapped_blocks[i + 1], sizeof(MemoryRegion*) * (uc->mapped_block_count - i));
memmove(&uc->mapped_blocks[i], &uc->mapped_blocks[i + 1], sizeof(MemoryRegion*) * (uc->mapped_block_count - i));
mr->destructor(mr);
g_free((char *)mr->name);
g_free(mr->ioeventfds);

View file

@ -1,6 +1,7 @@
/* Autogen header for Unicorn Engine - DONOT MODIFY */
#ifndef UNICORN_AUTOGEN_MIPS_H
#define UNICORN_AUTOGEN_MIPS_H
#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_mips
#define use_idiv_instructions_rt use_idiv_instructions_rt_mips
#define tcg_target_deposit_valid tcg_target_deposit_valid_mips
#define helper_power_down helper_power_down_mips

View file

@ -1,6 +1,7 @@
/* Autogen header for Unicorn Engine - DONOT MODIFY */
#ifndef UNICORN_AUTOGEN_MIPS64_H
#define UNICORN_AUTOGEN_MIPS64_H
#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_mips64
#define use_idiv_instructions_rt use_idiv_instructions_rt_mips64
#define tcg_target_deposit_valid tcg_target_deposit_valid_mips64
#define helper_power_down helper_power_down_mips64

View file

@ -1,6 +1,7 @@
/* Autogen header for Unicorn Engine - DONOT MODIFY */
#ifndef UNICORN_AUTOGEN_MIPS64EL_H
#define UNICORN_AUTOGEN_MIPS64EL_H
#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_mips64el
#define use_idiv_instructions_rt use_idiv_instructions_rt_mips64el
#define tcg_target_deposit_valid tcg_target_deposit_valid_mips64el
#define helper_power_down helper_power_down_mips64el

View file

@ -1,6 +1,7 @@
/* Autogen header for Unicorn Engine - DONOT MODIFY */
#ifndef UNICORN_AUTOGEN_MIPSEL_H
#define UNICORN_AUTOGEN_MIPSEL_H
#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_mipsel
#define use_idiv_instructions_rt use_idiv_instructions_rt_mipsel
#define tcg_target_deposit_valid tcg_target_deposit_valid_mipsel
#define helper_power_down helper_power_down_mipsel

View file

@ -1,6 +1,7 @@
/* Autogen header for Unicorn Engine - DONOT MODIFY */
#ifndef UNICORN_AUTOGEN_POWERPC_H
#define UNICORN_AUTOGEN_POWERPC_H
#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_powerpc
#define use_idiv_instructions_rt use_idiv_instructions_rt_powerpc
#define tcg_target_deposit_valid tcg_target_deposit_valid_powerpc
#define helper_power_down helper_power_down_powerpc

View file

@ -178,23 +178,33 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
uintptr_t haddr;
DATA_TYPE res;
int error_code;
struct hook *hook;
bool handled;
struct uc_struct *uc = env->uc;
MemoryRegion *mr = memory_mapping(uc, addr);
// memory might be still unmapped while reading or fetching
if (mr == NULL) {
handled = false;
#if defined(SOFTMMU_CODE_ACCESS)
error_code = UC_ERR_FETCH_UNMAPPED;
if (uc->hook_mem_fetch_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_fetch_idx].callback)(
uc, UC_MEM_FETCH_UNMAPPED, addr, DATA_SIZE, 0,
uc->hook_callbacks[uc->hook_mem_fetch_idx].user_data)) {
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_FETCH_UNMAPPED) {
if (!HOOK_BOUND_CHECK(hook, addr))
continue;
if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_FETCH_UNMAPPED, addr, DATA_SIZE, 0, hook->user_data)))
break;
}
#else
error_code = UC_ERR_READ_UNMAPPED;
if (uc->hook_mem_read_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_read_idx].callback)(
uc, UC_MEM_READ_UNMAPPED, addr, DATA_SIZE, 0,
uc->hook_callbacks[uc->hook_mem_read_idx].user_data)) {
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_UNMAPPED) {
if (!HOOK_BOUND_CHECK(hook, addr))
continue;
if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_READ_UNMAPPED, addr, DATA_SIZE, 0, hook->user_data)))
break;
}
#endif
if (handled) {
env->invalid_error = UC_ERR_OK;
mr = memory_mapping(uc, addr); // FIXME: what if mr is still NULL at this time?
} else {
@ -209,9 +219,15 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
#if defined(SOFTMMU_CODE_ACCESS)
// Unicorn: callback on fetch from NX
if (mr != NULL && !(mr->perms & UC_PROT_EXEC)) { // non-executable
if (uc->hook_mem_fetch_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].callback)(
uc, UC_MEM_FETCH_PROT, addr, DATA_SIZE, 0,
uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].user_data)) {
handled = false;
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_FETCH_PROT) {
if (!HOOK_BOUND_CHECK(hook, addr))
continue;
if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_FETCH_PROT, addr, DATA_SIZE, 0, hook->user_data)))
break;
}
if (handled) {
env->invalid_error = UC_ERR_OK;
} else {
env->invalid_addr = addr;
@ -224,19 +240,25 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
#endif
// Unicorn: callback on memory read
if (READ_ACCESS_TYPE == MMU_DATA_LOAD && env->uc->hook_mem_read) {
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_MEM_READ, addr);
if (trace) {
((uc_cb_hookmem_t)trace->callback)(env->uc, UC_MEM_READ,
(uint64_t)addr, (int)DATA_SIZE, (int64_t)0, trace->user_data);
if (READ_ACCESS_TYPE == MMU_DATA_LOAD) {
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ) {
if (!HOOK_BOUND_CHECK(hook, addr))
continue;
((uc_cb_hookmem_t)hook->callback)(env->uc, UC_MEM_READ, addr, DATA_SIZE, 0, hook->user_data);
}
}
// Unicorn: callback on non-readable memory
if (READ_ACCESS_TYPE == MMU_DATA_LOAD && mr != NULL && !(mr->perms & UC_PROT_READ)) { //non-readable
if (uc->hook_mem_read_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_read_prot_idx].callback)(
uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0,
uc->hook_callbacks[uc->hook_mem_read_prot_idx].user_data)) {
handled = false;
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_PROT) {
if (!HOOK_BOUND_CHECK(hook, addr))
continue;
if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0, hook->user_data)))
break;
}
if (handled) {
env->invalid_error = UC_ERR_OK;
} else {
env->invalid_addr = addr;
@ -368,23 +390,33 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
uintptr_t haddr;
DATA_TYPE res;
int error_code;
struct hook *hook;
bool handled;
struct uc_struct *uc = env->uc;
MemoryRegion *mr = memory_mapping(uc, addr);
// memory can be unmapped while reading or fetching
if (mr == NULL) {
handled = false;
#if defined(SOFTMMU_CODE_ACCESS)
error_code = UC_ERR_FETCH_UNMAPPED;
if (uc->hook_mem_fetch_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_fetch_idx].callback)(
uc, UC_MEM_FETCH_UNMAPPED, addr, DATA_SIZE, 0,
uc->hook_callbacks[uc->hook_mem_fetch_idx].user_data)) {
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_FETCH_UNMAPPED) {
if (!HOOK_BOUND_CHECK(hook, addr))
continue;
if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_FETCH_UNMAPPED, addr, DATA_SIZE, 0, hook->user_data)))
break;
}
#else
error_code = UC_ERR_READ_UNMAPPED;
if (uc->hook_mem_read_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_read_idx].callback)(
uc, UC_MEM_READ_UNMAPPED, addr, DATA_SIZE, 0,
uc->hook_callbacks[uc->hook_mem_read_idx].user_data)) {
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_UNMAPPED) {
if (!HOOK_BOUND_CHECK(hook, addr))
continue;
if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_READ_UNMAPPED, addr, DATA_SIZE, 0, hook->user_data)))
break;
}
#endif
if (handled) {
env->invalid_error = UC_ERR_OK;
mr = memory_mapping(uc, addr); // FIXME: what if mr is still NULL at this time?
} else {
@ -399,9 +431,15 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
#if defined(SOFTMMU_CODE_ACCESS)
// Unicorn: callback on fetch from NX
if (mr != NULL && !(mr->perms & UC_PROT_EXEC)) { // non-executable
if (uc->hook_mem_fetch_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].callback)(
uc, UC_MEM_FETCH_PROT, addr, DATA_SIZE, 0,
uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].user_data)) {
handled = false;
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_FETCH_PROT) {
if (!HOOK_BOUND_CHECK(hook, addr))
continue;
if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_FETCH_PROT, addr, DATA_SIZE, 0, hook->user_data)))
break;
}
if (handled) {
env->invalid_error = UC_ERR_OK;
} else {
env->invalid_addr = addr;
@ -414,19 +452,25 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
#endif
// Unicorn: callback on memory read
if (READ_ACCESS_TYPE == MMU_DATA_LOAD && env->uc->hook_mem_read) {
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_MEM_READ, addr);
if (trace) {
((uc_cb_hookmem_t)trace->callback)(env->uc, UC_MEM_READ,
(uint64_t)addr, (int)DATA_SIZE, (int64_t)0, trace->user_data);
if (READ_ACCESS_TYPE == MMU_DATA_LOAD) {
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ) {
if (!HOOK_BOUND_CHECK(hook, addr))
continue;
((uc_cb_hookmem_t)hook->callback)(env->uc, UC_MEM_READ, addr, DATA_SIZE, 0, hook->user_data);
}
}
// Unicorn: callback on non-readable memory
if (READ_ACCESS_TYPE == MMU_DATA_LOAD && mr != NULL && !(mr->perms & UC_PROT_READ)) { //non-readable
if (uc->hook_mem_read_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_read_prot_idx].callback)(
uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0,
uc->hook_callbacks[uc->hook_mem_read_prot_idx].user_data)) {
handled = false;
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_PROT) {
if (!HOOK_BOUND_CHECK(hook, addr))
continue;
if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0, hook->user_data)))
break;
}
if (handled) {
env->invalid_error = UC_ERR_OK;
} else {
env->invalid_addr = addr;
@ -595,24 +639,30 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
uintptr_t haddr;
struct hook *hook;
bool handled;
struct uc_struct *uc = env->uc;
MemoryRegion *mr = memory_mapping(uc, addr);
// Unicorn: callback on memory write
if (uc->hook_mem_write) {
struct hook_struct *trace = hook_find(uc, UC_HOOK_MEM_WRITE, addr);
if (trace) {
((uc_cb_hookmem_t)trace->callback)(uc, UC_MEM_WRITE,
(uint64_t)addr, (int)DATA_SIZE, (int64_t)val, trace->user_data);
}
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE) {
if (!HOOK_BOUND_CHECK(hook, addr))
continue;
((uc_cb_hookmem_t)hook->callback)(uc, UC_MEM_WRITE, addr, DATA_SIZE, val, hook->user_data);
}
// Unicorn: callback on invalid memory
if (uc->hook_mem_write_idx && mr == NULL) {
if (!((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_idx].callback)(
uc, UC_MEM_WRITE_UNMAPPED, addr, DATA_SIZE, (int64_t)val,
uc->hook_callbacks[uc->hook_mem_write_idx].user_data)) {
if (mr == NULL) {
handled = false;
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE_UNMAPPED) {
if (!HOOK_BOUND_CHECK(hook, addr))
continue;
if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_WRITE_UNMAPPED, addr, DATA_SIZE, val, hook->user_data)))
break;
}
if (!handled) {
// save error & quit
env->invalid_addr = addr;
env->invalid_error = UC_ERR_WRITE_UNMAPPED;
@ -627,12 +677,17 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
// Unicorn: callback on non-writable memory
if (mr != NULL && !(mr->perms & UC_PROT_WRITE)) { //non-writable
if (uc->hook_mem_write_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_prot_idx].callback)(
uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, (int64_t)val,
uc->hook_callbacks[uc->hook_mem_write_prot_idx].user_data)) {
env->invalid_error = UC_ERR_OK;
handled = false;
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE_PROT) {
if (!HOOK_BOUND_CHECK(hook, addr))
continue;
if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, val, hook->user_data)))
break;
}
else {
if (handled) {
env->invalid_error = UC_ERR_OK;
} else {
env->invalid_addr = addr;
env->invalid_error = UC_ERR_WRITE_PROT;
// printf("***** Invalid memory write (ro) at " TARGET_FMT_lx "\n", addr);
@ -742,24 +797,30 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
uintptr_t haddr;
struct hook *hook;
bool handled;
struct uc_struct *uc = env->uc;
MemoryRegion *mr = memory_mapping(uc, addr);
// Unicorn: callback on memory write
if (uc->hook_mem_write) {
struct hook_struct *trace = hook_find(uc, UC_HOOK_MEM_WRITE, addr);
if (trace) {
((uc_cb_hookmem_t)trace->callback)(uc, UC_MEM_WRITE,
(uint64_t)addr, (int)DATA_SIZE, (int64_t)val, trace->user_data);
}
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE) {
if (!HOOK_BOUND_CHECK(hook, addr))
continue;
((uc_cb_hookmem_t)hook->callback)(uc, UC_MEM_WRITE, addr, DATA_SIZE, val, hook->user_data);
}
// Unicorn: callback on invalid memory
if (uc->hook_mem_write_idx && mr == NULL) {
if (!((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_idx].callback)(
uc, UC_MEM_WRITE_UNMAPPED, addr, DATA_SIZE, (int64_t)val,
uc->hook_callbacks[uc->hook_mem_write_idx].user_data)) {
if (mr == NULL) {
handled = false;
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE_UNMAPPED) {
if (!HOOK_BOUND_CHECK(hook, addr))
continue;
if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_WRITE_UNMAPPED, addr, DATA_SIZE, val, hook->user_data)))
break;
}
if (!handled) {
// save error & quit
env->invalid_addr = addr;
env->invalid_error = UC_ERR_WRITE_UNMAPPED;
@ -774,12 +835,17 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
// Unicorn: callback on non-writable memory
if (mr != NULL && !(mr->perms & UC_PROT_WRITE)) { //non-writable
if (uc->hook_mem_write_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_prot_idx].callback)(
uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, (int64_t)val,
uc->hook_callbacks[uc->hook_mem_write_prot_idx].user_data)) {
env->invalid_error = UC_ERR_OK;
handled = false;
HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE_PROT) {
if (!HOOK_BOUND_CHECK(hook, addr))
continue;
if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, val, hook->user_data)))
break;
}
else {
if (handled) {
env->invalid_error = UC_ERR_OK;
} else {
env->invalid_addr = addr;
env->invalid_error = UC_ERR_WRITE_PROT;
// printf("***** Invalid memory write (ro) at " TARGET_FMT_lx "\n", addr);

View file

@ -1,6 +1,7 @@
/* Autogen header for Unicorn Engine - DONOT MODIFY */
#ifndef UNICORN_AUTOGEN_SPARC_H
#define UNICORN_AUTOGEN_SPARC_H
#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_sparc
#define use_idiv_instructions_rt use_idiv_instructions_rt_sparc
#define tcg_target_deposit_valid tcg_target_deposit_valid_sparc
#define helper_power_down helper_power_down_sparc

View file

@ -1,6 +1,7 @@
/* Autogen header for Unicorn Engine - DONOT MODIFY */
#ifndef UNICORN_AUTOGEN_SPARC64_H
#define UNICORN_AUTOGEN_SPARC64_H
#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_sparc64
#define use_idiv_instructions_rt use_idiv_instructions_rt_sparc64
#define tcg_target_deposit_valid tcg_target_deposit_valid_sparc64
#define helper_power_down helper_power_down_sparc64

View file

@ -1,4 +1,4 @@
DEF_HELPER_5(uc_tracecode, void, i32, ptr, ptr, i64, ptr)
DEF_HELPER_4(uc_tracecode, void, i32, i32, ptr, i64)
DEF_HELPER_FLAGS_1(clz_arm, TCG_CALL_NO_RWG_SE, i32, i32)

View file

@ -10984,10 +10984,8 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s)
s->pc += 4;
// Unicorn: trace this instruction on request
if (env->uc->hook_insn) {
struct hook_struct *trace = hook_find(s->uc, UC_HOOK_CODE, s->pc - 4);
if (trace)
gen_uc_tracecode(tcg_ctx, 4, trace->callback, env->uc, s->pc - 4, trace->user_data);
if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, s->pc - 4)) {
gen_uc_tracecode(tcg_ctx, 4, UC_HOOK_CODE_IDX, env->uc, s->pc - 4);
// the callback might want to stop emulation immediately
check_exit_request(tcg_ctx);
}
@ -11114,13 +11112,10 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu,
// Unicorn: trace this block on request
// Only hook this block if it is not broken from previous translation due to
// full translation cache
if (env->uc->hook_block && !env->uc->block_full) {
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start);
if (trace) {
// save block address to see if we need to patch block size later
env->uc->block_addr = pc_start;
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data);
}
if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) {
// save block address to see if we need to patch block size later
env->uc->block_addr = pc_start;
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start);
}
gen_tb_start(tcg_ctx);

View file

@ -7687,10 +7687,8 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) // qq
}
// Unicorn: trace this instruction on request
if (s->uc->hook_insn) {
struct hook_struct *trace = hook_find(s->uc, UC_HOOK_CODE, s->pc - 4);
if (trace)
gen_uc_tracecode(tcg_ctx, 4, trace->callback, s->uc, s->pc - 4, trace->user_data);
if (HOOK_EXISTS_BOUNDED(s->uc, UC_HOOK_CODE, s->pc - 4)) {
gen_uc_tracecode(tcg_ctx, 4, UC_HOOK_CODE_IDX, s->uc, s->pc - 4);
// the callback might want to stop emulation immediately
check_exit_request(tcg_ctx);
}
@ -10408,15 +10406,10 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) // qq
}
// Unicorn: trace this instruction on request
if (env->uc->hook_insn) {
struct hook_struct *trace = hook_find(s->uc, UC_HOOK_CODE, s->pc);
if (trace)
gen_uc_tracecode(tcg_ctx, 2, trace->callback, env->uc, s->pc, trace->user_data);
// if requested to emulate only some instructions, check to see
// if we need to exit immediately
if (env->uc->emu_count > 0) {
check_exit_request(tcg_ctx);
}
if (HOOK_EXISTS_BOUNDED(s->uc, UC_HOOK_CODE, s->pc)) {
gen_uc_tracecode(tcg_ctx, 2, UC_HOOK_CODE_IDX, s->uc, s->pc);
// the callback might want to stop emulation immediately
check_exit_request(tcg_ctx);
}
insn = arm_lduw_code(env, s->pc, s->bswap_code);
@ -11237,13 +11230,10 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
// Unicorn: trace this block on request
// Only hook this block if it is not broken from previous translation due to
// full translation cache
if (env->uc->hook_block && !env->uc->block_full) {
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start);
if (trace) {
// save block address to see if we need to patch block size later
env->uc->block_addr = pc_start;
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data);
}
if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) {
// save block address to see if we need to patch block size later
env->uc->block_addr = pc_start;
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start);
}
gen_tb_start(tcg_ctx);

View file

@ -3,21 +3,11 @@
#include "hw/boards.h"
#include "hw/arm/arm.h"
#include "sysemu/cpus.h"
#include "unicorn.h"
#include "cpu.h"
#include "unicorn_common.h"
#define READ_QWORD(x) ((uint64)x)
#define READ_DWORD(x) (x & 0xffffffff)
#define READ_WORD(x) (x & 0xffff)
#define READ_BYTE_H(x) ((x & 0xffff) >> 8)
#define READ_BYTE_L(x) (x & 0xff)
#include "uc_priv.h"
static void arm64_set_pc(struct uc_struct *uc, uint64_t address)
@ -60,11 +50,6 @@ int arm64_reg_read(struct uc_struct *uc, unsigned int regid, void *value)
return 0;
}
#define WRITE_DWORD(x, w) (x = (x & ~0xffffffff) | (w & 0xffffffff))
#define WRITE_WORD(x, w) (x = (x & ~0xffff) | (w & 0xffff))
#define WRITE_BYTE_H(x, b) (x = (x & ~0xff00) | (b & 0xff))
#define WRITE_BYTE_L(x, b) (x = (x & ~0xff) | (b & 0xff))
int arm64_reg_write(struct uc_struct *uc, unsigned int regid, const void *value)
{
CPUState *mycpu = first_cpu;
@ -82,6 +67,9 @@ int arm64_reg_write(struct uc_struct *uc, unsigned int regid, const void *value)
break;
case UC_ARM64_REG_PC:
ARM_CPU(uc, mycpu)->env.pc = *(uint64_t *)value;
// force to quit execution and flush TB
uc->quit_request = true;
uc_emu_stop(uc);
break;
case UC_ARM64_REG_SP:
ARM_CPU(uc, mycpu)->env.xregs[31] = *(uint64_t *)value;

View file

@ -3,20 +3,11 @@
#include "hw/boards.h"
#include "hw/arm/arm.h"
#include "sysemu/cpus.h"
#include "unicorn.h"
#include "cpu.h"
#include "unicorn_common.h"
#define READ_QWORD(x) ((uint64)x)
#define READ_DWORD(x) (x & 0xffffffff)
#define READ_WORD(x) (x & 0xffff)
#define READ_BYTE_H(x) ((x & 0xffff) >> 8)
#define READ_BYTE_L(x) (x & 0xff)
#include "uc_priv.h"
static void arm_set_pc(struct uc_struct *uc, uint64_t address)
@ -42,73 +33,57 @@ int arm_reg_read(struct uc_struct *uc, unsigned int regid, void *value)
mycpu = first_cpu;
switch(uc->mode) {
default:
break;
case UC_MODE_ARM:
case UC_MODE_THUMB:
if (regid >= UC_ARM_REG_R0 && regid <= UC_ARM_REG_R12)
*(int32_t *)value = ARM_CPU(uc, mycpu)->env.regs[regid - UC_ARM_REG_R0];
else {
switch(regid) {
case UC_ARM_REG_CPSR:
*(int32_t *)value = cpsr_read(&ARM_CPU(uc, mycpu)->env);
break;
//case UC_ARM_REG_SP:
case UC_ARM_REG_R13:
*(int32_t *)value = ARM_CPU(uc, mycpu)->env.regs[13];
break;
//case UC_ARM_REG_LR:
case UC_ARM_REG_R14:
*(int32_t *)value = ARM_CPU(uc, mycpu)->env.regs[14];
break;
//case UC_ARM_REG_PC:
case UC_ARM_REG_R15:
*(int32_t *)value = ARM_CPU(uc, mycpu)->env.regs[15];
break;
}
}
break;
if (regid >= UC_ARM_REG_R0 && regid <= UC_ARM_REG_R12)
*(int32_t *)value = ARM_CPU(uc, mycpu)->env.regs[regid - UC_ARM_REG_R0];
else {
switch(regid) {
case UC_ARM_REG_CPSR:
*(int32_t *)value = cpsr_read(&ARM_CPU(uc, mycpu)->env);
break;
//case UC_ARM_REG_SP:
case UC_ARM_REG_R13:
*(int32_t *)value = ARM_CPU(uc, mycpu)->env.regs[13];
break;
//case UC_ARM_REG_LR:
case UC_ARM_REG_R14:
*(int32_t *)value = ARM_CPU(uc, mycpu)->env.regs[14];
break;
//case UC_ARM_REG_PC:
case UC_ARM_REG_R15:
*(int32_t *)value = ARM_CPU(uc, mycpu)->env.regs[15];
break;
}
}
return 0;
}
#define WRITE_DWORD(x, w) (x = (x & ~0xffffffff) | (w & 0xffffffff))
#define WRITE_WORD(x, w) (x = (x & ~0xffff) | (w & 0xffff))
#define WRITE_BYTE_H(x, b) (x = (x & ~0xff00) | (b & 0xff))
#define WRITE_BYTE_L(x, b) (x = (x & ~0xff) | (b & 0xff))
int arm_reg_write(struct uc_struct *uc, unsigned int regid, const void *value)
{
CPUState *mycpu = first_cpu;
switch(uc->mode) {
default:
break;
if (regid >= UC_ARM_REG_R0 && regid <= UC_ARM_REG_R12)
ARM_CPU(uc, mycpu)->env.regs[regid - UC_ARM_REG_R0] = *(uint32_t *)value;
else {
switch(regid) {
//case UC_ARM_REG_SP:
case UC_ARM_REG_R13:
ARM_CPU(uc, mycpu)->env.regs[13] = *(uint32_t *)value;
break;
//case UC_ARM_REG_LR:
case UC_ARM_REG_R14:
ARM_CPU(uc, mycpu)->env.regs[14] = *(uint32_t *)value;
break;
//case UC_ARM_REG_PC:
case UC_ARM_REG_R15:
ARM_CPU(uc, mycpu)->env.pc = *(uint32_t *)value;
ARM_CPU(uc, mycpu)->env.regs[15] = *(uint32_t *)value;
// force to quit execution and flush TB
uc->quit_request = true;
uc_emu_stop(uc);
case UC_MODE_ARM:
case UC_MODE_THUMB:
if (regid >= UC_ARM_REG_R0 && regid <= UC_ARM_REG_R12)
ARM_CPU(uc, mycpu)->env.regs[regid - UC_ARM_REG_R0] = *(uint32_t *)value;
else {
switch(regid) {
//case UC_ARM_REG_SP:
case UC_ARM_REG_R13:
ARM_CPU(uc, mycpu)->env.regs[13] = *(uint32_t *)value;
break;
//case UC_ARM_REG_LR:
case UC_ARM_REG_R14:
ARM_CPU(uc, mycpu)->env.regs[14] = *(uint32_t *)value;
break;
//case UC_ARM_REG_PC:
case UC_ARM_REG_R15:
ARM_CPU(uc, mycpu)->env.regs[15] = *(uint32_t *)value;
break;
}
}
break;
break;
}
}
return 0;
@ -124,6 +99,24 @@ static bool arm_stop_interrupt(int intno)
}
}
static uc_err arm_query(struct uc_struct *uc, uc_query_type type, size_t *result)
{
CPUState *mycpu = first_cpu;
uint32_t mode;
switch(type) {
case UC_QUERY_MODE:
// zero out ARM/THUMB mode
mode = uc->mode & ~(UC_MODE_ARM | UC_MODE_THUMB);
// THUMB mode or ARM MOde
mode += ((ARM_CPU(uc, mycpu)->env.thumb != 0)? UC_MODE_THUMB : UC_MODE_ARM);
*result = mode;
return UC_ERR_OK;
default:
return UC_ERR_ARG;
}
}
void arm_uc_init(struct uc_struct* uc)
{
register_accel_types(uc);
@ -134,5 +127,6 @@ void arm_uc_init(struct uc_struct* uc)
uc->reg_reset = arm_reg_reset;
uc->set_pc = arm_set_pc;
uc->stop_interrupt = arm_stop_interrupt;
uc->query = arm_query;
uc_common_init(uc);
}

View file

@ -1,4 +1,4 @@
DEF_HELPER_5(uc_tracecode, void, i32, ptr, ptr, i64, ptr)
DEF_HELPER_4(uc_tracecode, void, i32, i32, ptr, i64)
DEF_HELPER_FLAGS_4(cc_compute_all, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int)
DEF_HELPER_FLAGS_4(cc_compute_c, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int)

View file

@ -945,14 +945,16 @@ void helper_syscall(CPUX86State *env, int next_eip_addend)
#else
void helper_syscall(CPUX86State *env, int next_eip_addend)
{
// Unicorn: call interrupt callback if registered
struct uc_struct *uc = env->uc;
if (uc->hook_syscall_idx) {
((uc_cb_insn_syscall_t)uc->hook_callbacks[uc->hook_syscall_idx].callback)(
uc, uc->hook_callbacks[uc->hook_syscall_idx].user_data);
// Unicorn: call registered syscall hooks
struct hook *hook;
HOOK_FOREACH(env->uc, hook, UC_HOOK_INSN) {
if (!HOOK_BOUND_CHECK(hook, env->eip))
continue;
if (hook->insn == UC_X86_INS_SYSCALL)
((uc_cb_insn_syscall_t)hook->callback)(env->uc, hook->user_data);
}
env->eip += next_eip_addend;
env->eip += next_eip_addend;
return;
int selector;
@ -2303,14 +2305,16 @@ void helper_lret_protected(CPUX86State *env, int shift, int addend)
void helper_sysenter(CPUX86State *env, int next_eip_addend)
{
// Unicorn: call interrupt callback if registered
struct uc_struct *uc = env->uc;
if (uc->hook_syscall_idx) {
((uc_cb_insn_syscall_t)uc->hook_callbacks[uc->hook_syscall_idx].callback)(
uc, uc->hook_callbacks[uc->hook_syscall_idx].user_data);
// Unicorn: call registered SYSENTER hooks
struct hook *hook;
HOOK_FOREACH(env->uc, hook, UC_HOOK_INSN) {
if (!HOOK_BOUND_CHECK(hook, env->eip))
continue;
if (hook->insn == UC_X86_INS_SYSENTER)
((uc_cb_insn_syscall_t)hook->callback)(env->uc, hook->user_data);
}
env->eip += next_eip_addend;
env->eip += next_eip_addend;
return;
if (env->sysenter_cs == 0) {

View file

@ -516,14 +516,14 @@ static inline void gen_op_addq_A0_reg_sN(TCGContext *s, int shift, int reg)
static inline void gen_op_ld_v(DisasContext *s, int idx, TCGv t0, TCGv a0)
{
if (s->uc->hook_mem_read)
if (HOOK_EXISTS(s->uc, UC_HOOK_MEM_READ))
gen_jmp_im(s, s->prev_pc); // Unicorn: sync EIP
tcg_gen_qemu_ld_tl(s->uc, t0, a0, s->mem_index, idx | MO_LE);
}
static inline void gen_op_st_v(DisasContext *s, int idx, TCGv t0, TCGv a0)
{
if (s->uc->hook_mem_write)
if (HOOK_EXISTS(s->uc, UC_HOOK_MEM_WRITE))
gen_jmp_im(s, s->prev_pc); // Unicorn: sync EIP
tcg_gen_qemu_st_tl(s->uc, t0, a0, s->mem_index, idx | MO_LE);
}
@ -4721,6 +4721,17 @@ static void sync_eflags(DisasContext *s, TCGContext *tcg_ctx)
tcg_gen_st_tl(tcg_ctx, *cpu_T[0], cpu_env, offsetof(CPUX86State, eflags));
}
static void restore_eflags(DisasContext *s, TCGContext *tcg_ctx)
{
TCGv **cpu_T = (TCGv **)tcg_ctx->cpu_T;
TCGv_ptr cpu_env = tcg_ctx->cpu_env;
tcg_gen_ld_tl(tcg_ctx, *cpu_T[0], cpu_env, offsetof(CPUX86State, eflags));
gen_helper_write_eflags(tcg_ctx, cpu_env, *cpu_T[0],
tcg_const_i32(tcg_ctx, (TF_MASK | AC_MASK | ID_MASK | NT_MASK) & 0xffff));
set_cc_op(s, CC_OP_EFLAGS);
}
/* convert one instruction. s->is_jmp is set if the translation must
be stopped. Return the next pc value */
static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
@ -4745,12 +4756,10 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
TCGv cpu_tmp4 = *(TCGv *)tcg_ctx->cpu_tmp4;
TCGv **cpu_T = (TCGv **)tcg_ctx->cpu_T;
TCGv **cpu_regs = (TCGv **)tcg_ctx->cpu_regs;
struct hook_struct *trace = NULL;
TCGArg *save_opparam_ptr = tcg_ctx->gen_opparam_ptr;
bool cc_op_dirty = s->cc_op_dirty;
bool changed_cc_op = false;
s->pc = pc_start;
// end address tells us to stop emulation
@ -4768,19 +4777,16 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
}
// Unicorn: trace this instruction on request
if (env->uc->hook_insn) {
trace = hook_find(env->uc, UC_HOOK_CODE, pc_start);
if (trace) {
if (s->last_cc_op != s->cc_op) {
sync_eflags(s, tcg_ctx);
s->last_cc_op = s->cc_op;
changed_cc_op = true;
}
// generate code to call callback
gen_uc_tracecode(tcg_ctx, 0xf1f1f1f1, trace->callback, env->uc, pc_start, trace->user_data);
// the callback might want to stop emulation immediately
check_exit_request(tcg_ctx);
if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, pc_start)) {
if (s->last_cc_op != s->cc_op) {
sync_eflags(s, tcg_ctx);
s->last_cc_op = s->cc_op;
changed_cc_op = true;
}
gen_uc_tracecode(tcg_ctx, 0xf1f1f1f1, UC_HOOK_CODE_IDX, env->uc, pc_start);
restore_eflags(s, tcg_ctx);
// the callback might want to stop emulation immediately
check_exit_request(tcg_ctx);
}
prefixes = 0;
@ -8173,7 +8179,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
gen_helper_unlock(tcg_ctx, cpu_env);
// Unicorn: patch the callback for the instruction size
if (trace) {
if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, pc_start)) {
// int i;
// for(i = 0; i < 20; i++)
// printf("=== [%u] = %x\n", i, *(save_opparam_ptr + i));
@ -8387,12 +8393,9 @@ static inline void gen_intermediate_code_internal(uint8_t *gen_opc_cc_op,
// Unicorn: trace this block on request
// Only hook this block if it is not broken from previous translation due to
// full translation cache
if (env->uc->hook_block && !env->uc->block_full) {
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start);
if (trace) {
env->uc->block_addr = pc_start;
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data);
}
if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) {
env->uc->block_addr = pc_start;
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start);
}
gen_tb_start(tcg_ctx);

View file

@ -2,19 +2,14 @@
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2015 */
#include "hw/boards.h"
#include "sysemu/cpus.h"
#include "hw/i386/pc.h"
#include "sysemu/cpus.h"
#include "unicorn.h"
#include "cpu.h"
#include "tcg.h"
#include "unicorn_common.h"
#define READ_QWORD(x) ((uint64)x)
#define READ_DWORD(x) (x & 0xffffffff)
#define READ_WORD(x) (x & 0xffff)
#define READ_BYTE_H(x) ((x & 0xffff) >> 8)
#define READ_BYTE_L(x) (x & 0xff)
#include <unicorn/x86.h> /* needed for uc_x86_mmr */
#include "uc_priv.h"
static void x86_set_pc(struct uc_struct *uc, uint64_t address)
@ -145,6 +140,58 @@ int x86_reg_read(struct uc_struct *uc, unsigned int regid, void *value)
{
CPUState *mycpu = first_cpu;
switch(regid) {
default:
break;
case UC_X86_REG_FP0 ... UC_X86_REG_FP7:
{
floatx80 reg = X86_CPU(uc, mycpu)->env.fpregs[regid - UC_X86_REG_FP0].d;
cpu_get_fp80(value, value+sizeof(uint64_t), reg);
}
break;
case UC_X86_REG_FPSW:
{
uint16_t fpus = X86_CPU(uc, mycpu)->env.fpus;
fpus = fpus & ~0x3800;
fpus |= ( X86_CPU(uc, mycpu)->env.fpstt & 0x7 ) << 11;
*(uint16_t*) value = fpus;
}
break;
case UC_X86_REG_FPCW:
*(uint16_t*) value = X86_CPU(uc, mycpu)->env.fpuc;
break;
case UC_X86_REG_FPTAG:
{
#define EXPD(fp) (fp.l.upper & 0x7fff)
#define MANTD(fp) (fp.l.lower)
#define MAXEXPD 0x7fff
int fptag, exp, i;
uint64_t mant;
CPU_LDoubleU tmp;
fptag = 0;
for (i = 7; i >= 0; i--) {
fptag <<= 2;
if (X86_CPU(uc, mycpu)->env.fptags[i]) {
fptag |= 3;
} else {
tmp.d = X86_CPU(uc, mycpu)->env.fpregs[i].d;
exp = EXPD(tmp);
mant = MANTD(tmp);
if (exp == 0 && mant == 0) {
/* zero */
fptag |= 1;
} else if (exp == 0 || exp == MAXEXPD
|| (mant & (1LL << 63)) == 0) {
/* NaNs, infinity, denormal */
fptag |= 2;
}
}
}
*(uint16_t*) value = fptag;
}
break;
}
switch(uc->mode) {
default:
break;
@ -260,22 +307,42 @@ int x86_reg_read(struct uc_struct *uc, unsigned int regid, void *value)
*(int16_t *)value = READ_WORD(X86_CPU(uc, mycpu)->env.eip);
break;
case UC_X86_REG_CS:
*(int32_t *)value = X86_CPU(uc, mycpu)->env.segs[R_CS].base;
*(int16_t *)value = (uint16_t)X86_CPU(uc, mycpu)->env.segs[R_CS].selector;
break;
case UC_X86_REG_DS:
*(int32_t *)value = X86_CPU(uc, mycpu)->env.segs[R_DS].base;
*(int16_t *)value = (uint16_t)X86_CPU(uc, mycpu)->env.segs[R_DS].selector;
break;
case UC_X86_REG_SS:
*(int32_t *)value = X86_CPU(uc, mycpu)->env.segs[R_SS].base;
*(int16_t *)value = (uint16_t)X86_CPU(uc, mycpu)->env.segs[R_SS].selector;
break;
case UC_X86_REG_ES:
*(int32_t *)value = X86_CPU(uc, mycpu)->env.segs[R_ES].base;
*(int16_t *)value = (uint16_t)X86_CPU(uc, mycpu)->env.segs[R_ES].selector;
break;
case UC_X86_REG_FS:
*(int32_t *)value = X86_CPU(uc, mycpu)->env.segs[R_FS].base;
*(int16_t *)value = (uint16_t)X86_CPU(uc, mycpu)->env.segs[R_FS].selector;
break;
case UC_X86_REG_GS:
*(int32_t *)value = X86_CPU(uc, mycpu)->env.segs[R_GS].base;
*(int16_t *)value = (uint16_t)X86_CPU(uc, mycpu)->env.segs[R_GS].selector;
break;
case UC_X86_REG_IDTR:
((uc_x86_mmr *)value)->limit = (uint16_t)X86_CPU(uc, mycpu)->env.idt.limit;
((uc_x86_mmr *)value)->base = (uint32_t)X86_CPU(uc, mycpu)->env.idt.base;
break;
case UC_X86_REG_GDTR:
((uc_x86_mmr *)value)->limit = (uint16_t)X86_CPU(uc, mycpu)->env.gdt.limit;
((uc_x86_mmr *)value)->base = (uint32_t)X86_CPU(uc, mycpu)->env.gdt.base;
break;
case UC_X86_REG_LDTR:
((uc_x86_mmr *)value)->limit = X86_CPU(uc, mycpu)->env.ldt.limit;
((uc_x86_mmr *)value)->base = (uint32_t)X86_CPU(uc, mycpu)->env.ldt.base;
((uc_x86_mmr *)value)->selector = (uint16_t)X86_CPU(uc, mycpu)->env.ldt.selector;
((uc_x86_mmr *)value)->flags = X86_CPU(uc, mycpu)->env.ldt.flags;
break;
case UC_X86_REG_TR:
((uc_x86_mmr *)value)->limit = X86_CPU(uc, mycpu)->env.tr.limit;
((uc_x86_mmr *)value)->base = (uint32_t)X86_CPU(uc, mycpu)->env.tr.base;
((uc_x86_mmr *)value)->selector = (uint16_t)X86_CPU(uc, mycpu)->env.tr.selector;
((uc_x86_mmr *)value)->flags = X86_CPU(uc, mycpu)->env.tr.flags;
break;
}
break;
@ -412,22 +479,22 @@ int x86_reg_read(struct uc_struct *uc, unsigned int regid, void *value)
*(int16_t *)value = READ_WORD(X86_CPU(uc, mycpu)->env.eip);
break;
case UC_X86_REG_CS:
*(int64_t *)value = X86_CPU(uc, mycpu)->env.segs[R_CS].base;
*(int16_t *)value = (uint16_t)X86_CPU(uc, mycpu)->env.segs[R_CS].selector;
break;
case UC_X86_REG_DS:
*(int64_t *)value = X86_CPU(uc, mycpu)->env.segs[R_DS].base;
*(int16_t *)value = (uint16_t)X86_CPU(uc, mycpu)->env.segs[R_DS].selector;
break;
case UC_X86_REG_SS:
*(int64_t *)value = X86_CPU(uc, mycpu)->env.segs[R_SS].base;
*(int16_t *)value = (uint16_t)X86_CPU(uc, mycpu)->env.segs[R_SS].selector;
break;
case UC_X86_REG_ES:
*(int64_t *)value = X86_CPU(uc, mycpu)->env.segs[R_ES].base;
*(int16_t *)value = (uint16_t)X86_CPU(uc, mycpu)->env.segs[R_ES].selector;
break;
case UC_X86_REG_FS:
*(int64_t *)value = X86_CPU(uc, mycpu)->env.segs[R_FS].base;
*(int16_t *)value = (uint16_t)X86_CPU(uc, mycpu)->env.segs[R_FS].selector;
break;
case UC_X86_REG_GS:
*(int64_t *)value = X86_CPU(uc, mycpu)->env.segs[R_GS].base;
*(int16_t *)value = (uint16_t)X86_CPU(uc, mycpu)->env.segs[R_GS].selector;
break;
case UC_X86_REG_R8:
*(int64_t *)value = READ_QWORD(X86_CPU(uc, mycpu)->env.regs[8]);
@ -525,6 +592,26 @@ int x86_reg_read(struct uc_struct *uc, unsigned int regid, void *value)
case UC_X86_REG_R15B:
*(int8_t *)value = READ_BYTE_L(X86_CPU(uc, mycpu)->env.regs[15]);
break;
case UC_X86_REG_IDTR:
((uc_x86_mmr *)value)->limit = (uint16_t)X86_CPU(uc, mycpu)->env.idt.limit;
((uc_x86_mmr *)value)->base = X86_CPU(uc, mycpu)->env.idt.base;
break;
case UC_X86_REG_GDTR:
((uc_x86_mmr *)value)->limit = (uint16_t)X86_CPU(uc, mycpu)->env.gdt.limit;
((uc_x86_mmr *)value)->base = X86_CPU(uc, mycpu)->env.gdt.base;
break;
case UC_X86_REG_LDTR:
((uc_x86_mmr *)value)->limit = X86_CPU(uc, mycpu)->env.ldt.limit;
((uc_x86_mmr *)value)->base = X86_CPU(uc, mycpu)->env.ldt.base;
((uc_x86_mmr *)value)->selector = (uint16_t)X86_CPU(uc, mycpu)->env.ldt.selector;
((uc_x86_mmr *)value)->flags = X86_CPU(uc, mycpu)->env.ldt.flags;
break;
case UC_X86_REG_TR:
((uc_x86_mmr *)value)->limit = X86_CPU(uc, mycpu)->env.tr.limit;
((uc_x86_mmr *)value)->base = X86_CPU(uc, mycpu)->env.tr.base;
((uc_x86_mmr *)value)->selector = (uint16_t)X86_CPU(uc, mycpu)->env.tr.selector;
((uc_x86_mmr *)value)->flags = X86_CPU(uc, mycpu)->env.tr.flags;
break;
}
break;
#endif
@ -534,16 +621,42 @@ int x86_reg_read(struct uc_struct *uc, unsigned int regid, void *value)
return 0;
}
#define WRITE_DWORD(x, w) (x = (x & ~0xffffffff) | (w & 0xffffffff))
#define WRITE_WORD(x, w) (x = (x & ~0xffff) | (w & 0xffff))
#define WRITE_BYTE_H(x, b) (x = (x & ~0xff00) | (b & 0xff))
#define WRITE_BYTE_L(x, b) (x = (x & ~0xff) | (b & 0xff))
int x86_reg_write(struct uc_struct *uc, unsigned int regid, const void *value)
{
CPUState *mycpu = first_cpu;
switch(regid) {
default:
break;
case UC_X86_REG_FP0 ... UC_X86_REG_FP7:
{
uint64_t mant = *(uint64_t*) value;
uint16_t upper = *(uint16_t*) (value + sizeof(uint64_t));
X86_CPU(uc, mycpu)->env.fpregs[regid - UC_X86_REG_FP0].d = cpu_set_fp80(mant, upper);
}
break;
case UC_X86_REG_FPSW:
{
uint16_t fpus = *(uint16_t*) value;
X86_CPU(uc, mycpu)->env.fpus = fpus & ~0x3800;
X86_CPU(uc, mycpu)->env.fpstt = (fpus >> 11) & 0x7;
}
break;
case UC_X86_REG_FPCW:
X86_CPU(uc, mycpu)->env.fpuc = *(uint16_t *)value;
break;
case UC_X86_REG_FPTAG:
{
int i;
uint16_t fptag = *(uint16_t*) value;
for (i = 0; i < 8; i++) {
X86_CPU(uc, mycpu)->env.fptags[i] = ((fptag & 3) == 3);
fptag >>= 2;
}
}
break;
}
switch(uc->mode) {
default:
break;
@ -656,27 +769,53 @@ int x86_reg_write(struct uc_struct *uc, unsigned int regid, const void *value)
break;
case UC_X86_REG_EIP:
X86_CPU(uc, mycpu)->env.eip = *(uint32_t *)value;
// force to quit execution and flush TB
uc->quit_request = true;
uc_emu_stop(uc);
break;
case UC_X86_REG_IP:
WRITE_WORD(X86_CPU(uc, mycpu)->env.eip, *(uint16_t *)value);
// force to quit execution and flush TB
uc->quit_request = true;
uc_emu_stop(uc);
break;
case UC_X86_REG_CS:
X86_CPU(uc, mycpu)->env.segs[R_CS].base = *(uint32_t *)value;
X86_CPU(uc, mycpu)->env.segs[R_CS].selector = *(uint16_t *)value;
break;
case UC_X86_REG_DS:
X86_CPU(uc, mycpu)->env.segs[R_DS].base = *(uint32_t *)value;
X86_CPU(uc, mycpu)->env.segs[R_DS].selector = *(uint16_t *)value;
break;
case UC_X86_REG_SS:
X86_CPU(uc, mycpu)->env.segs[R_SS].base = *(uint32_t *)value;
X86_CPU(uc, mycpu)->env.segs[R_SS].selector = *(uint16_t *)value;
break;
case UC_X86_REG_ES:
X86_CPU(uc, mycpu)->env.segs[R_ES].base = *(uint32_t *)value;
X86_CPU(uc, mycpu)->env.segs[R_ES].selector = *(uint16_t *)value;
break;
case UC_X86_REG_FS:
X86_CPU(uc, mycpu)->env.segs[R_FS].base = *(uint32_t *)value;
X86_CPU(uc, mycpu)->env.segs[R_FS].selector = *(uint16_t *)value;
break;
case UC_X86_REG_GS:
X86_CPU(uc, mycpu)->env.segs[R_GS].base = *(uint32_t *)value;
X86_CPU(uc, mycpu)->env.segs[R_GS].selector = *(uint16_t *)value;
break;
case UC_X86_REG_IDTR:
X86_CPU(uc, mycpu)->env.idt.limit = (uint16_t)((uc_x86_mmr *)value)->limit;
X86_CPU(uc, mycpu)->env.idt.base = (uint32_t)((uc_x86_mmr *)value)->base;
break;
case UC_X86_REG_GDTR:
X86_CPU(uc, mycpu)->env.gdt.limit = (uint16_t)((uc_x86_mmr *)value)->limit;
X86_CPU(uc, mycpu)->env.gdt.base = (uint32_t)((uc_x86_mmr *)value)->base;
break;
case UC_X86_REG_LDTR:
X86_CPU(uc, mycpu)->env.ldt.limit = ((uc_x86_mmr *)value)->limit;
X86_CPU(uc, mycpu)->env.ldt.base = (uint32_t)((uc_x86_mmr *)value)->base;
X86_CPU(uc, mycpu)->env.ldt.selector = (uint16_t)((uc_x86_mmr *)value)->selector;
X86_CPU(uc, mycpu)->env.ldt.flags = ((uc_x86_mmr *)value)->flags;
break;
case UC_X86_REG_TR:
X86_CPU(uc, mycpu)->env.tr.limit = ((uc_x86_mmr *)value)->limit;
X86_CPU(uc, mycpu)->env.tr.base = (uint32_t)((uc_x86_mmr *)value)->base;
X86_CPU(uc, mycpu)->env.tr.selector = (uint16_t)((uc_x86_mmr *)value)->selector;
X86_CPU(uc, mycpu)->env.tr.flags = ((uc_x86_mmr *)value)->flags;
break;
}
break;
@ -806,30 +945,39 @@ int x86_reg_write(struct uc_struct *uc, unsigned int regid, const void *value)
break;
case UC_X86_REG_RIP:
X86_CPU(uc, mycpu)->env.eip = *(uint64_t *)value;
// force to quit execution and flush TB
uc->quit_request = true;
uc_emu_stop(uc);
break;
case UC_X86_REG_EIP:
WRITE_DWORD(X86_CPU(uc, mycpu)->env.eip, *(uint32_t *)value);
// force to quit execution and flush TB
uc->quit_request = true;
uc_emu_stop(uc);
break;
case UC_X86_REG_IP:
WRITE_WORD(X86_CPU(uc, mycpu)->env.eip, *(uint16_t *)value);
// force to quit execution and flush TB
uc->quit_request = true;
uc_emu_stop(uc);
break;
case UC_X86_REG_CS:
X86_CPU(uc, mycpu)->env.segs[R_CS].base = *(uint64_t *)value;
X86_CPU(uc, mycpu)->env.segs[R_CS].selector = *(uint16_t *)value;
break;
case UC_X86_REG_DS:
X86_CPU(uc, mycpu)->env.segs[R_DS].base = *(uint64_t *)value;
X86_CPU(uc, mycpu)->env.segs[R_DS].selector = *(uint16_t *)value;
break;
case UC_X86_REG_SS:
X86_CPU(uc, mycpu)->env.segs[R_SS].base = *(uint64_t *)value;
X86_CPU(uc, mycpu)->env.segs[R_SS].selector = *(uint16_t *)value;
break;
case UC_X86_REG_ES:
X86_CPU(uc, mycpu)->env.segs[R_ES].base = *(uint64_t *)value;
X86_CPU(uc, mycpu)->env.segs[R_ES].selector = *(uint16_t *)value;
break;
case UC_X86_REG_FS:
X86_CPU(uc, mycpu)->env.segs[R_FS].base = *(uint64_t *)value;
X86_CPU(uc, mycpu)->env.segs[R_FS].selector = *(uint16_t *)value;
break;
case UC_X86_REG_GS:
X86_CPU(uc, mycpu)->env.segs[R_GS].base = *(uint64_t *)value;
X86_CPU(uc, mycpu)->env.segs[R_GS].selector = *(uint16_t *)value;
break;
case UC_X86_REG_R8:
X86_CPU(uc, mycpu)->env.regs[8] = *(uint64_t *)value;
@ -927,6 +1075,26 @@ int x86_reg_write(struct uc_struct *uc, unsigned int regid, const void *value)
case UC_X86_REG_R15B:
WRITE_BYTE_L(X86_CPU(uc, mycpu)->env.regs[15], *(uint8_t *)value);
break;
case UC_X86_REG_IDTR:
X86_CPU(uc, mycpu)->env.idt.limit = (uint16_t)((uc_x86_mmr *)value)->limit;
X86_CPU(uc, mycpu)->env.idt.base = ((uc_x86_mmr *)value)->base;
break;
case UC_X86_REG_GDTR:
X86_CPU(uc, mycpu)->env.gdt.limit = (uint16_t)((uc_x86_mmr *)value)->limit;
X86_CPU(uc, mycpu)->env.gdt.base = ((uc_x86_mmr *)value)->base;
break;
case UC_X86_REG_LDTR:
X86_CPU(uc, mycpu)->env.ldt.limit = ((uc_x86_mmr *)value)->limit;
X86_CPU(uc, mycpu)->env.ldt.base = ((uc_x86_mmr *)value)->base;
X86_CPU(uc, mycpu)->env.ldt.selector = (uint16_t)((uc_x86_mmr *)value)->selector;
X86_CPU(uc, mycpu)->env.ldt.flags = ((uc_x86_mmr *)value)->flags;
break;
case UC_X86_REG_TR:
X86_CPU(uc, mycpu)->env.tr.limit = ((uc_x86_mmr *)value)->limit;
X86_CPU(uc, mycpu)->env.tr.base = ((uc_x86_mmr *)value)->base;
X86_CPU(uc, mycpu)->env.tr.selector = (uint16_t)((uc_x86_mmr *)value)->selector;
X86_CPU(uc, mycpu)->env.tr.flags = ((uc_x86_mmr *)value)->flags;
break;
}
break;
#endif

View file

@ -1,4 +1,4 @@
DEF_HELPER_5(uc_tracecode, void, i32, ptr, ptr, i64, ptr)
DEF_HELPER_4(uc_tracecode, void, i32, i32, ptr, i64)
DEF_HELPER_1(bitrev, i32, i32)
DEF_HELPER_1(ff1, i32, i32)

View file

@ -3043,11 +3043,8 @@ static void disas_m68k_insn(CPUM68KState * env, DisasContext *s)
}
// Unicorn: trace this instruction on request
if (env->uc->hook_insn) {
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_CODE, s->pc);
if (trace)
gen_uc_tracecode(tcg_ctx, 2, trace->callback, env->uc, s->pc, trace->user_data);
if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, s->pc)) {
gen_uc_tracecode(tcg_ctx, 2, UC_HOOK_CODE_IDX, env->uc, s->pc);
// the callback might want to stop emulation immediately
check_exit_request(tcg_ctx);
}
@ -3109,13 +3106,10 @@ gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb,
// Unicorn: trace this block on request
// Only hook this block if it is not broken from previous translation due to
// full translation cache
if (env->uc->hook_block && !env->uc->block_full) {
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start);
if (trace) {
// save block address to see if we need to patch block size later
env->uc->block_addr = pc_start;
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data);
}
if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) {
// save block address to see if we need to patch block size later
env->uc->block_addr = pc_start;
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start);
}
gen_tb_start(tcg_ctx);

View file

@ -6,14 +6,8 @@
#include "sysemu/cpus.h"
#include "unicorn.h"
#include "cpu.h"
#include "unicorn_common.h"
#define READ_QWORD(x) ((uint64)x)
#define READ_DWORD(x) (x & 0xffffffff)
#define READ_WORD(x) (x & 0xffff)
#define READ_BYTE_H(x) ((x & 0xffff) >> 8)
#define READ_BYTE_L(x) (x & 0xff)
#include "uc_priv.h"
static void m68k_set_pc(struct uc_struct *uc, uint64_t address)
@ -51,12 +45,6 @@ int m68k_reg_read(struct uc_struct *uc, unsigned int regid, void *value)
return 0;
}
#define WRITE_DWORD(x, w) (x = (x & ~0xffffffff) | (w & 0xffffffff))
#define WRITE_WORD(x, w) (x = (x & ~0xffff) | (w & 0xffff))
#define WRITE_BYTE_H(x, b) (x = (x & ~0xff00) | (b & 0xff))
#define WRITE_BYTE_L(x, b) (x = (x & ~0xff) | (b & 0xff))
int m68k_reg_write(struct uc_struct *uc, unsigned int regid, const void *value)
{
CPUState *mycpu = first_cpu;
@ -70,6 +58,9 @@ int m68k_reg_write(struct uc_struct *uc, unsigned int regid, const void *value)
default: break;
case UC_M68K_REG_PC:
M68K_CPU(uc, mycpu)->env.pc = *(uint32_t *)value;
// force to quit execution and flush TB
uc->quit_request = true;
uc_emu_stop(uc);
break;
}
}

View file

@ -1,4 +1,4 @@
DEF_HELPER_5(uc_tracecode, void, i32, ptr, ptr, i64, ptr)
DEF_HELPER_4(uc_tracecode, void, i32, i32, ptr, i64)
DEF_HELPER_3(raise_exception_err, noreturn, env, i32, int)
DEF_HELPER_2(raise_exception, noreturn, env, i32)

View file

@ -11343,12 +11343,9 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, bool *insn_n
n_bytes = 2;
// Unicorn: trace this instruction on request
if (env->uc->hook_insn) {
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_CODE, ctx->pc);
if (trace) {
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, ctx->pc, trace->user_data);
*insn_need_patch = true;
}
if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, ctx->pc)) {
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_CODE_IDX, env->uc, ctx->pc);
*insn_need_patch = true;
// the callback might want to stop emulation immediately
check_exit_request(tcg_ctx);
}
@ -13942,12 +13939,9 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, bool *ins
}
// Unicorn: trace this instruction on request
if (env->uc->hook_insn) {
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_CODE, ctx->pc);
if (trace) {
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, ctx->pc, trace->user_data);
*insn_need_patch = true;
}
if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, ctx->pc)) {
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_CODE_IDX, env->uc, ctx->pc);
*insn_need_patch = true;
// the callback might want to stop emulation immediately
check_exit_request(tcg_ctx);
}
@ -18504,13 +18498,10 @@ static void gen_msa(CPUMIPSState *env, DisasContext *ctx)
// Unicorn: trace this instruction on request
static void hook_insn(CPUMIPSState *env, DisasContext *ctx, bool *insn_need_patch, int *insn_patch_offset, int offset_value)
{
if (env->uc->hook_insn) {
TCGContext *tcg_ctx = ctx->uc->tcg_ctx;
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_CODE, ctx->pc);
if (trace) {
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, ctx->pc, trace->user_data);
*insn_need_patch = true;
}
TCGContext *tcg_ctx = ctx->uc->tcg_ctx;
if (HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_CODE, ctx->pc)) {
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_CODE_IDX, env->uc, ctx->pc);
*insn_need_patch = true;
// the callback might want to stop emulation immediately
check_exit_request(tcg_ctx);
*insn_patch_offset = offset_value;
@ -19223,13 +19214,10 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb,
// Unicorn: trace this block on request
// Only hook this block if it is not broken from previous translation due to
// full translation cache
if (env->uc->hook_block && !env->uc->block_full) {
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start);
if (trace) {
// save block address to see if we need to patch block size later
env->uc->block_addr = pc_start;
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data);
}
if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) {
// save block address to see if we need to patch block size later
env->uc->block_addr = pc_start;
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start);
}
gen_tb_start(tcg_ctx);
@ -19275,7 +19263,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb,
int insn_patch_offset = 1;
// Unicorn: save param buffer
if (env->uc->hook_insn)
if (HOOK_EXISTS(env->uc, UC_HOOK_CODE))
save_opparam_ptr = tcg_ctx->gen_opparam_ptr;
is_slot = ctx.hflags & MIPS_HFLAG_BMASK;

View file

@ -6,15 +6,8 @@
#include "sysemu/cpus.h"
#include "unicorn.h"
#include "cpu.h"
#include "unicorn_common.h"
#define READ_QWORD(x) ((uint64)x)
#define READ_DWORD(x) (x & 0xffffffff)
#define READ_WORD(x) (x & 0xffff)
#define READ_BYTE_H(x) ((x & 0xffff) >> 8)
#define READ_BYTE_L(x) (x & 0xff)
#include "uc_priv.h"
static uint64_t mips_mem_redirect(uint64_t address)
@ -64,12 +57,6 @@ int mips_reg_read(struct uc_struct *uc, unsigned int regid, void *value)
return 0;
}
#define WRITE_DWORD(x, w) (x = (x & ~0xffffffff) | (w & 0xffffffff))
#define WRITE_WORD(x, w) (x = (x & ~0xffff) | (w & 0xffff))
#define WRITE_BYTE_H(x, b) (x = (x & ~0xff00) | (b & 0xff))
#define WRITE_BYTE_L(x, b) (x = (x & ~0xff) | (b & 0xff))
int mips_reg_write(struct uc_struct *uc, unsigned int regid, const void *value)
{
CPUState *mycpu = first_cpu;
@ -81,6 +68,9 @@ int mips_reg_write(struct uc_struct *uc, unsigned int regid, const void *value)
default: break;
case UC_MIPS_REG_PC:
MIPS_CPU(uc, mycpu)->env.active_tc.PC = *(uint32_t *)value;
// force to quit execution and flush TB
uc->quit_request = true;
uc_emu_stop(uc);
break;
}
}

View file

@ -1,4 +1,4 @@
DEF_HELPER_5(uc_tracecode, void, i32, ptr, ptr, i64, ptr)
DEF_HELPER_4(uc_tracecode, void, i32, i32, ptr, i64)
DEF_HELPER_1(power_down, void, env)
#ifndef TARGET_SPARC64

View file

@ -2630,18 +2630,9 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn, bool hook_ins
tcg_gen_debug_insn_start(tcg_ctx, dc->pc);
}
// end address tells us to stop emulation
if (dc->pc == dc->uc->addr_end) {
insn = 0x91d02000; // generate TRAP to end this TB
hook_insn = false; // do not hook this instruction
}
// Unicorn: trace this instruction on request
if (hook_insn && dc->uc->hook_insn) {
struct hook_struct *trace = hook_find(dc->uc, UC_HOOK_CODE, dc->pc);
if (trace)
gen_uc_tracecode(tcg_ctx, 4, trace->callback, dc->uc, dc->pc, trace->user_data);
if (hook_insn && HOOK_EXISTS_BOUNDED(dc->uc, UC_HOOK_CODE, dc->pc)) {
gen_uc_tracecode(tcg_ctx, 4, UC_HOOK_CODE_IDX, dc->uc, dc->pc);
// the callback might want to stop emulation immediately
check_exit_request(tcg_ctx);
}
@ -5408,9 +5399,8 @@ static inline void gen_intermediate_code_internal(SPARCCPU *cpu,
// early check to see if the address of this block is the until address
if (pc_start == env->uc->addr_end) {
gen_tb_start(tcg_ctx);
insn = 0x91d02000; // generate TRAP to end this TB
disas_sparc_insn(dc, insn, false);
goto exit_gen_loop;
gen_helper_power_down(tcg_ctx, tcg_ctx->cpu_env);
goto done_generating;
}
max_insns = tb->cflags & CF_COUNT_MASK;
@ -5428,13 +5418,10 @@ static inline void gen_intermediate_code_internal(SPARCCPU *cpu,
// Unicorn: trace this block on request
// Only hook this block if it is not broken from previous translation due to
// full translation cache
if (env->uc->hook_block && !env->uc->block_full) {
struct hook_struct *trace = hook_find(env->uc, UC_HOOK_BLOCK, pc_start);
if (trace) {
// save block address to see if we need to patch block size later
env->uc->block_addr = pc_start;
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, trace->callback, env->uc, pc_start, trace->user_data);
}
if (!env->uc->block_full && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, pc_start)) {
// save block address to see if we need to patch block size later
env->uc->block_addr = pc_start;
gen_uc_tracecode(tcg_ctx, 0xf8f8f8f8, UC_HOOK_BLOCK_IDX, env->uc, pc_start);
}
gen_tb_start(tcg_ctx);

View file

@ -7,13 +7,7 @@
#include "unicorn.h"
#include "cpu.h"
#include "unicorn_common.h"
#define READ_QWORD(x) ((uint64)x)
#define READ_DWORD(x) (x & 0xffffffff)
#define READ_WORD(x) (x & 0xffff)
#define READ_BYTE_H(x) ((x & 0xffff) >> 8)
#define READ_BYTE_L(x) (x & 0xff)
#include "uc_priv.h"
static bool sparc_stop_interrupt(int intno)
@ -87,6 +81,9 @@ int sparc_reg_write(struct uc_struct *uc, unsigned int regid, const void *value)
case UC_SPARC_REG_PC:
SPARC_CPU(uc, mycpu)->env.pc = *(uint32_t *)value;
SPARC_CPU(uc, mycpu)->env.npc = *(uint32_t *)value + 4;
// force to quit execution and flush TB
uc->quit_request = true;
uc_emu_stop(uc);
break;
}
}

View file

@ -7,13 +7,7 @@
#include "unicorn.h"
#include "cpu.h"
#include "unicorn_common.h"
#define READ_QWORD(x) ((uint64)x)
#define READ_DWORD(x) (x & 0xffffffff)
#define READ_WORD(x) (x & 0xffff)
#define READ_BYTE_H(x) ((x & 0xffff) >> 8)
#define READ_BYTE_L(x) (x & 0xff)
#include "uc_priv.h"
static bool sparc_stop_interrupt(int intno)

View file

@ -1209,7 +1209,7 @@ static inline void tcg_out_tlb_load(TCGContext *s, TCGReg addrlo, TCGReg addrhi,
tcg_out_mov(s, ttype, r1, addrlo);
// Unicorn: fast path if hookmem is not enable
if (!s->uc->hook_mem_read && !s->uc->hook_mem_write)
if (!HOOK_EXISTS(s->uc, UC_HOOK_MEM_READ) && !HOOK_EXISTS(s->uc, UC_HOOK_MEM_WRITE))
tcg_out_opc(s, OPC_JCC_long + JCC_JNE, 0, 0, 0);
else
tcg_out_opc(s, OPC_JMP_long, 0, 0, 0); /* slow_path */

View file

@ -27,14 +27,13 @@
int gen_new_label(TCGContext *);
static inline void gen_uc_tracecode(TCGContext *tcg_ctx, int32_t size, void *callback, void *uc, uint64_t pc, void *data)
static inline void gen_uc_tracecode(TCGContext *tcg_ctx, int32_t size, int32_t type, void *uc, uint64_t pc)
{
TCGv_i32 tsize = tcg_const_i32(tcg_ctx, size);
TCGv_ptr tcallback = tcg_const_ptr(tcg_ctx, callback);
TCGv_i32 ttype = tcg_const_i32(tcg_ctx, type);
TCGv_ptr tuc = tcg_const_ptr(tcg_ctx, uc);
TCGv_i64 tpc = tcg_const_i64(tcg_ctx, pc);
TCGv_ptr tdata = tcg_const_ptr(tcg_ctx, data);
gen_helper_uc_tracecode(tcg_ctx, tsize, tcallback, tuc, tpc, tdata);
gen_helper_uc_tracecode(tcg_ctx, tsize, ttype, tuc, tpc);
}
static inline void tcg_gen_op0(TCGContext *s, TCGOpcode opc)

View file

@ -179,7 +179,7 @@ static int cpu_gen_code(CPUArchState *env, TranslationBlock *tb, int *gen_code_s
gen_intermediate_code(env, tb);
// Unicorn: when tracing block, patch 1st operand for block size
if (env->uc->hook_block && env->uc->block_addr == tb->pc) {
if (env->uc->block_addr == tb->pc && HOOK_EXISTS_BOUNDED(env->uc, UC_HOOK_BLOCK, tb->pc)) {
if (env->uc->block_full) // block size is unknown
*(s->gen_opparam_buf + 1) = 0;
else

View file

@ -29,8 +29,8 @@
static void error_exit(int err, const char *msg)
{
// fprintf(stderr, "qemu: %s: %s\n", msg, strerror(err));
// abort();
fprintf(stderr, "qemu: %s: %s\n", msg, strerror(err));
abort();
}
void qemu_mutex_init(QemuMutex *mutex)

View file

@ -1,6 +1,7 @@
/* Autogen header for Unicorn Engine - DONOT MODIFY */
#ifndef UNICORN_AUTOGEN_X86_64_H
#define UNICORN_AUTOGEN_X86_64_H
#define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_x86_64
#define use_idiv_instructions_rt use_idiv_instructions_rt_x86_64
#define tcg_target_deposit_valid tcg_target_deposit_valid_x86_64
#define helper_power_down helper_power_down_x86_64

View file

@ -142,13 +142,13 @@ static void do_nx_demo(bool cause_fault)
/*
bits 32
page0:
page0: @0
times 4091 inc eax
jmp page2
page1:
times 4095 inc eax
page1: @1000
times 4095 inc eax (or INC ECX)
hlt
page2:
page2: @2000
jmp page1
*/
memset(code_buf, 0x40, sizeof(code_buf)); // fill with inc eax
@ -168,9 +168,9 @@ static void do_nx_demo(bool cause_fault)
}
// intercept code and invalid memory events
if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK ||
if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK ||
uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID,
hook_mem_invalid, NULL) != UC_ERR_OK) {
hook_mem_invalid, NULL, 1, 0) != UC_ERR_OK) {
printf("not ok - Failed to install hooks\n");
return;
}
@ -248,10 +248,10 @@ static void do_perms_demo(bool change_perms)
}
// intercept code and invalid memory events
if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK ||
if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK ||
uc_hook_add(uc, &trace1,
UC_HOOK_MEM_INVALID,
hook_mem_invalid, NULL) != UC_ERR_OK) {
hook_mem_invalid, NULL, 1, 0) != UC_ERR_OK) {
printf("not ok - Failed to install hooks\n");
return;
}
@ -326,10 +326,10 @@ static void do_unmap_demo(bool do_unmap)
}
// intercept code and invalid memory events
if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK ||
if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK ||
uc_hook_add(uc, &trace1,
UC_HOOK_MEM_INVALID,
hook_mem_invalid, NULL) != UC_ERR_OK) {
hook_mem_invalid, NULL, 1, 0) != UC_ERR_OK) {
printf("not ok - Failed to install hooks\n");
return;
}

View file

@ -77,10 +77,10 @@ static void test_arm(void)
uc_reg_write(uc, UC_ARM_REG_R3, &r3);
// tracing all basic blocks with customized callback
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0);
// tracing one instruction at ADDRESS with customized callback
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS);
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, ADDRESS, ADDRESS);
// emulate machine code in infinite time (last param = 0), or when
// finishing all the code.
@ -128,10 +128,10 @@ static void test_thumb(void)
uc_reg_write(uc, UC_ARM_REG_SP, &sp);
// tracing all basic blocks with customized callback
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0);
// tracing one instruction at ADDRESS with customized callback
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS);
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, ADDRESS, ADDRESS);
// emulate machine code in infinite time (last param = 0), or when
// finishing all the code.

View file

@ -75,10 +75,10 @@ static void test_arm64(void)
uc_reg_write(uc, UC_ARM64_REG_X15, &x15);
// tracing all basic blocks with customized callback
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0);
// tracing one instruction at ADDRESS with customized callback
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS);
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, ADDRESS, ADDRESS);
// emulate machine code in infinite time (last param = 0), or when
// finishing all the code.

View file

@ -108,10 +108,10 @@ static void test_m68k(void)
uc_reg_write(uc, UC_M68K_REG_SR, &sr);
// tracing all basic blocks with customized callback
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0);
// tracing all instruction
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0);
// emulate machine code in infinite time (last param = 0), or when
// finishing all the code.

View file

@ -72,10 +72,10 @@ static void test_mips_eb(void)
uc_reg_write(uc, UC_MIPS_REG_1, &r1);
// tracing all basic blocks with customized callback
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0);
// tracing one instruction at ADDRESS with customized callback
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS);
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, ADDRESS, ADDRESS);
// emulate machine code in infinite time (last param = 0), or when
// finishing all the code.
@ -105,7 +105,7 @@ static void test_mips_el(void)
printf("Emulate MIPS code (little-endian)\n");
// Initialize emulator in MIPS mode
err = uc_open(UC_ARCH_MIPS, UC_MODE_MIPS32, &uc);
err = uc_open(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN, &uc);
if (err) {
printf("Failed on uc_open() with error returned: %u (%s)\n",
err, uc_strerror(err));
@ -122,10 +122,10 @@ static void test_mips_el(void)
uc_reg_write(uc, UC_MIPS_REG_1, &r1);
// tracing all basic blocks with customized callback
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0);
// tracing one instruction at ADDRESS with customized callback
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS);
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, ADDRESS, ADDRESS);
// emulate machine code in infinite time (last param = 0), or when
// finishing all the code.

View file

@ -57,7 +57,7 @@ static void test_sparc(void)
printf("Emulate SPARC code\n");
// Initialize emulator in Sparc mode
err = uc_open(UC_ARCH_SPARC, UC_MODE_32, &uc);
err = uc_open(UC_ARCH_SPARC, UC_MODE_SPARC32|UC_MODE_BIG_ENDIAN, &uc);
if (err) {
printf("Failed on uc_open() with error returned: %u (%s)\n",
err, uc_strerror(err));
@ -76,10 +76,10 @@ static void test_sparc(void)
uc_reg_write(uc, UC_SPARC_REG_G3, &g3);
// tracing all basic blocks with customized callback
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0);
// tracing all instructions with customized callback
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0);
// emulate machine code in infinite time (last param = 0), or when
// finishing all the code.

View file

@ -219,10 +219,10 @@ static void test_i386(void)
uc_reg_write(uc, UC_X86_REG_EDX, &r_edx);
// tracing all basic blocks with customized callback
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0);
// tracing all instruction by having @begin > @end
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0);
// emulate machine code in infinite time
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32) - 1, 0, 0);
@ -289,10 +289,10 @@ static void test_i386_map_ptr(void)
uc_reg_write(uc, UC_X86_REG_EDX, &r_edx);
// tracing all basic blocks with customized callback
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0);
// tracing all instruction by having @begin > @end
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0);
// emulate machine code in infinite time
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32) - 1, 0, 0);
@ -345,10 +345,10 @@ static void test_i386_jump(void)
}
// tracing 1 basic block with customized callback
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS);
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, ADDRESS, ADDRESS);
// tracing 1 instruction at ADDRESS
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS);
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, ADDRESS, ADDRESS);
// emulate machine code in infinite time
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_JUMP) - 1, 0, 0);
@ -447,10 +447,10 @@ static void test_i386_invalid_mem_read(void)
uc_reg_write(uc, UC_X86_REG_EDX, &r_edx);
// tracing all basic blocks with customized callback
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0);
// tracing all instruction by having @begin > @end
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0);
// emulate machine code in infinite time
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_MEM_READ) - 1, 0, 0);
@ -505,13 +505,13 @@ static void test_i386_invalid_mem_write(void)
uc_reg_write(uc, UC_X86_REG_EDX, &r_edx);
// tracing all basic blocks with customized callback
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0);
// tracing all instruction by having @begin > @end
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0);
// intercept invalid memory events
uc_hook_add(uc, &trace3, UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, hook_mem_invalid, NULL);
uc_hook_add(uc, &trace3, UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, hook_mem_invalid, NULL, 1, 0);
// emulate machine code in infinite time
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_MEM_WRITE) - 1, 0, 0);
@ -576,10 +576,10 @@ static void test_i386_jump_invalid(void)
uc_reg_write(uc, UC_X86_REG_EDX, &r_edx);
// tracing all basic blocks with customized callback
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0);
// tracing all instructions by having @begin > @end
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0);
// emulate machine code in infinite time
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_JMP_INVALID) - 1, 0, 0);
@ -632,15 +632,15 @@ static void test_i386_inout(void)
uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx);
// tracing all basic blocks with customized callback
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0);
// tracing all instructions
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0);
// uc IN instruction
uc_hook_add(uc, &trace3, UC_HOOK_INSN, hook_in, NULL, UC_X86_INS_IN);
uc_hook_add(uc, &trace3, UC_HOOK_INSN, hook_in, NULL, 1, 0, UC_X86_INS_IN);
// uc OUT instruction
uc_hook_add(uc, &trace4, UC_HOOK_INSN, hook_out, NULL, UC_X86_INS_OUT);
uc_hook_add(uc, &trace4, UC_HOOK_INSN, hook_out, NULL, 1, 0, UC_X86_INS_OUT);
// emulate machine code in infinite time
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_INOUT) - 1, 0, 0);
@ -721,16 +721,16 @@ static void test_x86_64(void)
uc_reg_write(uc, UC_X86_REG_R15, &r15);
// tracing all basic blocks with customized callback
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0);
// tracing all instructions in the range [ADDRESS, ADDRESS+20]
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code64, NULL, (uint64_t)ADDRESS, (uint64_t)(ADDRESS+20));
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code64, NULL, ADDRESS, ADDRESS+20);
// tracing all memory WRITE access (with @begin > @end)
uc_hook_add(uc, &trace3, UC_HOOK_MEM_WRITE, hook_mem64, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace3, UC_HOOK_MEM_WRITE, hook_mem64, NULL, 1, 0);
// tracing all memory READ access (with @begin > @end)
uc_hook_add(uc, &trace4, UC_HOOK_MEM_READ, hook_mem64, NULL, (uint64_t)1, (uint64_t)0);
uc_hook_add(uc, &trace4, UC_HOOK_MEM_READ, hook_mem64, NULL, 1, 0);
// emulate machine code in infinite time (last param = 0), or when
// finishing all the code.
@ -804,7 +804,7 @@ static void test_x86_64_syscall(void)
}
// hook interrupts for syscall
uc_hook_add(uc, &trace1, UC_HOOK_INSN, hook_syscall, NULL, UC_X86_INS_SYSCALL);
uc_hook_add(uc, &trace1, UC_HOOK_INSN, hook_syscall, NULL, 1, 0, UC_X86_INS_SYSCALL);
// initialize machine registers
uc_reg_write(uc, UC_X86_REG_RAX, &rax);

View file

@ -138,7 +138,7 @@ static void test_i386(void)
uc_hook_add(uc, &trace1, UC_HOOK_CODE, hook_code, NULL, 1, 0);
// handle interrupt ourself
uc_hook_add(uc, &trace2, UC_HOOK_INTR, hook_intr, NULL);
uc_hook_add(uc, &trace2, UC_HOOK_INTR, hook_intr, NULL, 1, 0);
printf("\n>>> Start tracing this Linux code\n");

30
tests/regress/LICENSE Normal file
View file

@ -0,0 +1,30 @@
This is the software license for Unicorn regression tests. The regression tests
are written by several Unicorn contributors (See CREDITS.TXT) and maintained by
Hoang-Vu Dang <dang.hvu@gmail.com>
Copyright (c) 2015, Unicorn contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the developer(s) nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View file

@ -37,6 +37,7 @@ TESTS += mips_branch_likely_issue
TESTS += hook_extrainvoke
TESTS += sysenter_hook_x86
TESTS += emu_clear_errors
TESTS += mem_fuzz
all: $(TESTS)

Some files were not shown because too many files have changed in this diff Show more