Rewrite the Java bindings.

This brings the Java API up to par with Python feature-wise and substantially
simplifies the hook implementation, enabling proper bounds-checked hooks.

The rewrite strives for compatibility with the previous API, but there are some
breaking changes. It is possible to push closer to full backwards compatibility
if required, at the cost of reintroducing some of the suboptimal designs. Here
are the main points of breakage:

- ReadHook and WriteHook are gone, replaced simply by MemHook. Hooking valid
  memory accesses now requires a type parameter. This enables fetch and
  read-after hooks with a unified API and a single callback object.
- mem_read now takes an int, not a long. We are unable to allocate more than 2GB
  in a single request anyway (Java limitation).
- Instruction hooks now require specifying the instruction explicitly, instead
  of guessing based on the hook type. This is necessary to distinguish
  sysenter/syscall and ARM64 mrs/msr/sys/sysl, without excessively bloating the
  library with redundant hook types. Bounds must also be specified, to support
  bounds-checked instruction hooks.
- Reading object-type registers (any register larger than 64 bits, or registers
  with special formats) requires a second argument to reg_read. This allows us
  to provide a fast reg_read that returns a long for the common cases, while
  still supporting a more general reg_read for other registers.
- mem_map_ptr is rewritten to take a *direct* java.nio.Buffer, which enables
  many more use cases than a simple byte array, and improves performance (a
  byte array cannot really be used as a mapped buffer without GC-pinning it,
  which hurts the GC performance).
- Context handling API is redesigned to be safer and more object-oriented.

A lot of bugs are fixed with this implementation:
- Unicorn instances can be properly garbage-collected, instead of hanging around
  forever in the Unicorn.unicorns table.
- Hooks no longer fire outside of their bounds (#1164), and in fact, hook bounds
  are properly respected (previously, all hooks were just registered globally to
  all addresses).
- Hooks are substantially faster, as they are now dispatched directly via a
  single method call rather than being indirected through invokeCallbacks.
- Loading vector registers works now, rather than crashing the VM (#1539).

Several features are now enabled in the Java implementation:

- All of the current ctl_* calls are implemented.
- mmio_map is implemented.
- New virtual TLB mode is implemented.
- reading/writing Context registers is implemented.
- New hook types are added: TcgOpcodeHook, EdgeGeneratedHook,
  InvalidInstructionHook, TlbFillHook, and the instruction hooks Arm64SysHook,
  CpuidHook.
- All known special registers are supported.
This commit is contained in:
Robert Xiao 2023-05-06 17:49:41 -07:00
parent 8777bb6ae6
commit aa430587cc
33 changed files with 2991 additions and 1421 deletions

View File

@ -51,7 +51,7 @@ unicorn_Unicorn.h: unicorn/Unicorn.java
javac -h . $<
unicorn_Unicorn.o: unicorn_Unicorn.c unicorn_Unicorn.h
$(CC) -c $(CFLAGS) $(INCS) $< -o $@
$(CC) -O2 -Wall -Wextra -Wno-unused-parameter -c $(CFLAGS) $(INCS) $< -o $@
libunicorn_java$(LIB_EXT): unicorn_Unicorn.o
@ -66,7 +66,7 @@ jar: jarfiles
jar cf $(JARFILE) unicorn/*.class
test: lib samples tests
java -cp .:testdep/hamcrest-2.2.jar:testdep/junit-4.13.2.jar org.junit.runner.JUnitCore $(subst /,.,$(TESTS:.java=))
java -Xcheck:jni -cp .:testdep/hamcrest-2.2.jar:testdep/junit-4.13.2.jar org.junit.runner.JUnitCore $(subst /,.,$(TESTS:.java=))
install: lib jar
cp libunicorn_java$(LIB_EXT) /usr/lib

View File

@ -100,7 +100,7 @@ public class SampleNetworkAuditing {
long buf = ecx;
long count = edx;
byte[] content = uc.mem_read(buf, count);
byte[] content = uc.mem_read(buf, (int) count);
String msg = String.format("write data=%s count=%d to fd(%d)",
new String(content), count, fd);
@ -166,7 +166,7 @@ public class SampleNetworkAuditing {
long addrlen =
toInt(uc.mem_read(args + SIZE_REG * 2, SIZE_REG));
byte[] sock_addr = uc.mem_read(umyaddr, addrlen);
byte[] sock_addr = uc.mem_read(umyaddr, (int) addrlen);
String msg = String.format("fd(%d) bind to %s", fd,
parse_sock_address(sock_addr));
@ -181,7 +181,7 @@ public class SampleNetworkAuditing {
long addrlen =
toInt(uc.mem_read(args + SIZE_REG * 2, SIZE_REG));
byte[] sock_addr = uc.mem_read(uservaddr, addrlen);
byte[] sock_addr = uc.mem_read(uservaddr, (int) addrlen);
String msg = String.format("fd(%d) connect to %s", fd,
parse_sock_address(sock_addr));
fd_chains.add_log(fd, msg);
@ -211,7 +211,7 @@ public class SampleNetworkAuditing {
long upeer_len = toInt(uc.mem_read(upeer_addrlen, 4));
byte[] sock_addr =
uc.mem_read(upeer_sockaddr, upeer_len);
uc.mem_read(upeer_sockaddr, (int) upeer_len);
String msg =
String.format("fd(%d) accept client with upeer=%s",
@ -227,7 +227,7 @@ public class SampleNetworkAuditing {
long flags =
toInt(uc.mem_read(args + SIZE_REG * 3, SIZE_REG));
byte[] buf = uc.mem_read(buff, length);
byte[] buf = uc.mem_read(buff, (int) length);
String msg = String.format("fd(%d) send data=%s", fd,
new String(buf));
fd_chains.add_log(fd, msg);

View File

@ -104,7 +104,8 @@ public class Sample_x86 {
}
private static class MyWriteInvalidHook implements EventMemHook {
public boolean hook(Unicorn u, long address, int size, long value,
public boolean hook(Unicorn u, int type, long address, int size,
long value,
Object user) {
System.out.printf(
">>> Missing memory is being WRITE at 0x%x, data size = %d, data value = 0x%x\n",
@ -131,16 +132,18 @@ public class Sample_x86 {
}
}
private static class MyRead64Hook implements ReadHook {
public void hook(Unicorn u, long address, int size, Object user) {
private static class MyRead64Hook implements MemHook {
public void hook(Unicorn u, int type, long address, int size,
long value, Object user) {
System.out.printf(
">>> Memory is being READ at 0x%x, data size = %d\n", address,
size);
}
}
private static class MyWrite64Hook implements WriteHook {
public void hook(Unicorn u, long address, int size, long value,
private static class MyWrite64Hook implements MemHook {
public void hook(Unicorn u, int type, long address, int size,
long value,
Object user) {
System.out.printf(
">>> Memory is being WRITE at 0x%x, data size = %d, data value = 0x%x\n",
@ -295,9 +298,9 @@ public class Sample_x86 {
u.hook_add(new MyCodeHook(), 1, 0, null);
// handle IN instruction
u.hook_add(new MyInHook(), null);
u.hook_add(new MyInHook(), Unicorn.UC_X86_INS_IN, 1, 0, null);
// handle OUT instruction
u.hook_add(new MyOutHook(), null);
u.hook_add(new MyOutHook(), Unicorn.UC_X86_INS_OUT, 1, 0, null);
// emulate machine code in infinite time
u.emu_start(ADDRESS, ADDRESS + X86_CODE32_INOUT.length, 0, 0);
@ -454,6 +457,7 @@ public class Sample_x86 {
// intercept invalid memory events
u.hook_add(new MyWriteInvalidHook(), Unicorn.UC_HOOK_MEM_WRITE_UNMAPPED,
1, 0,
null);
// emulate machine code in infinite time
@ -591,10 +595,10 @@ public class Sample_x86 {
u.hook_add(new MyCode64Hook(), ADDRESS, ADDRESS + 20, null);
// tracing all memory WRITE access (with @begin > @end)
u.hook_add(new MyWrite64Hook(), 1, 0, null);
u.hook_add(new MyWrite64Hook(), Unicorn.UC_HOOK_MEM_WRITE, 1, 0, null);
// tracing all memory READ access (with @begin > @end)
u.hook_add(new MyRead64Hook(), 1, 0, null);
u.hook_add(new MyRead64Hook(), Unicorn.UC_HOOK_MEM_READ, 1, 0, null);
// emulate machine code in infinite time (last param = 0), or when
// finishing all the code.

View File

@ -58,8 +58,8 @@ public class Sample_x86_mmr {
// 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);
ldtr2 = (X86_MMR) uc.reg_read(Unicorn.UC_X86_REG_LDTR, null);
gdtr2 = (X86_MMR) uc.reg_read(Unicorn.UC_X86_REG_GDTR, null);
System.out.printf(">>> EAX = 0x%x\n", eax);

View File

@ -0,0 +1,184 @@
package tests;
import static org.junit.Assert.assertEquals;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import org.junit.Test;
import unicorn.MemRegion;
import unicorn.TlbFillHook;
import unicorn.Unicorn;
import unicorn.X86_Float80;
public class FunctionalityTests {
@Test
public void testMemRegions() {
Unicorn uc = new Unicorn(Unicorn.UC_ARCH_ARM64, Unicorn.UC_MODE_ARM);
long ADDR1 = 0x10000;
long ADDR2 = 0xdeadbeeffeed1000L;
uc.mem_map(ADDR1, 2 * 1024 * 1024, Unicorn.UC_PROT_ALL);
uc.mem_map(ADDR2, 4096, Unicorn.UC_PROT_READ);
MemRegion[] arr = uc.mem_regions();
assertEquals("two memory regions", 2, arr.length);
assertEquals("begin", ADDR1, arr[0].begin);
assertEquals("end", ADDR1 + 2 * 1024 * 1024 - 1, arr[0].end);
assertEquals("perms", Unicorn.UC_PROT_ALL, arr[0].perms);
assertEquals("begin", ADDR2, arr[1].begin);
assertEquals("end", ADDR2 + 4096 - 1, arr[1].end);
assertEquals("perms", Unicorn.UC_PROT_READ, arr[1].perms);
uc.close();
}
@Test
public void testContext() {
Unicorn uc = new Unicorn(Unicorn.UC_ARCH_ARM64, Unicorn.UC_MODE_ARM);
uc.reg_write(Unicorn.UC_ARM64_REG_X0, 0xdeadbeef);
Unicorn.Context ctx = uc.context_save();
uc.reg_write(Unicorn.UC_ARM64_REG_X0, 0xfeedface);
assertEquals("X0 changed", 0xfeedface,
uc.reg_read(Unicorn.UC_ARM64_REG_X0));
uc.context_restore(ctx);
assertEquals("X0 restored", 0xdeadbeef,
uc.reg_read(Unicorn.UC_ARM64_REG_X0));
uc.reg_write(Unicorn.UC_ARM64_REG_X0, 0xfee1dead);
uc.context_update(ctx);
assertEquals("X0 changed", 0xfee1dead,
uc.reg_read(Unicorn.UC_ARM64_REG_X0));
uc.reg_write(Unicorn.UC_ARM64_REG_X0, 0xdeadbeef);
assertEquals("X0 changed", 0xdeadbeef,
uc.reg_read(Unicorn.UC_ARM64_REG_X0));
uc.context_restore(ctx);
assertEquals("X0 restored", 0xfee1dead,
uc.reg_read(Unicorn.UC_ARM64_REG_X0));
uc.close();
}
@Test
public void testMmio() {
// mov ecx, [0xaaaaaaa8]; inc ecx; dec edx; mov [0xaaaaaaa8], ecx; inc ecx; dec edx
final byte[] X86_CODE32_MEM_READ_WRITE =
{ -117, 13, -88, -86, -86, -86, 65, 74, -119, 13, -88, -86, -86,
-86, 65, 74 };
long ADDRESS = 0x100000;
Unicorn u = new Unicorn(Unicorn.UC_ARCH_X86, Unicorn.UC_MODE_32);
// map 2MB memory for this emulation
u.mem_map(ADDRESS, 2 * 1024 * 1024, Unicorn.UC_PROT_ALL);
// write machine code to be emulated to memory
u.mem_write(ADDRESS, X86_CODE32_MEM_READ_WRITE);
// initialize machine registers
u.reg_write(Unicorn.UC_X86_REG_ECX, 0x12345678);
u.reg_write(Unicorn.UC_X86_REG_EDX, 0x22334455);
u.mmio_map(0xaaaaa000L, 0x1000, (uc, offset, size, user_data) -> {
assertEquals("read offset", 0xaa8, offset);
assertEquals("read size", 4, size);
assertEquals("read user_data", "read_data", user_data);
return 0x44556677;
}, "read_data", (uc, offset, size, value, user_data) -> {
assertEquals("write offset", 0xaa8, offset);
assertEquals("write size", 4, size);
assertEquals("write value", 0x44556678, value);
assertEquals("write user_data", "write_data", user_data);
}, "write_data");
u.emu_start(ADDRESS, ADDRESS + X86_CODE32_MEM_READ_WRITE.length, 0, 0);
assertEquals("ecx", 0x44556679, u.reg_read(Unicorn.UC_X86_REG_ECX));
assertEquals("edx", 0x22334453, u.reg_read(Unicorn.UC_X86_REG_EDX));
u.close();
}
@Test
public void testMemMapPtr() {
ByteBuffer buffer =
ByteBuffer.allocateDirect(0x1000).order(ByteOrder.LITTLE_ENDIAN);
final byte[] X86_CODE32_MEM_WRITE =
{ -119, 13, -86, -86, -86, -86, 65, 74 };
long ADDRESS = 0x100000;
Unicorn u = new Unicorn(Unicorn.UC_ARCH_X86, Unicorn.UC_MODE_32);
u.mem_map(ADDRESS, 2 * 1024 * 1024, Unicorn.UC_PROT_ALL);
u.mem_map_ptr(0xaaaaa000L, buffer, Unicorn.UC_PROT_ALL);
u.mem_write(ADDRESS, X86_CODE32_MEM_WRITE);
u.reg_write(Unicorn.UC_X86_REG_ECX, 0x12345678);
u.emu_start(ADDRESS, ADDRESS + X86_CODE32_MEM_WRITE.length, 0, 0);
assertEquals("buffer contents", 0x12345678, buffer.getInt(0xaaa));
u.close();
}
@Test
public void testTlbHook() {
// mov ecx, [0xaaaaaaa8]
final byte[] X86_CODE32_MEM_READ = { -117, 13, -88, -86, -86, -86 };
long ADDRESS = 0x100000;
Unicorn u = new Unicorn(Unicorn.UC_ARCH_X86, Unicorn.UC_MODE_32);
u.mem_map(ADDRESS, 2 * 1024 * 1024, Unicorn.UC_PROT_ALL);
u.mem_map(0xbbbbb000L, 0x1000, Unicorn.UC_PROT_READ);
u.hook_add((TlbFillHook) (uc, address, type, user_data) -> {
assertEquals("fill hook address", 0xaaaaa000L, address);
assertEquals("fill hook type", Unicorn.UC_MEM_READ, type);
assertEquals("fill hook user", "fill_hook", user_data);
return 0xbbbbb000L | Unicorn.UC_PROT_READ;
}, 0xaaaaa000L, 0xaaaab000L, "fill_hook");
u.mem_write(ADDRESS, X86_CODE32_MEM_READ);
u.mem_write(0xbbbbbaa8L, new byte[] { 1, 2, 3, 4 });
u.reg_write(Unicorn.UC_X86_REG_ECX, 0x12345678);
u.ctl_tlb_mode(Unicorn.UC_TLB_VIRTUAL);
u.emu_start(ADDRESS, ADDRESS + X86_CODE32_MEM_READ.length, 0, 0);
assertEquals("ecx", u.reg_read(Unicorn.UC_X86_REG_ECX), 0x04030201);
u.close();
}
@Test
public void testX86ReadFloat80() {
// fldl2e; fsin
final byte[] X86_CODE = { -39, -22, -39, -2 };
long ADDRESS = 0x100000;
Unicorn u = new Unicorn(Unicorn.UC_ARCH_X86, Unicorn.UC_MODE_32);
u.mem_map(ADDRESS, 2 * 1024 * 1024, Unicorn.UC_PROT_ALL);
u.mem_write(ADDRESS, X86_CODE);
u.emu_start(ADDRESS, ADDRESS + X86_CODE.length, 0, 0);
X86_Float80 reg1 =
(X86_Float80) u.reg_read(Unicorn.UC_X86_REG_ST0, null);
X86_Float80 reg2 =
(X86_Float80) u.reg_read(Unicorn.UC_X86_REG_FP7, null);
assertEquals(null, ADDRESS, ADDRESS, ADDRESS);
assertEquals(Math.sin(Math.log(Math.E) / Math.log(2)), reg1.toDouble(),
1e-12);
assertEquals(reg1.toDouble(), reg2.toDouble(), 1e-12);
u.close();
}
@Test
public void testX86WriteFloat80() {
// fsin
final byte[] X86_CODE = { -39, -2 };
long ADDRESS = 0x100000;
Unicorn u = new Unicorn(Unicorn.UC_ARCH_X86, Unicorn.UC_MODE_32);
u.mem_map(ADDRESS, 2 * 1024 * 1024, Unicorn.UC_PROT_ALL);
u.mem_write(ADDRESS, X86_CODE);
X86_Float80 reg = X86_Float80.fromDouble(-1.1);
u.reg_write(Unicorn.UC_X86_REG_ST0, reg);
u.emu_start(ADDRESS, ADDRESS + X86_CODE.length, 0, 0);
reg = (X86_Float80) u.reg_read(Unicorn.UC_X86_REG_ST0, null);
assertEquals(Math.sin(-1.1), reg.toDouble(), 1e-12);
u.close();
}
}

View File

@ -2,6 +2,8 @@ package tests;
import static org.junit.Assert.assertEquals;
import java.math.BigInteger;
import org.junit.Test;
import unicorn.Unicorn;
@ -14,9 +16,14 @@ public class RegressionTests {
public void testARM64VReg() {
Unicorn uc = new Unicorn(Unicorn.UC_ARCH_ARM64, Unicorn.UC_MODE_ARM);
uc.reg_write(Unicorn.UC_ARM64_REG_X0, 0x1);
uc.reg_write(Unicorn.UC_ARM64_REG_V0, 0x2);
uc.reg_write(Unicorn.UC_ARM64_REG_V0, BigInteger.valueOf(0x1234));
uc.reg_read(Unicorn.UC_ARM64_REG_X0);
uc.reg_read(Unicorn.UC_ARM64_REG_V0); // should not crash
assertEquals("V0 value", BigInteger.valueOf(0x1234),
uc.reg_read(Unicorn.UC_ARM64_REG_V0, null)); // should not crash
assertEquals("V0 low byte", 0x34,
uc.reg_read(Unicorn.UC_ARM64_REG_B0));
assertEquals("V0 low halfword", 0x1234,
uc.reg_read(Unicorn.UC_ARM64_REG_H0));
uc.close();
}
@ -48,7 +55,7 @@ public class RegressionTests {
u.close();
}
/** Test that close() can be called multiple times without crashing */
/** Test that Unicorn instances are properly garbage-collected */
@Test
public void testUnicornsWillGC() {
final boolean[] close_called = { false };

View File

@ -0,0 +1,40 @@
/*
Java bindings for the Unicorn Emulator Engine
Copyright(c) 2023 Robert Xiao
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;
/** Callback for {@code UC_HOOK_INSN} with {@code UC_ARM64_INS_MRS},
* {@code UC_ARM64_INS_MSR}, {@code UC_ARM64_INS_SYS}
* or {@code UC_ARM64_INS_SYSL} */
public interface Arm64SysHook extends InstructionHook {
/** Called to handle an AArch64 MRS, MSR, SYS or SYSL instruction.
*
* @param u {@link Unicorn} instance firing this hook
* @param reg source or destination register
* ({@code UC_ARM64_REG_X*} constant)
* @param cp_reg coprocessor register specification
* ({@code .val} = current value of {@code reg})
* @param user user data provided when registering this hook
* @return 1 to skip the instruction (marking it as handled),
* 0 to let QEMU handle it
*/
public int hook(Unicorn u, int reg, Arm64_CP cp_reg, Object user);
}

View File

@ -0,0 +1,43 @@
/*
Java bindings for the Unicorn Emulator Engine
Copyright(c) 2023 Robert Xiao
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;
/** ARM64 coprocessor registers for instructions MRS, MSR, SYS, SYSL */
public class Arm64_CP {
public int crn, crm, op0, op1, op2;
public long val;
public Arm64_CP(int crn, int crm, int op0, int op1, int op2, long val) {
this.crn = crn;
this.crm = crm;
this.op0 = op0;
this.op1 = op1;
this.op2 = op2;
this.val = val;
}
@Override
public String toString() {
return "Arm64_CP [crn=" + crn + ", crm=" + crm + ", op0=" + op0 +
", op1=" + op1 + ", op2=" + op2 + ", val=" + val + "]";
}
}

View File

@ -0,0 +1,26 @@
package unicorn;
/** ARM coprocessor register for MRC, MCR, MRRC, MCRR */
public class Arm_CP {
public int cp, is64, sec, crn, crm, opc1, opc2;
public long val;
public Arm_CP(int cp, int is64, int sec, int crn, int crm, int opc1,
int opc2, long val) {
this.cp = cp;
this.is64 = is64;
this.sec = sec;
this.crn = crn;
this.crm = crm;
this.opc1 = opc1;
this.opc2 = opc2;
this.val = val;
}
@Override
public String toString() {
return "Arm_CP [cp=" + cp + ", is64=" + is64 + ", sec=" + sec +
", crn=" + crn + ", crm=" + crm + ", opc1=" + opc1 + ", opc2=" +
opc2 + ", val=" + val + "]";
}
}

View File

@ -21,6 +21,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
package unicorn;
/** Callback for {@code UC_HOOK_BLOCK} */
public interface BlockHook extends Hook {
/** Called on each basic block within the hooked range.
*
* @param u {@link Unicorn} instance firing this hook
* @param address address of the first instruction in the block
* @param size size of the block, in bytes
* @param user user data provided when registering this hook
*/
public void hook(Unicorn u, long address, int size, Object user);
}

View File

@ -21,6 +21,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
package unicorn;
/** Callback for {@code UC_HOOK_CODE} */
public interface CodeHook extends Hook {
/** Called on each instruction within the hooked range.
*
* @param u {@link Unicorn} instance firing this hook
* @param address address of the instruction
* @param size size of the instruction, in bytes
* @param user user data provided when registering this hook
*/
public void hook(Unicorn u, long address, int size, Object user);
}

View File

@ -0,0 +1,34 @@
/*
Java bindings for the Unicorn Emulator Engine
Copyright(c) 2023 Robert Xiao
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;
/** Callback for {@code UC_HOOK_INSN} with {@code UC_X86_INS_CPUID} */
public interface CpuidHook extends InstructionHook {
/** Called to handle an x86 CPUID instruction.
*
* @param u {@link Unicorn} instance firing this hook
* @param user user data provided when registering this hook
* @return 1 to skip the instruction (marking it as handled),
* 0 to let QEMU handle it
*/
public int hook(Unicorn u, Object user);
}

View File

@ -2,7 +2,7 @@
Java bindings for the Unicorn Emulator Engine
Copyright(c) 2015 Chris Eagle
Copyright(c) 2023 Robert Xiao
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@ -21,7 +21,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
package unicorn;
public interface WriteHook extends Hook {
public void hook(Unicorn u, long address, int size, long value,
Object user);
/** Callback for UC_HOOK_EDGE_GENERATED */
public interface EdgeGeneratedHook extends Hook {
public void hook(Unicorn u, TranslationBlock cur_tb,
TranslationBlock prev_tb, Object user);
}

View File

@ -21,7 +21,24 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
package unicorn;
/** Callback for {@code UC_HOOK_MEM_INVALID}
* (<code>UC_HOOK_MEM_{READ,WRITE,FETCH}_{UNMAPPED,PROT}</code>) */
public interface EventMemHook extends Hook {
public boolean hook(Unicorn u, long address, int size, long value,
/** Called when an invalid memory access occurs within the registered
* range.
*
* @param u {@link Unicorn} instance firing this hook
* @param type type of the memory access and violation: one of
* <code>UC_MEM_{READ,WRITE,FETCH}_{UNMAPPED,PROT}</code>
* @param address address of the memory access
* @param size size of the memory access
* @param value value written ({@code UC_MEM_WRITE_*} only)
* @param user user data provided when registering this hook
* @return {@code true} to mark the exception as handled, which
* will retry the memory access. If no hooks return
* {@code true}, the memory access will fail and a CPU
* exception will be raised.
*/
public boolean hook(Unicorn u, int type, long address, int size, long value,
Object user);
}

View File

@ -21,10 +21,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
package unicorn;
/**
* Base class for all unicorn hooking interfaces
*/
/** Base interface for all Unicorn hooks */
public interface Hook {
}

View File

@ -21,6 +21,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
package unicorn;
public interface InHook extends Hook {
/** Callback for {@code UC_HOOK_INSN} with {@code UC_X86_INS_IN} */
public interface InHook extends InstructionHook {
/** Called to handle an x86 IN instruction.
*
* @param u {@link Unicorn} instance firing this hook
* @param port I/O port number
* @param size size of the request (1, 2, or 4 bytes)
* @param user user data provided when registering this hook
* @return value of the I/O request
*/
public int hook(Unicorn u, int port, int size, Object user);
}

View File

@ -21,6 +21,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
package unicorn;
public interface ReadHook extends Hook {
public void hook(Unicorn u, long address, int size, Object user);
/** Base interface for {@code UC_HOOK_INSN} hooks */
public interface InstructionHook extends Hook {
}

View File

@ -21,6 +21,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
package unicorn;
/** Callback for {@code UC_HOOK_INTR} */
public interface InterruptHook extends Hook {
/** Called when a CPU interrupt occurs.
*
* @param u {@link Unicorn} instance firing this hook
* @param intno CPU-specific interrupt number
* @param user user data provided when registering this hook
*/
public void hook(Unicorn u, int intno, Object user);
}

View File

@ -0,0 +1,36 @@
/*
Java bindings for the Unicorn Emulator Engine
Copyright(c) 2023 Robert Xiao
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;
/* Callback for {@code UC_HOOK_INSN_INVALID} */
public interface InvalidInstructionHook extends Hook {
/** Called when an invalid instruction is encountered.
*
* @param u {@link Unicorn} instance firing this hook
* @param user user data provided when registering this hook
* @return {@code true} to mark the exception as handled. Emulation
* will stop without raising an invalid instruction exception.
* If no hooks return {@code true}, emulation will stop with
* an invalid instruction exception.
*/
public boolean hook(Unicorn u, Object user);
}

View File

@ -21,6 +21,22 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
package unicorn;
public interface MemHook extends ReadHook, WriteHook {
/** Callback for {@code UC_HOOK_MEM_VALID}
* (<code>UC_HOOK_MEM_{READ,WRITE,FETCH}</code> and/or
* {@code UC_HOOK_MEM_READ_AFTER}) */
public interface MemHook extends Hook {
/** Called when a valid memory access occurs within the registered range.
*
* @param u {@link Unicorn} instance firing this hook
* @param type type of the memory access: one of {@code UC_MEM_READ},
* {@code UC_MEM_WRITE} or {@code UC_MEM_READ_AFTER}.
* @param address address of the memory access
* @param size size of the memory access
* @param value value read ({@code UC_MEM_READ_AFTER} only) or written
* ({@code UC_MEM_WRITE} only). Not meaningful for
* {@code UC_MEM_READ} events.
* @param user user data provided when registering this hook
*/
public void hook(Unicorn u, int type, long address, int size, long value,
Object user);
}

View File

@ -31,4 +31,10 @@ public class MemRegion {
this.end = end;
this.perms = perms;
}
@Override
public String toString() {
return "MemRegion [begin=" + begin + ", end=" + end + ", perms=" +
perms + "]";
}
}

View File

@ -0,0 +1,37 @@
/*
Java bindings for the Unicorn Emulator Engine
Copyright(c) 2023 Robert Xiao
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;
/** Interface for handling reads from memory-mapped I/O, mapped via
* {@link Unicorn#mmio_map} */
public interface MmioReadHandler {
/** Called when a memory read is made to an address in the mapped range.
*
* @param u {@link Unicorn} instance firing this hook
* @param offset offset of the request address from the start of the
* mapped range
* @param size size of the memory access, in bytes
* @param user user data provided when registering this hook
* @return value of this I/O request
*/
long read(Unicorn u, long offset, int size, Object user);
}

View File

@ -0,0 +1,37 @@
/*
Java bindings for the Unicorn Emulator Engine
Copyright(c) 2023 Robert Xiao
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;
/** Interface for handling writes to memory-mapped I/O, mapped via
* {@link Unicorn#mmio_map} */
public interface MmioWriteHandler {
/** Called when a memory write is made to an address in the mapped range.
*
* @param u {@link Unicorn} instance firing this hook
* @param offset offset of the request address from the start of the
* mapped range
* @param size size of the memory access, in bytes
* @param value value being written
* @param user user data provided when registering this hook
*/
void write(Unicorn u, long offset, int size, long value, Object user_data);
}

View File

@ -21,6 +21,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
package unicorn;
public interface OutHook extends Hook {
/** Callback for {@code UC_HOOK_INSN} with {@code UC_X86_INS_OUT} */
public interface OutHook extends InstructionHook {
/** Called to handle an x86 OUT instruction.
*
* @param u {@link Unicorn} instance firing this hook
* @param port I/O port number
* @param size size of the request (1, 2, or 4 bytes)
* @param user user data provided when registering this hook
*/
public void hook(Unicorn u, int port, int size, int value, Object user);
}

View File

@ -21,6 +21,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
package unicorn;
public interface SyscallHook extends Hook {
/** Callback for {@code UC_HOOK_INSN} with {@code UC_X86_INS_SYSCALL} or
* {@code UC_X86_INS_SYSENTER} */
public interface SyscallHook extends InstructionHook {
/** Called to handle an x86 SYSCALL or SYSENTER instruction.
*
* @param u {@link Unicorn} instance firing this hook
* @param user user data provided when registering this hook
*/
public void hook(Unicorn u, Object user);
}

View File

@ -0,0 +1,40 @@
/*
Java bindings for the Unicorn Emulator Engine
Copyright(c) 2023 Robert Xiao
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;
/** Callback for {@code UC_HOOK_TCG_OPCODE} */
public interface TcgOpcodeHook extends Hook {
/** Called on every instruction of the registered type(s) within the
* registered range. For example, a {@code UC_TCG_OP_SUB} hook fires on
* every instruction that contains a subtraction operation, unless
* otherwise filtered.
*
* @param u {@link Unicorn} instance firing this hook
* @param address address of the instruction
* @param arg1 first argument to the instruction
* @param arg2 second argument to the instruction
* @param size size of the operands (currently, 32 or 64)
* @param user user data provided when registering this hook
*/
public void hook(Unicorn u, long address, long arg1, long arg2, int size,
Object user);
}

View File

@ -0,0 +1,42 @@
/*
Java bindings for the Unicorn Emulator Engine
Copyright(c) 2023 Robert Xiao
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;
/** Callback for {@code UC_HOOK_TLB_FILL} */
public interface TlbFillHook extends Hook {
/** Called to map a virtual address within the registered range to a
* physical address. The resulting mapping is cached in the QEMU TLB.
* These hooks are only called if the TLB mode (set via
* {@link Unicorn#ctl_tlb_mode}) is set to {@code UC_TLB_VIRTUAL}.
*
* @param u {@link Unicorn} instance firing this hook
* @param vaddr virtual address being mapped
* @param type type of memory access ({@code UC_MEM_READ},
* {@code UC_MEM_WRITE} or {@code UC_MEM_FETCH}).
* @param user user data provided when registering this hook
* @return the page-aligned physical address ORed with the page
* protection bits ({@code UC_PROT_*}). Return -1L to
* indicate an unmapped address; if all hooks return -1L,
* the memory access will fail and raise a CPU exception.
*/
public long hook(Unicorn u, long vaddr, int type, Object user);
}

View File

@ -0,0 +1,35 @@
/*
Java bindings for the Unicorn Emulator Engine
Copyright(c) 2023 Robert Xiao
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;
/** uc_tb */
public class TranslationBlock {
public long pc;
public int icount;
public int size;
public TranslationBlock(long pc, int icount, int size) {
this.pc = pc;
this.icount = icount;
this.size = size;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,72 @@
/*
Java bindings for the Unicorn Emulator Engine
Copyright(c) 2023 Robert Xiao
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_Float80 {
public long mantissa;
public short exponent;
public X86_Float80(long mantissa, short exponent) {
this.mantissa = mantissa;
this.exponent = exponent;
}
public double toDouble() {
boolean sign = (exponent & 0x8000) != 0;
int exp = exponent & 0x7fff;
if (exp == 0) {
return sign ? -0.0 : 0.0;
} else if (exp == 0x7fff) {
if (((mantissa >> 62) & 1) == 0) {
return sign ? Double.NEGATIVE_INFINITY
: Double.POSITIVE_INFINITY;
} else {
return Double.NaN;
}
} else {
exp -= 16383;
double f = mantissa >>> 1;
return Math.scalb(sign ? -f : f, exp - 62);
}
}
public static X86_Float80 fromDouble(double val) {
if (Double.isNaN(val)) {
return new X86_Float80(-1L, (short) -1);
} else if (Double.isInfinite(val)) {
return new X86_Float80(1L << 63,
(short) (val < 0 ? 0xffff : 0x7fff));
} else {
int exp = Math.getExponent(val);
long mantissa = ((long) Math.scalb(Math.abs(val), 62 - exp)) << 1;
exp += 16383;
return new X86_Float80(mantissa,
(short) (val < 0 ? (exp | 0x8000) : exp));
}
}
@Override
public String toString() {
return "X86_Float80 [mantissa=" + mantissa + ", exponent=" + exponent +
"]";
}
}

View File

@ -21,6 +21,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
package unicorn;
/** Memory-Management Register for instructions IDTR, GDTR, LDTR, TR. */
public class X86_MMR {
public long base;
public int limit;
@ -40,4 +41,10 @@ public class X86_MMR {
selector = 0;
flags = 0;
}
@Override
public String toString() {
return "X86_MMR [base=" + base + ", limit=" + limit + ", flags=" +
flags + ", selector=" + selector + "]";
}
}

View File

@ -0,0 +1,38 @@
/*
Java bindings for the Unicorn Emulator Engine
Copyright(c) 2023 Robert Xiao
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;
/** Model-specific register */
public class X86_MSR {
public int rid;
public long value;
public X86_MSR(int rid, long value) {
this.rid = rid;
this.value = value;
}
@Override
public String toString() {
return "X86_MSR [rid=" + rid + ", value=" + value + "]";
}
}

File diff suppressed because it is too large Load Diff