diff --git a/bindings/java/Makefile b/bindings/java/Makefile
index 245bfd35..65712f44 100644
--- a/bindings/java/Makefile
+++ b/bindings/java/Makefile
@@ -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
diff --git a/bindings/java/samples/SampleNetworkAuditing.java b/bindings/java/samples/SampleNetworkAuditing.java
index 0aa27bc2..d41d4061 100644
--- a/bindings/java/samples/SampleNetworkAuditing.java
+++ b/bindings/java/samples/SampleNetworkAuditing.java
@@ -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);
diff --git a/bindings/java/samples/Sample_x86.java b/bindings/java/samples/Sample_x86.java
index 53ea1429..6dea2849 100644
--- a/bindings/java/samples/Sample_x86.java
+++ b/bindings/java/samples/Sample_x86.java
@@ -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.
diff --git a/bindings/java/samples/Sample_x86_mmr.java b/bindings/java/samples/Sample_x86_mmr.java
index c07b48d3..41378d74 100644
--- a/bindings/java/samples/Sample_x86_mmr.java
+++ b/bindings/java/samples/Sample_x86_mmr.java
@@ -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);
diff --git a/bindings/java/tests/FunctionalityTests.java b/bindings/java/tests/FunctionalityTests.java
new file mode 100644
index 00000000..ad3d6393
--- /dev/null
+++ b/bindings/java/tests/FunctionalityTests.java
@@ -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();
+ }
+}
diff --git a/bindings/java/tests/RegressionTests.java b/bindings/java/tests/RegressionTests.java
index 3191b7bf..af5cdb05 100644
--- a/bindings/java/tests/RegressionTests.java
+++ b/bindings/java/tests/RegressionTests.java
@@ -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 };
diff --git a/bindings/java/unicorn/Arm64SysHook.java b/bindings/java/unicorn/Arm64SysHook.java
new file mode 100644
index 00000000..067fd6e3
--- /dev/null
+++ b/bindings/java/unicorn/Arm64SysHook.java
@@ -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);
+}
diff --git a/bindings/java/unicorn/Arm64_CP.java b/bindings/java/unicorn/Arm64_CP.java
new file mode 100644
index 00000000..6c0e9528
--- /dev/null
+++ b/bindings/java/unicorn/Arm64_CP.java
@@ -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 + "]";
+ }
+}
diff --git a/bindings/java/unicorn/Arm_CP.java b/bindings/java/unicorn/Arm_CP.java
new file mode 100644
index 00000000..278cc272
--- /dev/null
+++ b/bindings/java/unicorn/Arm_CP.java
@@ -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 + "]";
+ }
+}
diff --git a/bindings/java/unicorn/BlockHook.java b/bindings/java/unicorn/BlockHook.java
index 1ade7a2b..c60b3334 100644
--- a/bindings/java/unicorn/BlockHook.java
+++ b/bindings/java/unicorn/BlockHook.java
@@ -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);
}
diff --git a/bindings/java/unicorn/CodeHook.java b/bindings/java/unicorn/CodeHook.java
index e78a9803..c7bc83bc 100644
--- a/bindings/java/unicorn/CodeHook.java
+++ b/bindings/java/unicorn/CodeHook.java
@@ -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);
}
diff --git a/bindings/java/unicorn/CpuidHook.java b/bindings/java/unicorn/CpuidHook.java
new file mode 100644
index 00000000..6ddcab40
--- /dev/null
+++ b/bindings/java/unicorn/CpuidHook.java
@@ -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);
+}
diff --git a/bindings/java/unicorn/WriteHook.java b/bindings/java/unicorn/EdgeGeneratedHook.java
similarity index 75%
rename from bindings/java/unicorn/WriteHook.java
rename to bindings/java/unicorn/EdgeGeneratedHook.java
index 25c0208a..1564d752 100644
--- a/bindings/java/unicorn/WriteHook.java
+++ b/bindings/java/unicorn/EdgeGeneratedHook.java
@@ -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);
}
diff --git a/bindings/java/unicorn/EventMemHook.java b/bindings/java/unicorn/EventMemHook.java
index 8262b179..bd110063 100644
--- a/bindings/java/unicorn/EventMemHook.java
+++ b/bindings/java/unicorn/EventMemHook.java
@@ -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}
+ * (UC_HOOK_MEM_{READ,WRITE,FETCH}_{UNMAPPED,PROT}) */
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
+ * UC_MEM_{READ,WRITE,FETCH}_{UNMAPPED,PROT}
+ * @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);
}
diff --git a/bindings/java/unicorn/Hook.java b/bindings/java/unicorn/Hook.java
index 97e0fc26..57168a56 100644
--- a/bindings/java/unicorn/Hook.java
+++ b/bindings/java/unicorn/Hook.java
@@ -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 {
}
diff --git a/bindings/java/unicorn/InHook.java b/bindings/java/unicorn/InHook.java
index 9f0c978b..cea07886 100644
--- a/bindings/java/unicorn/InHook.java
+++ b/bindings/java/unicorn/InHook.java
@@ -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);
}
diff --git a/bindings/java/unicorn/ReadHook.java b/bindings/java/unicorn/InstructionHook.java
similarity index 86%
rename from bindings/java/unicorn/ReadHook.java
rename to bindings/java/unicorn/InstructionHook.java
index 28b6ca34..5d540070 100644
--- a/bindings/java/unicorn/ReadHook.java
+++ b/bindings/java/unicorn/InstructionHook.java
@@ -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 {
+
}
diff --git a/bindings/java/unicorn/InterruptHook.java b/bindings/java/unicorn/InterruptHook.java
index 3702e887..ae670160 100644
--- a/bindings/java/unicorn/InterruptHook.java
+++ b/bindings/java/unicorn/InterruptHook.java
@@ -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);
}
diff --git a/bindings/java/unicorn/InvalidInstructionHook.java b/bindings/java/unicorn/InvalidInstructionHook.java
new file mode 100644
index 00000000..1edb509a
--- /dev/null
+++ b/bindings/java/unicorn/InvalidInstructionHook.java
@@ -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);
+}
diff --git a/bindings/java/unicorn/MemHook.java b/bindings/java/unicorn/MemHook.java
index 3151c84d..4de5ea77 100644
--- a/bindings/java/unicorn/MemHook.java
+++ b/bindings/java/unicorn/MemHook.java
@@ -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}
+ * (UC_HOOK_MEM_{READ,WRITE,FETCH} 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);
}
diff --git a/bindings/java/unicorn/MemRegion.java b/bindings/java/unicorn/MemRegion.java
index 1e4733dd..20fa9200 100644
--- a/bindings/java/unicorn/MemRegion.java
+++ b/bindings/java/unicorn/MemRegion.java
@@ -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 + "]";
+ }
}
diff --git a/bindings/java/unicorn/MmioReadHandler.java b/bindings/java/unicorn/MmioReadHandler.java
new file mode 100644
index 00000000..adfd42ea
--- /dev/null
+++ b/bindings/java/unicorn/MmioReadHandler.java
@@ -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);
+}
diff --git a/bindings/java/unicorn/MmioWriteHandler.java b/bindings/java/unicorn/MmioWriteHandler.java
new file mode 100644
index 00000000..20eb643c
--- /dev/null
+++ b/bindings/java/unicorn/MmioWriteHandler.java
@@ -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);
+}
diff --git a/bindings/java/unicorn/OutHook.java b/bindings/java/unicorn/OutHook.java
index 674e88e3..4f9b33be 100644
--- a/bindings/java/unicorn/OutHook.java
+++ b/bindings/java/unicorn/OutHook.java
@@ -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);
}
diff --git a/bindings/java/unicorn/SyscallHook.java b/bindings/java/unicorn/SyscallHook.java
index bf6476db..a05bb5fc 100644
--- a/bindings/java/unicorn/SyscallHook.java
+++ b/bindings/java/unicorn/SyscallHook.java
@@ -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);
}
diff --git a/bindings/java/unicorn/TcgOpcodeHook.java b/bindings/java/unicorn/TcgOpcodeHook.java
new file mode 100644
index 00000000..a9e7c423
--- /dev/null
+++ b/bindings/java/unicorn/TcgOpcodeHook.java
@@ -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);
+}
diff --git a/bindings/java/unicorn/TlbFillHook.java b/bindings/java/unicorn/TlbFillHook.java
new file mode 100644
index 00000000..270fc44f
--- /dev/null
+++ b/bindings/java/unicorn/TlbFillHook.java
@@ -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);
+}
diff --git a/bindings/java/unicorn/TranslationBlock.java b/bindings/java/unicorn/TranslationBlock.java
new file mode 100644
index 00000000..755534c1
--- /dev/null
+++ b/bindings/java/unicorn/TranslationBlock.java
@@ -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;
+ }
+}
diff --git a/bindings/java/unicorn/Unicorn.java b/bindings/java/unicorn/Unicorn.java
index 9488da6a..d28f3999 100644
--- a/bindings/java/unicorn/Unicorn.java
+++ b/bindings/java/unicorn/Unicorn.java
@@ -2,7 +2,7 @@
Java bindings for the Unicorn Emulator Engine
-Copyright(c) 2015 Chris Eagle
+Copyright(c) 2015 Chris Eagle, 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,324 +21,90 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
package unicorn;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
+import java.math.BigInteger;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
import java.util.Hashtable;
public class Unicorn
- implements UnicornConst, ArmConst, Arm64Const, M68kConst, SparcConst,
- MipsConst, X86Const {
+ implements UnicornConst, Arm64Const, ArmConst, M68kConst, MipsConst,
+ PpcConst, RiscvConst, S390xConst, SparcConst, TriCoreConst, X86Const {
- private long eng;
+ private long nativePtr;
private int arch;
private int mode;
+ private Hashtable hooks = new Hashtable<>();
- private long blockHandle = 0;
- private long interruptHandle = 0;
- private long codeHandle = 0;
+ /** Wrapper around a registered hook */
+ private static class HookWrapper {
+ private long nativePtr;
- private Hashtable eventMemHandles =
- new Hashtable();
- private long readInvalidHandle = 0;
- private long writeInvalidHandle = 0;
- private long fetchProtHandle = 0;
- private long readProtHandle = 0;
- private long writeProtHandle = 0;
-
- private long readHandle = 0;
- private long writeHandle = 0;
- private long inHandle = 0;
- private long outHandle = 0;
- private long syscallHandle = 0;
-
- private class Tuple {
- public Hook function;
- public Object data;
-
- public Tuple(Hook f, Object d) {
- function = f;
- data = d;
+ @Override
+ protected void finalize() {
+ _hookwrapper_free(nativePtr);
}
}
- private ArrayList blockList = new ArrayList();
- private ArrayList intrList = new ArrayList();
- private ArrayList codeList = new ArrayList();
- private ArrayList readList = new ArrayList();
- private ArrayList writeList = new ArrayList();
- private ArrayList inList = new ArrayList();
- private ArrayList outList = new ArrayList();
- private ArrayList syscallList = new ArrayList();
+ public static class Context {
+ long nativePtr;
+ public int arch;
+ public int mode;
- private Hashtable> eventMemLists =
- new Hashtable<>();
+ @Override
+ protected void finalize() {
+ _context_free(nativePtr);
+ }
- private ArrayList> allLists = new ArrayList<>();
+ /**
+ * Read register value. See {@link Unicorn#reg_read(int)}.
+ *
+ * @param regid Register ID that is to be retrieved. This function only supports
+ * integer registers at most 64 bits long.
+ * @return value of the register.
+ */
+ public long reg_read(int regid) throws UnicornException {
+ return do_reg_read_long(nativePtr, 1, arch, regid);
+ }
- private static Hashtable eventMemMap = new Hashtable<>();
- private static Hashtable> unicorns =
- new Hashtable<>();
+ /**
+ * Read register value. See {@link Unicorn#reg_read(int, Object)}.
+ *
+ * @param regid Register ID that is to be retrieved.
+ * @param opt Options for this register, or null if no options are required.
+ * @return value of the register - Long, BigInteger, or structure.
+ */
+ public Object reg_read(int regid, Object opt) throws UnicornException {
+ return do_reg_read_obj(nativePtr, 1, arch, regid, opt);
+ }
+
+ /**
+ * Write to register. See {@link Unicorn#reg_write(int, long)}.
+ *
+ * @param regid Register ID that is to be modified.
+ * @param value Object containing the new register value.
+ */
+ public void reg_write(int regid, long value) throws UnicornException {
+ do_reg_write_long(nativePtr, 1, arch, regid, value);
+ }
+
+ /**
+ * Write to register. See {@link Unicorn#reg_write(int, Object)}.
+ *
+ * @param regid Register ID that is to be modified.
+ * @param value Object containing the new register value.
+ */
+ public void reg_write(int regid, Object value) throws UnicornException {
+ do_reg_write_obj(nativePtr, 1, arch, regid, value);
+ }
+ }
- // required to load native method implementations
static {
// load libunicorn_java.{so,dylib} or unicorn_java.dll
System.loadLibrary("unicorn_java");
- eventMemMap.put(UC_HOOK_MEM_READ_UNMAPPED, UC_MEM_READ_UNMAPPED);
- eventMemMap.put(UC_HOOK_MEM_WRITE_UNMAPPED, UC_MEM_WRITE_UNMAPPED);
- eventMemMap.put(UC_HOOK_MEM_FETCH_UNMAPPED, UC_MEM_FETCH_UNMAPPED);
- eventMemMap.put(UC_HOOK_MEM_READ_PROT, UC_MEM_READ_PROT);
- eventMemMap.put(UC_HOOK_MEM_WRITE_PROT, UC_MEM_WRITE_PROT);
- eventMemMap.put(UC_HOOK_MEM_FETCH_PROT, UC_MEM_FETCH_PROT);
- eventMemMap.put(UC_HOOK_MEM_READ, UC_MEM_READ);
- eventMemMap.put(UC_HOOK_MEM_WRITE, UC_MEM_WRITE);
- eventMemMap.put(UC_HOOK_MEM_FETCH, UC_MEM_FETCH);
- eventMemMap.put(UC_HOOK_MEM_READ_AFTER, UC_MEM_READ_AFTER);
}
- /**
- * Invoke all UC_HOOK_BLOCK callbacks registered for a specific Unicorn.
- * This function gets invoked from the native C callback registered for
- * for UC_HOOK_BLOCK
- *
- * @param eng A Unicorn uc_engine* eng returned by uc_open
- * @param address The address of the instruction being executed
- * @param size The size of the basic block being executed
- * @see hook_add, unicorn.BlockHook
- */
- private static void invokeBlockCallbacks(long eng, long address, int size) {
- Unicorn u = unicorns.get(eng).get();
- if (u != null) {
- for (Tuple p : u.blockList) {
- BlockHook bh = (BlockHook) p.function;
- bh.hook(u, address, size, p.data);
- }
- }
- }
-
- /**
- * Invoke all UC_HOOK_INTR callbacks registered for a specific Unicorn.
- * This function gets invoked from the native C callback registered for
- * for UC_HOOK_INTR
- *
- * @param eng A Unicorn uc_engine* eng returned by uc_open
- * @param intno The interrupt number
- * @see hook_add, unicorn.InterruptHook
- */
- private static void invokeInterruptCallbacks(long eng, int intno) {
- Unicorn u = unicorns.get(eng).get();
- if (u != null) {
- for (Tuple p : u.intrList) {
- InterruptHook ih = (InterruptHook) p.function;
- ih.hook(u, intno, p.data);
- }
- }
- }
-
- /**
- * Invoke all UC_HOOK_CODE callbacks registered for a specific Unicorn.
- * This function gets invoked from the native C callback registered for
- * for UC_HOOK_CODE
- *
- * @param eng A Unicorn uc_engine* eng returned by uc_open
- * @param address The address of the instruction being executed
- * @param size The size of the instruction being executed
- * @see hook_add, unicorn.CodeHook
- */
- private static void invokeCodeCallbacks(long eng, long address, int size) {
- Unicorn u = unicorns.get(eng).get();
- if (u != null) {
- for (Tuple p : u.codeList) {
- CodeHook ch = (CodeHook) p.function;
- ch.hook(u, address, size, p.data);
- }
- }
- }
-
- /**
- * Invoke all UC_HOOK_MEM_XXX_UNMAPPED and/or UC_HOOK_MEM_XXX_PROT callbacks
- * registered
- * for a specific Unicorn.
- * This function gets invoked from the native C callback registered for
- * for UC_HOOK_MEM_XXX_UNMAPPED or UC_HOOK_MEM_XXX_PROT
- *
- * @param eng A Unicorn uc_engine* eng returned by uc_open
- * @param type The type of event that is taking place
- * @param address Address of instruction being executed
- * @param size Size of data being read or written
- * @param value Value of data being written to memory, or irrelevant if type =
- * READ.
- * @return true to continue, or false to stop program (due to invalid memory).
- * @see hook_add, unicorn.EventMemHook
- */
- private static boolean invokeEventMemCallbacks(long eng, int type,
- long address, int size,
- long value) {
- Unicorn u = unicorns.get(eng).get();
- boolean result = true;
- if (u != null) {
- ArrayList funcList = u.eventMemLists.get(type);
- if (funcList != null) {
- for (Tuple p : funcList) {
- EventMemHook emh = (EventMemHook) p.function;
- result &= emh.hook(u, address, size, value, p.data);
- }
- }
- }
- return result;
- }
-
- /**
- * Invoke all UC_HOOK_MEM_READ callbacks registered for a specific Unicorn.
- * This function gets invoked from the native C callback registered for
- * for UC_HOOK_MEM_READ
- *
- * @param eng A Unicorn uc_engine* eng returned by uc_open
- * @param address Address of instruction being executed
- * @param size Size of data being read
- * @see hook_add, unicorn.ReadHook
- */
- private static void invokeReadCallbacks(long eng, long address, int size) {
- Unicorn u = unicorns.get(eng).get();
- if (u != null) {
- for (Tuple p : u.readList) {
- ReadHook rh = (ReadHook) p.function;
- rh.hook(u, address, size, p.data);
- }
- }
- }
-
- /**
- * Invoke all UC_HOOK_MEM_WRITE callbacks registered for a specific Unicorn.
- * This function gets invoked from the native C callback registered for
- * for UC_HOOK_MEM_WRITE
- *
- * @param eng A Unicorn uc_engine* eng returned by uc_open
- * @param address Address of instruction being executed
- * @param size Size of data being read
- * @param value value being written
- * @see hook_add, unicorn.WriteHook
- */
- private static void invokeWriteCallbacks(long eng, long address, int size,
- long value) {
- Unicorn u = unicorns.get(eng).get();
- if (u != null) {
- for (Tuple p : u.writeList) {
- WriteHook wh = (WriteHook) p.function;
- wh.hook(u, address, size, value, p.data);
- }
- }
- }
-
- /**
- * Invoke all UC_HOOK_INSN callbacks registered for a specific Unicorn.
- * This is specifically for the x86 IN instruction.
- * This function gets invoked from the native C callback registered for
- * for UC_HOOK_INSN
- *
- * @param eng A Unicorn uc_engine* eng returned by uc_open
- * @param port I/O Port number
- * @param size Data size (1/2/4) to be read from this port
- * @return Data supplied from the input port
- * @see hook_add, unicorn.InHook
- */
- private static int invokeInCallbacks(long eng, int port, int size) {
- Unicorn u = unicorns.get(eng).get();
- int result = 0;
- if (u != null) {
- for (Tuple p : u.inList) {
- InHook ih = (InHook) p.function;
- result = ih.hook(u, port, size, p.data);
- }
- }
- return result;
- }
-
- /**
- * Invoke all UC_HOOK_INSN callbacks registered for a specific Unicorn.
- * This is specifically for the x86 OUT instruction.
- * This function gets invoked from the native C callback registered for
- * for UC_HOOK_INSN
- *
- * @param eng A Unicorn uc_engine* eng returned by uc_open
- * @param port I/O Port number
- * @param size Data size (1/2/4) to be written to this port
- * @see hook_add, unicorn.OutHook
- */
- private static void invokeOutCallbacks(long eng, int port, int size,
- int value) {
- Unicorn u = unicorns.get(eng).get();
- int result = 0;
- if (u != null) {
- for (Tuple p : u.outList) {
- OutHook oh = (OutHook) p.function;
- oh.hook(u, port, size, value, p.data);
- }
- }
- }
-
- /**
- * Invoke all UC_HOOK_INSN callbacks registered for a specific Unicorn.
- * This is specifically for the x86 SYSCALL and SYSENTER instruction.
- * This function gets invoked from the native C callback registered for
- * for UC_HOOK_INSN
- *
- * @param eng A Unicorn uc_engine* eng returned by uc_open
- * @see hook_add, unicorn.SyscallHook
- */
- private static void invokeSyscallCallbacks(long eng) {
- Unicorn u = unicorns.get(eng).get();
- int result = 0;
- if (u != null) {
- for (Tuple p : u.syscallList) {
- SyscallHook sh = (SyscallHook) p.function;
- sh.hook(u, p.data);
- }
- }
- }
-
- /**
- * 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
- *
- * @param arch Architecture type (UC_ARCH_*)
- * @param mode Hardware mode. This is combined of UC_MODE_*
- */
- private native long open(int arch, int mode) throws UnicornException;
-
/**
* Create a new Unicorn object
*
@@ -351,22 +117,26 @@ public class Unicorn
// remember these in case we need arch specific code
this.arch = arch;
this.mode = mode;
- eng = open(arch, mode);
- unicorns.put(eng, new WeakReference<>(this));
- allLists.add(blockList);
- allLists.add(intrList);
- allLists.add(codeList);
- allLists.add(readList);
- allLists.add(writeList);
- allLists.add(inList);
- allLists.add(outList);
- allLists.add(syscallList);
+ nativePtr = _open(arch, mode);
+ }
+
+ /**
+ * Close the underlying uc_engine* eng associated with this Unicorn object
+ *
+ */
+
+ public void close() throws UnicornException {
+ if (nativePtr != 0) {
+ _close(nativePtr);
+ nativePtr = 0;
+ }
}
/**
* Perform native cleanup tasks associated with a Unicorn object
*
*/
+ @Override
protected void finalize() {
close();
}
@@ -379,7 +149,9 @@ public class Unicorn
*
* For example Unicorn version 1.2 would yield 0x0102
*/
- public native static int version();
+ public static int version() {
+ return _version();
+ }
/**
* Determine if the given architecture is supported by this library.
@@ -388,158 +160,10 @@ public class Unicorn
* @return true if this library supports the given arch.
* @see unicorn.UnicornConst
*/
- public native static boolean arch_supported(int arch);
-
- /**
- * Close the underlying uc_engine* eng associated with this Unicorn object
- *
- */
- private native void _close() throws UnicornException;
-
- public void close() throws UnicornException {
- if (eng != 0) {
- _close();
- unicorns.remove(eng);
- eng = 0;
- }
+ public static boolean arch_supported(int arch) {
+ return _arch_supported(arch);
}
- /**
- * 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.UnicornConst
- */
- public native int errno();
-
- /**
- * Return a string describing given error code.
- *
- * @param code Error code (see UC_ERR_* above)
- * @return Returns a String that describes the error code
- * @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_read(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.
- * @return Object containing the requested register value. Long, BigInteger, or
- * other custom class used to represent register values
- */
- 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);
- }
- }
-
- /**
- * Batch write register values. regids.length == vals.length or UC_ERR_ARG
- *
- * @param regids Array of register IDs to be written.
- * @param vals Array of register values to be written.
- */
- public void reg_write_batch(int regids[], Object vals[])
- throws UnicornException {
- if (regids.length != vals.length) {
- throw new UnicornException(strerror(UC_ERR_ARG));
- }
- for (int i = 0; i < regids.length; i++) {
- reg_write(regids[i], vals[i]);
- }
- }
-
- /**
- * Batch read register values.
- *
- * @param regids Array of register IDs to be read.
- * @return Array containing the requested register values.
- */
- public Object[] reg_read_batch(int regids[]) throws UnicornException {
- Object[] vals = new Object[regids.length];
- for (int i = 0; i < regids.length; i++) {
- vals[i] = reg_read(regids[i]);
- }
- return vals;
- }
-
- /**
- * Write to memory.
- *
- * @param address Start addres of the memory region to be written.
- * @param bytes The values to be written into memory. bytes.length bytes will
- * be written.
- */
- public native void mem_write(long address, byte[] bytes)
- throws UnicornException;
-
- /**
- * Read memory contents.
- *
- * @param address Start addres of the memory region to be read.
- * @param size Number of bytes to be retrieved.
- * @return Byte array containing the contents of the requested memory range.
- */
- public native byte[] mem_read(long address, long size)
- throws UnicornException;
-
/**
* Emulate machine code in a specific duration of time.
*
@@ -552,100 +176,452 @@ public class Unicorn
* 0, we will emulate all the code available, until the code is
* finished.
*/
- public native void emu_start(long begin, long until, long timeout,
+ public void emu_start(long begin, long until, long timeout,
long count)
- throws UnicornException;
+ throws UnicornException {
+ _emu_start(nativePtr, begin, until, timeout, count);
+ }
/**
* Stop emulation (which was started by emu_start() ).
* This is typically called from callback functions registered via tracing APIs.
* NOTE: for now, this will stop the execution only after the current block.
*/
- public native void emu_stop() throws UnicornException;
+ public void emu_stop() throws UnicornException {
+ _emu_stop(nativePtr);
+ }
- /**
- * Hook registration helper for hook types that require no additional arguments.
- *
- * @param eng Internal unicorn uc_engine* eng associated with hooking Unicorn
- * object
- * @param type UC_HOOK_* hook type
- * @return Unicorn uch returned for registered hook function
- */
- private native static long registerHook(long eng, int type);
-
- /**
- * Hook registration helper for hook types that require one additional argument.
- *
- * @param eng Internal unicorn uc_engine* eng associated with hooking Unicorn
- * object
- * @param type UC_HOOK_* hook type
- * @param arg1 Additional varargs argument
- * @return Unicorn uch returned for registered hook function
- */
- private native static long registerHook(long eng, int type, int arg1);
-
- /**
- * Hook registration helper for hook types that require two additional
- * arguments.
- *
- * @param eng Internal unicorn uc_engine* eng associated with hooking Unicorn
- * object
- * @param type UC_HOOK_* hook type
- * @param arg1 First additional varargs argument
- * @param arg2 Second additional varargs argument
- * @return Unicorn uch returned for registered hook function
- */
- private native static long registerHook(long eng, int type, long arg1,
- long arg2);
-
- /**
- * Hook registration for UC_HOOK_BLOCK hooks. The registered callback function
- * will be
- * invoked when a basic block is entered and the address of the basic block (BB)
- * falls in the
- * range begin <= BB <= end. For the special case in which begin > end, the
- * callback will be
- * invoked whenver any basic block is entered.
- *
- * @param callback Implementation of a BlockHook interface
- * @param begin Start address of hooking range
- * @param end End address of hooking range
- * @param user_data User data to be passed to the callback function each time
- * the event is triggered
- */
- public void hook_add(BlockHook callback, long begin, long end,
- Object user_data)
- throws UnicornException {
- if (blockHandle == 0) {
- blockHandle = registerHook(eng, UC_HOOK_BLOCK, begin, end);
+ private static boolean is_long_register(int arch, int regid) {
+ switch (arch) {
+ case UC_ARCH_X86:
+ return !(regid == UC_X86_REG_IDTR || regid == UC_X86_REG_GDTR ||
+ regid == UC_X86_REG_LDTR || regid == UC_X86_REG_TR ||
+ (regid >= UC_X86_REG_FP0 && regid <= UC_X86_REG_FP7) ||
+ (regid >= UC_X86_REG_ST0 && regid <= UC_X86_REG_ST7) ||
+ (regid >= UC_X86_REG_XMM0 && regid <= UC_X86_REG_XMM31) ||
+ (regid >= UC_X86_REG_YMM0 && regid <= UC_X86_REG_YMM31) ||
+ (regid >= UC_X86_REG_ZMM0 && regid <= UC_X86_REG_ZMM31) ||
+ regid == UC_X86_REG_MSR);
+ case UC_ARCH_ARM:
+ return !(regid == UC_ARM_REG_CP_REG);
+ case UC_ARCH_ARM64:
+ return !(regid == UC_ARM64_REG_CP_REG ||
+ (regid >= UC_ARM64_REG_Q0 && regid <= UC_ARM64_REG_Q31) ||
+ (regid >= UC_ARM64_REG_V0 && regid <= UC_ARM64_REG_V31));
}
- blockList.add(new Tuple(callback, user_data));
+ return true;
+ }
+
+ private static long do_reg_read_long(long ptr, int isContext, int arch,
+ int regid) throws UnicornException {
+ if (is_long_register(arch, regid)) {
+ return _reg_read_long(ptr, isContext, regid);
+ } else {
+ throw new UnicornException("Invalid register for reg_read_long");
+ }
+ }
+
+ private static Object do_reg_read_obj(long ptr, int isContext, int arch,
+ int regid,
+ Object opt) throws UnicornException {
+ switch (arch) {
+ case UC_ARCH_X86:
+ if (regid == UC_X86_REG_IDTR || regid == UC_X86_REG_GDTR ||
+ regid == UC_X86_REG_LDTR || regid == UC_X86_REG_TR) {
+ return _reg_read_x86_mmr(ptr, isContext, regid);
+ } else if ((regid >= UC_X86_REG_FP0 && regid <= UC_X86_REG_FP7) ||
+ (regid >= UC_X86_REG_ST0 && regid <= UC_X86_REG_ST7)) {
+ ByteBuffer b =
+ ByteBuffer.allocate(16).order(ByteOrder.LITTLE_ENDIAN);
+ _reg_read_bytes(ptr, isContext, regid, b.array());
+ return new X86_Float80(b.getLong(0), b.getShort(8));
+ } else if (regid >= UC_X86_REG_XMM0 && regid <= UC_X86_REG_XMM31) {
+ return do_reg_read_bigint(ptr, isContext, regid, 128);
+ } else if (regid >= UC_X86_REG_YMM0 && regid <= UC_X86_REG_YMM31) {
+ return do_reg_read_bigint(ptr, isContext, regid, 256);
+ } else if (regid >= UC_X86_REG_ZMM0 && regid <= UC_X86_REG_ZMM31) {
+ return do_reg_read_bigint(ptr, isContext, regid, 512);
+ } else if (regid == UC_X86_REG_MSR) {
+ X86_MSR reg = (X86_MSR) opt;
+ return (Long) _reg_read_x86_msr(ptr, isContext, reg.rid);
+ }
+ case UC_ARCH_ARM:
+ if (regid == UC_ARM_REG_CP_REG) {
+ Arm_CP reg = (Arm_CP) opt;
+ return (Long) _reg_read_arm_cp(ptr, isContext, reg.cp, reg.is64,
+ reg.sec, reg.crn, reg.crm, reg.opc1, reg.opc2);
+ }
+ case UC_ARCH_ARM64:
+ if (regid == UC_ARM64_REG_CP_REG) {
+ Arm64_CP reg = (Arm64_CP) opt;
+ return (Long) _reg_read_arm64_cp(ptr, isContext, reg.crn,
+ reg.crm, reg.op0, reg.op1, reg.op2);
+ } else if ((regid >= UC_ARM64_REG_Q0 &&
+ regid <= UC_ARM64_REG_Q31) ||
+ (regid >= UC_ARM64_REG_V0 && regid <= UC_ARM64_REG_V31)) {
+ return do_reg_read_bigint(ptr, isContext, regid, 128);
+ }
+ }
+ return _reg_read_long(ptr, isContext, regid);
+ }
+
+ private static void do_reg_write_long(long ptr, int isContext, int arch,
+ int regid, long value) throws UnicornException {
+ if (is_long_register(arch, regid)) {
+ _reg_write_long(ptr, isContext, regid, value);
+ } else {
+ throw new UnicornException("Invalid register for reg_read_long");
+ }
+ }
+
+ private static void do_reg_write_obj(long ptr, int isContext, int arch,
+ int regid,
+ Object value) throws UnicornException {
+ switch (arch) {
+ case UC_ARCH_X86:
+ if (regid == UC_X86_REG_IDTR || regid == UC_X86_REG_GDTR ||
+ regid == UC_X86_REG_LDTR || regid == UC_X86_REG_TR) {
+ X86_MMR reg = (X86_MMR) value;
+ _reg_write_x86_mmr(ptr, isContext, regid, reg.selector,
+ reg.base, reg.limit, reg.flags);
+ return;
+ } else if ((regid >= UC_X86_REG_FP0 && regid <= UC_X86_REG_FP7) ||
+ (regid >= UC_X86_REG_ST0 && regid <= UC_X86_REG_ST7)) {
+ X86_Float80 reg = (X86_Float80) value;
+ ByteBuffer b =
+ ByteBuffer.allocate(16).order(ByteOrder.LITTLE_ENDIAN);
+ b.putLong(0, reg.mantissa);
+ b.putShort(8, reg.exponent);
+ _reg_write_bytes(ptr, isContext, regid, b.array());
+ return;
+ } else if (regid >= UC_X86_REG_XMM0 && regid <= UC_X86_REG_XMM31) {
+ do_reg_write_bigint(ptr, isContext, regid, (BigInteger) value,
+ 128);
+ return;
+ } else if (regid >= UC_X86_REG_YMM0 && regid <= UC_X86_REG_YMM31) {
+ do_reg_write_bigint(ptr, isContext, regid, (BigInteger) value,
+ 256);
+ return;
+ } else if (regid >= UC_X86_REG_ZMM0 && regid <= UC_X86_REG_ZMM31) {
+ do_reg_write_bigint(ptr, isContext, regid, (BigInteger) value,
+ 512);
+ return;
+ } else if (regid == UC_X86_REG_MSR) {
+ X86_MSR reg = (X86_MSR) value;
+ _reg_write_x86_msr(ptr, isContext, reg.rid, reg.value);
+ return;
+ }
+ case UC_ARCH_ARM:
+ if (regid == UC_ARM_REG_CP_REG) {
+ Arm_CP reg = (Arm_CP) value;
+ _reg_write_arm_cp(ptr, isContext, reg.cp, reg.is64, reg.sec,
+ reg.crn, reg.crm, reg.opc1, reg.opc2, reg.val);
+ return;
+ }
+ case UC_ARCH_ARM64:
+ if (regid == UC_ARM64_REG_CP_REG) {
+ Arm64_CP reg = (Arm64_CP) value;
+ _reg_write_arm64_cp(ptr, isContext, reg.crn, reg.crm, reg.op0,
+ reg.op1, reg.op2, reg.val);
+ return;
+ } else if ((regid >= UC_ARM64_REG_Q0 &&
+ regid <= UC_ARM64_REG_Q31) ||
+ (regid >= UC_ARM64_REG_V0 && regid <= UC_ARM64_REG_V31)) {
+ do_reg_write_bigint(ptr, isContext, regid, (BigInteger) value,
+ 128);
+ return;
+ }
+ }
+ _reg_write_long(ptr, isContext, regid, (Long) value);
+ }
+
+ private static BigInteger do_reg_read_bigint(long ptr, int isContext,
+ int regid,
+ int nbits) {
+
+ byte[] buf = new byte[nbits >> 3];
+ _reg_read_bytes(ptr, isContext, regid, buf);
+ if (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)) {
+ // reverse native buffer to big-endian on little-endian hosts
+ int i = buf.length - 1;
+ int j = 0;
+ while (i > j) {
+ byte temp = buf[i];
+ buf[i] = buf[j];
+ buf[j] = temp;
+ i--;
+ j++;
+ }
+ }
+ return new BigInteger(buf);
+ }
+
+ private static void do_reg_write_bigint(long ptr, int isContext, int regid,
+ BigInteger value, int nbits) {
+ byte[] val = value.toByteArray();
+ if (val.length > (nbits >> 3)) {
+ throw new IllegalArgumentException(
+ "input integer is too large for a " + nbits +
+ "-bit register (got " + (val.length * 8) + " bits)");
+ }
+ byte[] buf = new byte[nbits >> 3];
+ if (val[0] < 0) {
+ Arrays.fill(buf, (byte) 0xff);
+ }
+
+ if (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)) {
+ for (int i = 0; i < val.length; i++) {
+ buf[i] = val[val.length - i - 1];
+ }
+ } else {
+ System.arraycopy(val, 0, buf, buf.length - val.length, val.length);
+ }
+ _reg_write_bytes(ptr, isContext, regid, buf);
}
/**
- * Hook registration for UC_HOOK_INTR hooks. The registered callback function
- * will be
+ * Read register value. This reads any register that would return a Long
+ * from {@link #reg_read(int, Object)}.
+ *
+ * @param regid Register ID that is to be retrieved. This function only supports
+ * integer registers at most 64 bits long.
+ * @return value of the register.
+ */
+ public long reg_read(int regid) throws UnicornException {
+ return do_reg_read_long(nativePtr, 0, arch, regid);
+ }
+
+ /**
+ * Read register value. The return type depends on regid as follows.
+ * opt should be null unless otherwise specified.
+ *
+ * - {@code UC_X86_REG_*TR} => {@link X86_MMR}
+ *
- {@code UC_X86_REG_FP*} => {@link X86_Float80}
+ *
- {@code UC_X86_REG_ST*} => {@link X86_Float80}
+ *
- {@code UC_X86_REG_XMM*} => {@link BigInteger} (128 bits)
+ *
- {@code UC_X86_REG_YMM*} => {@link BigInteger} (256 bits)
+ *
- {@code UC_X86_REG_MSR} (opt: {@link X86_MSR}) => {@link Long}
+ *
- {@code UC_ARM_REG_CP} (opt: {@link Arm_CP}) => {@link Long}
+ *
- {@code UC_ARM64_REG_CP} (opt: {@link Arm64_CP}) => {@link Long}
+ *
- {@code UC_ARM64_REG_Q*} => {@link BigInteger} (128 bits)
+ *
- {@code UC_ARM64_REG_V*} => {@link BigInteger} (128 bits)
+ *
+ *
+ * @param regid Register ID that is to be retrieved.
+ * @param opt Options for this register, or null if no options are required.
+ * @return value of the register - Long, BigInteger, or structure.
+ */
+ public Object reg_read(int regid, Object opt) throws UnicornException {
+ return do_reg_read_obj(nativePtr, 0, arch, regid, opt);
+ }
+
+ /**
+ * Write to register. This sets any register that doesn't require special
+ * options and which is at most 64 bits long.
+ *
+ * @param regid Register ID that is to be modified.
+ * @param value Object containing the new register value.
+ */
+ public void reg_write(int regid, long value) throws UnicornException {
+ do_reg_write_long(nativePtr, 0, arch, regid, value);
+ }
+
+ /**
+ * Write to register. The type of {@code value} depends on regid:
+ *
+ * - {@code UC_X86_REG_*TR} => {@link X86_MMR}
+ *
- {@code UC_X86_REG_FP*} => {@link X86_Float80}
+ *
- {@code UC_X86_REG_ST*} => {@link X86_Float80}
+ *
- {@code UC_X86_REG_XMM*} => {@link BigInteger} (128 bits)
+ *
- {@code UC_X86_REG_YMM*} => {@link BigInteger} (256 bits)
+ *
- {@code UC_X86_REG_MSR} => {@link X86_MSR}
+ *
- {@code UC_ARM_REG_CP} => {@link Arm_CP}
+ *
- {@code UC_ARM64_REG_CP} => {@link Arm64_CP}
+ *
- {@code UC_ARM64_REG_Q*} => {@link BigInteger} (128 bits)
+ *
- {@code UC_ARM64_REG_V*} => {@link BigInteger} (128 bits)
+ *
+ *
+ * @param regid Register ID that is to be modified.
+ * @param value Object containing the new register value.
+ */
+ public void reg_write(int regid, Object value) throws UnicornException {
+ do_reg_write_obj(nativePtr, 0, arch, regid, value);
+ }
+
+ /**
+ * Read from memory.
+ *
+ * @param address Start address of the memory region to be read.
+ * @param size Number of bytes to be retrieved.
+ * @return Byte array containing the contents of the requested memory range.
+ */
+ public byte[] mem_read(long address, int size) throws UnicornException {
+ byte[] result = new byte[size];
+ _mem_read(nativePtr, address, result);
+ return result;
+ }
+
+ /**
+ * Write to memory.
+ *
+ * @param address Start addres of the memory region to be written.
+ * @param bytes The values to be written into memory. bytes.length bytes will
+ * be written.
+ */
+ public void mem_write(long address, byte[] bytes) throws UnicornException {
+ _mem_write(nativePtr, address, bytes);
+ }
+
+ /**
+ * 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 long query(int type) throws UnicornException {
+ return _query(nativePtr, type);
+ }
+
+ /**
+ * 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.UnicornConst
+ */
+ public int errno() {
+ return _errno(nativePtr);
+ }
+
+ /**
+ * Return a string describing given error code.
+ *
+ * @param code Error code (see UC_ERR_* above)
+ * @return Returns a String that describes the error code
+ * @see unicorn.UnicornConst
+ */
+ public static String strerror(int code) {
+ return _strerror(code);
+ }
+
+ public int ctl_get_mode() {
+ return _ctl_get_mode(nativePtr);
+ }
+
+ public int ctl_get_arch() {
+ return _ctl_get_arch(nativePtr);
+ }
+
+ public long ctl_get_timeout() {
+ return _ctl_get_timeout(nativePtr);
+ }
+
+ public int ctl_get_page_size() {
+ return _ctl_get_page_size(nativePtr);
+ }
+
+ public void ctl_set_page_size(int page_size) {
+ _ctl_set_page_size(nativePtr, page_size);
+ }
+
+ public void ctl_exits_enabled(boolean value) {
+ _ctl_set_use_exits(nativePtr, value);
+ }
+
+ public long ctl_get_exits_cnt() {
+ return _ctl_get_exits_cnt(nativePtr);
+ }
+
+ public long[] ctl_get_exits() {
+ return _ctl_get_exits(nativePtr);
+ }
+
+ public void ctl_set_exits(long[] exits) {
+ _ctl_set_exits(nativePtr, exits);
+ }
+
+ public int ctl_get_cpu_model() {
+ return _ctl_get_cpu_model(nativePtr);
+ }
+
+ /**
+ * Set the emulated cpu model.
+ *
+ * @param cpu_model CPU model type (see UC_CPU_*).
+ */
+ public void ctl_set_cpu_model(int cpu_model) {
+ _ctl_set_cpu_model(nativePtr, cpu_model);
+ }
+
+ public TranslationBlock ctl_request_cache(long address) {
+ return _ctl_request_cache(nativePtr, address);
+ }
+
+ public void ctl_remove_cache(long address, long end) {
+ _ctl_remove_cache(nativePtr, address, end);
+ }
+
+ public void ctl_flush_tb() {
+ _ctl_flush_tb(nativePtr);
+ }
+
+ public void ctl_flush_tlb() {
+ _ctl_flush_tlb(nativePtr);
+ }
+
+ public void ctl_tlb_mode(int mode) {
+ _ctl_tlb_mode(nativePtr, mode);
+ }
+
+ private long registerHook(long val) {
+ HookWrapper wrapper = new HookWrapper();
+ wrapper.nativePtr = val;
+ hooks.put(val, wrapper);
+ return val;
+ }
+
+ /**
+ * Register a UC_HOOK_INTR hook. The registered callback function will be
* invoked whenever an interrupt instruction is executed.
*
* @param callback Implementation of a InterruptHook interface
* @param user_data User data to be passed to the callback function each time
* the event is triggered
*/
- public void hook_add(InterruptHook callback, Object user_data)
+ public long hook_add(InterruptHook callback, Object user_data)
throws UnicornException {
- if (interruptHandle == 0) {
- interruptHandle = registerHook(eng, UC_HOOK_INTR);
- }
- intrList.add(new Tuple(callback, user_data));
+ return registerHook(
+ _hook_add(nativePtr, UC_HOOK_INTR, callback, user_data, 1, 0));
}
/**
- * Hook registration for UC_HOOK_CODE hooks. The registered callback function
- * will be
- * invoked when an instruction is executed from the address range begin <= PC <=
- * end. For
- * the special case in which begin > end, the callback will be invoked for ALL
- * instructions.
+ * Register a UC_HOOK_INSN hook. The registered callback function will be
+ * invoked whenever the matching special instruction is executed.
+ * The exact interface called will depend on the instruction being hooked.
+ *
+ * @param callback Implementation of an InstructionHook sub-interface
+ * @param insn UC__INS_ constant, e.g. UC_X86_INS_IN or UC_ARM64_INS_MRS
+ * @param begin Start address of hooking range
+ * @param end End address of hooking range
+ * @param user_data User data to be passed to the callback function each time
+ * the event is triggered
+ */
+ public long hook_add(InstructionHook callback, int insn, long begin,
+ long end,
+ Object user_data)
+ throws UnicornException {
+ return registerHook(_hook_add(nativePtr, UC_HOOK_INSN, callback,
+ user_data, begin, end, insn));
+ }
+
+ /**
+ * Register a UC_HOOK_CODE hook. The registered callback function will be
+ * invoked when an instruction is executed from the address range
+ * begin <= PC <= end. For the special case in which begin > end, the
+ * callback will be invoked for ALL instructions.
*
* @param callback Implementation of a CodeHook interface
* @param begin Start address of hooking range
@@ -653,176 +629,130 @@ public class Unicorn
* @param user_data User data to be passed to the callback function each time
* the event is triggered
*/
- public void hook_add(CodeHook callback, long begin, long end,
+ public long hook_add(CodeHook callback, long begin, long end,
Object user_data)
throws UnicornException {
- if (codeHandle == 0) {
- codeHandle = registerHook(eng, UC_HOOK_CODE, begin, end);
- }
- codeList.add(new Tuple(callback, user_data));
+ return registerHook(_hook_add(nativePtr, UC_HOOK_CODE, callback,
+ user_data, begin, end));
}
/**
- * Hook registration for UC_HOOK_MEM_READ hooks. The registered callback
- * function will be
- * invoked whenever a memory read is performed within the address range begin <=
- * read_addr <= end. For
- * the special case in which begin > end, the callback will be invoked for ALL
- * memory reads.
+ * Register a UC_HOOK_BLOCK hook. The registered callback function will be
+ * invoked when a basic block is entered and the address of the basic block
+ * (BB) falls in the range begin <= BB <= end. For the special case in which
+ * begin > end, the callback will be invoked whenver any basic block is
+ * entered.
*
- * @param callback Implementation of a ReadHook interface
- * @param begin Start address of memory read range
- * @param end End address of memory read range
+ * @param callback Implementation of a BlockHook interface
+ * @param begin Start address of hooking range
+ * @param end End address of hooking range
* @param user_data User data to be passed to the callback function each time
* the event is triggered
*/
- public void hook_add(ReadHook callback, long begin, long end,
+ public long hook_add(BlockHook callback, long begin, long end,
Object user_data)
throws UnicornException {
- if (readHandle == 0) {
- readHandle = registerHook(eng, UC_HOOK_MEM_READ, begin, end);
- }
- readList.add(new Tuple(callback, user_data));
+ return registerHook(_hook_add(nativePtr, UC_HOOK_BLOCK, callback,
+ user_data, begin, end));
}
/**
- * Hook registration for UC_HOOK_MEM_WRITE hooks. The registered callback
- * function will be
- * invoked whenever a memory write is performed within the address range begin
- * <= write_addr <= end. For
- * the special case in which begin > end, the callback will be invoked for ALL
- * memory writes.
- *
- * @param callback Implementation of a WriteHook interface
- * @param begin Start address of memory write range
- * @param end End address of memory write range
- * @param user_data User data to be passed to the callback function each time
- * the event is triggered
- */
- public void hook_add(WriteHook callback, long begin, long end,
- Object user_data)
- throws UnicornException {
- if (writeHandle == 0) {
- writeHandle = registerHook(eng, UC_HOOK_MEM_WRITE, begin, end);
- }
- writeList.add(new Tuple(callback, user_data));
- }
-
- /**
- * Hook registration for UC_HOOK_MEM_WRITE | UC_HOOK_MEM_WRITE hooks. The
- * registered callback function will be
- * invoked whenever a memory write or read is performed within the address range
- * begin <= addr <= end. For
- * the special case in which begin > end, the callback will be invoked for ALL
- * memory writes.
+ * Register a UC_HOOK_MEM_VALID hook (UC_HOOK_MEM_{READ,WRITE_FETCH} and/or
+ * UC_HOOK_MEM_READ_AFTER. The registered callback function will be
+ * invoked whenever a corresponding memory operation is performed within the
+ * address range begin <= addr <= end. For the special case in which
+ * begin > end, the callback will be invoked for ALL memory operations.
*
* @param callback Implementation of a MemHook interface
+ * @param type UC_HOOK_MEM_* constant for a valid memory event
* @param begin Start address of memory range
* @param end End address of memory range
* @param user_data User data to be passed to the callback function each time
* the event is triggered
*/
- public void hook_add(MemHook callback, long begin, long end,
+ public long hook_add(MemHook callback, int type, long begin, long end,
Object user_data)
throws UnicornException {
- hook_add((ReadHook) callback, begin, end, user_data);
- hook_add((WriteHook) callback, begin, end, user_data);
+ return registerHook(
+ _hook_add(nativePtr, type, callback, user_data, begin, end));
}
/**
- * Hook registration for UC_HOOK_MEM_XXX_UNMAPPED and UC_HOOK_MEM_XXX_PROT
- * hooks.
- * The registered callback function will be invoked whenever a read or write is
- * attempted from an invalid or protected memory address.
+ * Register a UC_HOOK_MEM_XXX_UNMAPPED and/or UC_HOOK_MEM_XXX_PROT hook.
+ * The registered callback function will be invoked whenever a memory
+ * operation is attempted from an invalid or protected memory address.
+ * The registered callback function will be invoked whenever a
+ * corresponding memory operation is performed within the address range
+ * begin <= addr <= end. For the special case in which begin > end, the
+ * callback will be invoked for ALL memory operations.
*
* @param callback Implementation of a EventMemHook interface
* @param type Type of memory event being hooked such as
* UC_HOOK_MEM_READ_UNMAPPED or UC_HOOK_MEM_WRITE_PROT
+ * @param begin Start address of memory range
+ * @param end End address of memory range
* @param user_data User data to be passed to the callback function each time
* the event is triggered
*/
- public void hook_add(EventMemHook callback, int type, Object user_data)
+ public long hook_add(EventMemHook callback, int type, long begin, long end,
+ Object user_data)
throws UnicornException {
- // test all of the EventMem related bits in type
- for (Integer htype : eventMemMap.keySet()) {
- if ((type & htype) != 0) { // the 'htype' bit is set in type
- Long handle = eventMemHandles.get(htype);
- if (handle == null) {
- eventMemHandles.put(htype, registerHook(eng, htype));
- }
- int cbType = eventMemMap.get(htype);
- ArrayList flist = eventMemLists.get(cbType);
- if (flist == null) {
- flist = new ArrayList();
- allLists.add(flist);
- eventMemLists.put(cbType, flist);
- }
- flist.add(new Tuple(callback, user_data));
- }
+ return registerHook(
+ _hook_add(nativePtr, type, callback, user_data, begin, end));
+ }
+
+ public long hook_add(InvalidInstructionHook callback,
+ Object user_data) {
+ return registerHook(_hook_add(nativePtr, UC_HOOK_INSN_INVALID, callback,
+ user_data, 1, 0));
+ }
+
+ public long hook_add(EdgeGeneratedHook callback, long begin, long end,
+ Object user_data)
+ throws UnicornException {
+ return registerHook(_hook_add(nativePtr, UC_HOOK_EDGE_GENERATED,
+ callback, user_data, begin, end));
+ }
+
+ public long hook_add(TcgOpcodeHook callback, long begin, long end,
+ int opcode, int flags,
+ Object user_data)
+ throws UnicornException {
+ return registerHook(_hook_add(nativePtr, UC_HOOK_TCG_OPCODE, callback,
+ user_data, begin, end, opcode, flags));
+ }
+
+ public long hook_add(TlbFillHook callback, long begin, long end,
+ Object user_data) throws UnicornException {
+ return registerHook(_hook_add(nativePtr, UC_HOOK_TLB_FILL, callback,
+ user_data, begin, end));
+ }
+
+ /** Remove a hook that was previously registered.
+ *
+ * @param hook The return value from any hook_add function.
+ */
+ public void hook_del(long hook) throws UnicornException {
+ if (hooks.contains(hook)) {
+ hooks.remove(hooks, hook);
+ _hook_del(nativePtr, hook);
+ } else {
+ throw new UnicornException("Hook is not registered!");
}
}
/**
- * Hook registration for UC_HOOK_INSN hooks (x86 IN instruction only). The
- * registered callback
- * function will be invoked whenever an x86 IN instruction is executed.
- *
- * @param callback Implementation of a InHook interface
- * @param user_data User data to be passed to the callback function each time
- * the event is triggered
- */
- public void hook_add(InHook callback, Object user_data)
+ * Create a memory-mapped I/O range.
+ */
+ public void mmio_map(long address, long size, MmioReadHandler read_cb,
+ Object user_data_read, MmioWriteHandler write_cb,
+ Object user_data_write)
throws UnicornException {
- if (inHandle == 0) {
- inHandle = registerHook(eng, UC_HOOK_INSN, Unicorn.UC_X86_INS_IN);
- }
- inList.add(new Tuple(callback, user_data));
- }
-
- /**
- * Hook registration for UC_HOOK_INSN hooks (x86 OUT instruction only). The
- * registered callback
- * function will be invoked whenever an x86 OUT instruction is executed.
- *
- * @param callback Implementation of a OutHook interface
- * @param user_data User data to be passed to the callback function each time
- * the event is triggered
- */
- public void hook_add(OutHook callback, Object user_data)
- throws UnicornException {
- if (outHandle == 0) {
- outHandle = registerHook(eng, UC_HOOK_INSN, Unicorn.UC_X86_INS_OUT);
- }
- outList.add(new Tuple(callback, user_data));
- }
-
- /**
- * Hook registration for UC_HOOK_INSN hooks (x86 SYSCALL/SYSENTER instruction
- * only). The registered callback
- * function will be invoked whenever an x86 SYSCALL or SYSENTER instruction is
- * executed.
- *
- * @param callback Implementation of a SyscallHook interface
- * @param user_data User data to be passed to the callback function each time
- * the event is triggered
- */
- public void hook_add(SyscallHook callback, Object user_data)
- throws UnicornException {
- if (syscallHandle == 0) {
- syscallHandle =
- registerHook(eng, UC_HOOK_INSN, Unicorn.UC_X86_INS_SYSCALL);
- }
- syscallList.add(new Tuple(callback, user_data));
- }
-
- public void hook_del(Hook hook) throws UnicornException {
- for (ArrayList l : allLists) {
- for (Tuple t : l) {
- if (t.function.equals(hook)) {
- allLists.remove(t);
- return;
- }
- }
+ /* TODO: Watch mem_unmap to know when it's safe to release the hook. */
+ long[] hooks = _mmio_map(nativePtr, address, size, read_cb,
+ user_data_read, write_cb, user_data_write);
+ for (long hook : hooks) {
+ registerHook(hook);
}
}
@@ -834,27 +764,27 @@ public class Unicorn
* @param perms Permissions on the memory block. A combination of
* UC_PROT_READ, UC_PROT_WRITE, UC_PROT_EXEC
*/
- public native void mem_map(long address, long size, int perms)
- throws UnicornException;
+ public void mem_map(long address, long size, int perms)
+ throws UnicornException {
+ _mem_map(nativePtr, address, size, perms);
+ }
/**
* 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 buf Direct-mapped Buffer referencing the memory to
+ * map into the emulator. IMPORTANT: You are responsible
+ * for ensuring that this Buffer remains alive as long
+ * as the emulator is running!
* @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;
+ public void mem_map_ptr(long address, Buffer buf, int perms)
+ throws UnicornException {
+ _mem_map_ptr(nativePtr, address, buf, perms);
+ }
/**
* Unmap a range of memory.
@@ -862,8 +792,9 @@ public class Unicorn
* @param address Base address of the memory range
* @param size Size of the memory block.
*/
- public native void mem_unmap(long address, long size)
- throws UnicornException;
+ public void mem_unmap(long address, long size) throws UnicornException {
+ _mem_unmap(nativePtr, address, size);
+ }
/**
* Change permissions on a range of memory.
@@ -873,8 +804,10 @@ public class Unicorn
* @param perms New permissions on the memory block. A combination of
* UC_PROT_READ, UC_PROT_WRITE, UC_PROT_EXEC
*/
- public native void mem_protect(long address, long size, int perms)
- throws UnicornException;
+ public void mem_protect(long address, long size, int perms)
+ throws UnicornException {
+ _mem_protect(nativePtr, address, size, perms);
+ }
/**
* Retrieve all memory regions mapped by mem_map() and mem_map_ptr()
@@ -882,48 +815,196 @@ public class Unicorn
*
* @return list of mapped regions.
*/
- public native MemRegion[] mem_regions() throws UnicornException;
+ public MemRegion[] mem_regions() throws UnicornException {
+ return _mem_regions(nativePtr);
+ }
- /**
- * Allocate a region that can be used with uc_context_{save,restore} to perform
- * quick save/rollback of the CPU context, which includes registers and some
- * internal metadata. Contexts may not be shared across engine instances with
- * differing arches or modes.
- *
- * @return context handle for use with save/restore.
- */
- public native long context_alloc();
+ public Context context_save() throws UnicornException {
+ long ptr = _context_alloc(nativePtr);
+ Context context = new Context();
+ context.nativePtr = ptr;
+ context.arch = arch;
+ context.mode = mode;
+ _context_save(nativePtr, ptr);
+ return context;
+ }
- /**
- * Free a resource allocated within Unicorn. Use for handles
- * allocated by context_alloc.
- *
- * @param Previously allocated Unicorn object handle.
- */
- public native void free(long handle);
+ public void context_update(Context context) throws UnicornException {
+ if (context.arch != arch || context.mode != mode) {
+ throw new UnicornException(
+ "Context is not compatible with this Unicorn");
+ }
+ _context_save(nativePtr, context.nativePtr);
+ }
- /**
- * Save a copy of the internal CPU context.
- * This API should be used to efficiently make or update a saved copy of the
- * internal CPU state.
- *
- * @param context handle previously returned by context_alloc.
- */
- public native void context_save(long context);
+ public void context_restore(Context context) throws UnicornException {
+ if (context.arch != arch || context.mode != mode) {
+ throw new UnicornException(
+ "Context is not compatible with this Unicorn");
+ }
+ _context_restore(nativePtr, context.nativePtr);
+ }
- /**
- * Restore the current CPU context from a saved copy.
- * This API should be used to roll the CPU context back to a previous
- * state saved by uc_context_save().
- *
- * @param context handle previously returned by context_alloc.
- */
- public native void context_restore(long context);
+ /* Native implementation */
+ private static native long _open(int arch, int mode)
+ throws UnicornException;
+
+ private static native void _close(long uc) throws UnicornException;
+
+ private static native void _emu_start(long uc, long begin, long until,
+ long timeout,
+ long count) throws UnicornException;
+
+ private static native void _emu_stop(long uc) throws UnicornException;
+
+ private static native long _reg_read_long(long ptr, int isContext,
+ int regid) throws UnicornException;
+
+ private static native void _reg_read_bytes(long ptr, int isContext,
+ int regid, byte[] data) throws UnicornException;
+
+ private static native void _reg_write_long(long ptr, int isContext,
+ int regid, long val)
+ throws UnicornException;
+
+ private static native void _reg_write_bytes(long ptr, int isContext,
+ int regid, byte[] data) throws UnicornException;
+
+ private static native X86_MMR _reg_read_x86_mmr(long ptr, int isContext,
+ int regid) throws UnicornException;
+
+ private static native void _reg_write_x86_mmr(long ptr, int isContext,
+ int regid, short selector, long base, int limit, int flags)
+ throws UnicornException;
+
+ private static native long _reg_read_x86_msr(long ptr, int isContext,
+ int rid) throws UnicornException;
+
+ private static native void _reg_write_x86_msr(long ptr, int isContext,
+ int rid, long value) throws UnicornException;
+
+ private static native long _reg_read_arm_cp(long ptr, int isContext, int cp,
+ int is64, int sec, int crn, int crm, int opc1, int opc2)
+ throws UnicornException;
+
+ private static native void _reg_write_arm_cp(long ptr, int isContext,
+ int cp, int is64, int sec, int crn, int crm, int opc1, int opc2,
+ long value) throws UnicornException;
+
+ private static native long _reg_read_arm64_cp(long ptr, int isContext,
+ int crn, int crm, int op0, int op1, int op2)
+ throws UnicornException;
+
+ private static native void _reg_write_arm64_cp(long ptr, int isContext,
+ int crn, int crm, int op0, int op1, int op2, long value)
+ throws UnicornException;
+
+ private static native void _mem_read(long uc, long address,
+ byte[] dest) throws UnicornException;
+
+ private static native void _mem_write(long uc, long address,
+ byte[] src) throws UnicornException;
+
+ private static native int _version();
+
+ private static native boolean _arch_supported(int arch);
+
+ private static native long _query(long uc, int type)
+ throws UnicornException;
+
+ private static native int _errno(long uc);
+
+ private static native String _strerror(int code);
+
+ private native long _hook_add(long uc, int type, Hook callback,
+ Object user_data, long begin, long end) throws UnicornException;
+
+ private native long _hook_add(long uc, int type, Hook callback,
+ Object user_data, long begin, long end, int arg)
+ throws UnicornException;
+
+ private native long _hook_add(long uc, int type, Hook callback,
+ Object user_data, long begin, long end, int arg1, int arg2)
+ throws UnicornException;
+
+ private static native void _hook_del(long uc, long hh)
+ throws UnicornException;
+
+ private static native void _hookwrapper_free(long hh)
+ throws UnicornException;
+
+ private native long[] _mmio_map(long uc, long address, long size,
+ MmioReadHandler read_cb, Object user_data_read,
+ MmioWriteHandler write_cb, Object user_data_write)
+ throws UnicornException;
+
+ private static native void _mem_map(long uc, long address, long size,
+ int perms) throws UnicornException;
+
+ private static native void _mem_map_ptr(long uc, long address, Buffer buf,
+ int perms) throws UnicornException;
+
+ private static native void _mem_unmap(long uc, long address, long size)
+ throws UnicornException;
+
+ private static native void _mem_protect(long uc, long address, long size,
+ int perms) throws UnicornException;
+
+ private static native MemRegion[] _mem_regions(long uc)
+ throws UnicornException;
+
+ private static native long _context_alloc(long uc) throws UnicornException;
+
+ private static native void _context_free(long ctx) throws UnicornException;
+
+ private static native void _context_save(long uc, long ctx)
+ throws UnicornException;
+
+ private static native void _context_restore(long uc, long ctx)
+ throws UnicornException;
+
+ private static native int _ctl_get_mode(long uc) throws UnicornException;
+
+ private static native int _ctl_get_arch(long uc) throws UnicornException;
+
+ private static native long _ctl_get_timeout(long uc)
+ throws UnicornException;
+
+ private static native int _ctl_get_page_size(long uc)
+ throws UnicornException;
+
+ private static native void _ctl_set_page_size(long uc, int page_size)
+ throws UnicornException;
+
+ private static native void _ctl_set_use_exits(long uc, boolean value)
+ throws UnicornException;
+
+ private static native long _ctl_get_exits_cnt(long uc)
+ throws UnicornException;
+
+ private static native long[] _ctl_get_exits(long uc)
+ throws UnicornException;
+
+ private static native void _ctl_set_exits(long uc, long[] exits)
+ throws UnicornException;
+
+ private static native int _ctl_get_cpu_model(long uc)
+ throws UnicornException;
+
+ private static native void _ctl_set_cpu_model(long uc, int cpu_model)
+ throws UnicornException;
+
+ private static native TranslationBlock _ctl_request_cache(long uc,
+ long address) throws UnicornException;
+
+ private static native void _ctl_remove_cache(long uc, long address,
+ long end) throws UnicornException;
+
+ private static native void _ctl_flush_tb(long uc) throws UnicornException;
+
+ private static native void _ctl_flush_tlb(long uc) throws UnicornException;
+
+ private static native void _ctl_tlb_mode(long uc, int mode)
+ throws UnicornException;
- /**
- * Set the emulated cpu model.
- *
- * @param cpu_model CPU model type (see UC_CPU_*).
- */
- public native void ctl_set_cpu_model(int cpu_model);
}
diff --git a/bindings/java/unicorn/X86_Float80.java b/bindings/java/unicorn/X86_Float80.java
new file mode 100644
index 00000000..df76dc7b
--- /dev/null
+++ b/bindings/java/unicorn/X86_Float80.java
@@ -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 +
+ "]";
+ }
+}
diff --git a/bindings/java/unicorn/X86_MMR.java b/bindings/java/unicorn/X86_MMR.java
index 17f3fe33..5f8e8b63 100644
--- a/bindings/java/unicorn/X86_MMR.java
+++ b/bindings/java/unicorn/X86_MMR.java
@@ -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 + "]";
+ }
}
diff --git a/bindings/java/unicorn/X86_MSR.java b/bindings/java/unicorn/X86_MSR.java
new file mode 100644
index 00000000..1bf9138c
--- /dev/null
+++ b/bindings/java/unicorn/X86_MSR.java
@@ -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 + "]";
+ }
+}
diff --git a/bindings/java/unicorn_Unicorn.c b/bindings/java/unicorn_Unicorn.c
index de45f8ff..58ce3825 100644
--- a/bindings/java/unicorn_Unicorn.c
+++ b/bindings/java/unicorn_Unicorn.c
@@ -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
@@ -28,18 +28,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include
#include "unicorn_Unicorn.h"
-// cache jmethodID values as we look them up
-static jmethodID invokeBlockCallbacks = 0;
-static jmethodID invokeInterruptCallbacks = 0;
-static jmethodID invokeCodeCallbacks = 0;
-
-static jmethodID invokeEventMemCallbacks = 0;
-static jmethodID invokeReadCallbacks = 0;
-static jmethodID invokeWriteCallbacks = 0;
-static jmethodID invokeInCallbacks = 0;
-static jmethodID invokeOutCallbacks = 0;
-static jmethodID invokeSyscallCallbacks = 0;
-
static JavaVM *cachedJVM;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved)
@@ -48,772 +36,1223 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved)
return JNI_VERSION_1_6;
}
-// Callback function for tracing code (UC_HOOK_CODE & UC_HOOK_BLOCK)
-// @address: address where the code is being executed
-// @size: size of machine instruction being executed
-// @user_data: user data passed to tracing APIs.
-static void cb_hookcode(uc_engine *eng, uint64_t address, uint32_t size,
- void *user_data)
+static void throwUnicornException(JNIEnv *env, uc_err err)
+{
+ jclass clazz = (*env)->FindClass(env, "unicorn/UnicornException");
+ const char *msg = uc_strerror(err);
+ (*env)->ThrowNew(env, clazz, msg);
+}
+
+static void throwCustomUnicornException(JNIEnv *env, const char *msg)
+{
+ jclass clazz = (*env)->FindClass(env, "unicorn/UnicornException");
+ (*env)->ThrowNew(env, clazz, msg);
+}
+
+static void throwOutOfMemoryError(JNIEnv *env, char *message)
+{
+ jclass clazz = (*env)->FindClass(env, "java/lang/OutOfMemoryError");
+ (*env)->ThrowNew(env, clazz, message);
+}
+
+static jobject makeX86_MMR(JNIEnv *env, const uc_x86_mmr *mmr)
+{
+ if (mmr == NULL) {
+ return NULL;
+ }
+
+ static jclass clazz;
+ if (!clazz) {
+ clazz = (*env)->FindClass(env, "unicorn/X86_MMR");
+ if (!clazz)
+ return NULL;
+ clazz = (*env)->NewGlobalRef(env, clazz);
+ if (!clazz)
+ return NULL;
+ }
+
+ static jmethodID clazzInit;
+ if (!clazzInit) {
+ clazzInit = (*env)->GetMethodID(env, clazz, "", "(JIIS)V");
+ if (!clazzInit)
+ return NULL;
+ }
+
+ return (*env)->NewObject(env, clazz, clazzInit, (jlong)mmr->base,
+ (jint)mmr->limit, (jint)mmr->flags,
+ (jshort)mmr->selector);
+}
+
+static jobject makeArm64_CP(JNIEnv *env, const uc_arm64_cp_reg *cp_reg)
+{
+ if (cp_reg == NULL) {
+ return NULL;
+ }
+
+ static jclass clazz;
+ if (!clazz) {
+ clazz = (*env)->FindClass(env, "unicorn/Arm64_CP");
+ if (!clazz)
+ return NULL;
+ clazz = (*env)->NewGlobalRef(env, clazz);
+ if (!clazz)
+ return NULL;
+ }
+
+ static jmethodID clazzInit;
+ if (!clazzInit) {
+ clazzInit = (*env)->GetMethodID(env, clazz, "", "(IIIIIJ)V");
+ if (!clazzInit)
+ return NULL;
+ }
+
+ return (*env)->NewObject(env, clazz, clazzInit, (jint)cp_reg->crn,
+ (jint)cp_reg->crm, (jint)cp_reg->op0,
+ (jint)cp_reg->op1, (jint)cp_reg->op2,
+ (jlong)cp_reg->val);
+}
+
+static jobject makeTranslationBlock(JNIEnv *env, const uc_tb *tb)
+{
+ if (tb == NULL) {
+ return NULL;
+ }
+
+ static jclass clazz;
+ if (!clazz) {
+ clazz = (*env)->FindClass(env, "unicorn/TranslationBlock");
+ if (!clazz)
+ return NULL;
+ clazz = (*env)->NewGlobalRef(env, clazz);
+ if (!clazz)
+ return NULL;
+ }
+
+ static jmethodID clazzInit;
+ if (!clazzInit) {
+ clazzInit = (*env)->GetMethodID(env, clazz, "", "(JII)V");
+ if (!clazzInit)
+ return NULL;
+ }
+
+ return (*env)->NewObject(env, clazz, clazzInit, (jlong)tb->pc,
+ (jint)tb->icount, (jint)tb->size);
+}
+
+struct hook_wrapper {
+ uc_hook uc_hh;
+ jobject unicorn;
+ jobject hook_obj;
+ jmethodID hook_meth;
+ jobject user_data;
+};
+
+static bool hookErrorCheck(uc_engine *uc, JNIEnv *env)
+{
+ /* If a hook throws an exception, we want to report it as soon as possible.
+ Additionally, once an exception is set, calling further hooks is
+ inadvisable. Therefore, try and stop the emulator as soon as an exception
+ is detected.
+ */
+ if ((*env)->ExceptionCheck(env)) {
+ uc_emu_stop(uc);
+ return true;
+ }
+ return false;
+}
+
+static const char *const sig_InterruptHook =
+ "(Lunicorn/Unicorn;ILjava/lang/Object;)V";
+static void cb_hookintr(uc_engine *uc, uint32_t intno, void *user_data)
{
JNIEnv *env;
(*cachedJVM)->AttachCurrentThread(cachedJVM, (void **)&env, NULL);
- jclass clz = (*env)->FindClass(env, "unicorn/Unicorn");
- if ((*env)->ExceptionCheck(env)) {
- return;
- }
- (*env)->CallStaticVoidMethod(env, clz, invokeCodeCallbacks, (jlong)eng,
- (jlong)address, (int)size);
- (*cachedJVM)->DetachCurrentThread(cachedJVM);
+ struct hook_wrapper *hh = user_data;
+ (*env)->CallVoidMethod(env, hh->hook_obj, hh->hook_meth, hh->unicorn,
+ (jint)intno, hh->user_data);
+ hookErrorCheck(uc, env);
}
-// Callback function for tracing code (UC_HOOK_CODE & UC_HOOK_BLOCK)
-// @address: address where the code is being executed
-// @size: size of machine instruction being executed
-// @user_data: user data passed to tracing APIs.
-static void cb_hookblock(uc_engine *eng, uint64_t address, uint32_t size,
- void *user_data)
-{
- JNIEnv *env;
- (*cachedJVM)->AttachCurrentThread(cachedJVM, (void **)&env, NULL);
- jclass clz = (*env)->FindClass(env, "unicorn/Unicorn");
- if ((*env)->ExceptionCheck(env)) {
- return;
- }
- (*env)->CallStaticVoidMethod(env, clz, invokeBlockCallbacks, (jlong)eng,
- (jlong)address, (int)size);
- (*cachedJVM)->DetachCurrentThread(cachedJVM);
-}
-
-// Callback function for tracing interrupts (for uc_hook_intr())
-// @intno: interrupt number
-// @user_data: user data passed to tracing APIs.
-static void cb_hookintr(uc_engine *eng, uint32_t intno, void *user_data)
-{
- JNIEnv *env;
- (*cachedJVM)->AttachCurrentThread(cachedJVM, (void **)&env, NULL);
- jclass clz = (*env)->FindClass(env, "unicorn/Unicorn");
- if ((*env)->ExceptionCheck(env)) {
- return;
- }
- (*env)->CallStaticVoidMethod(env, clz, invokeInterruptCallbacks, (jlong)eng,
- (int)intno);
- (*cachedJVM)->DetachCurrentThread(cachedJVM);
-}
-
-// Callback function for tracing IN instruction of X86
-// @port: port number
-// @size: data size (1/2/4) to be read from this port
-// @user_data: user data passed to tracing APIs.
-static uint32_t cb_insn_in(uc_engine *eng, uint32_t port, int size,
+static const char *const sig_InHook =
+ "(Lunicorn/Unicorn;IILjava/lang/Object;)I";
+static uint32_t cb_insn_in(uc_engine *uc, uint32_t port, int size,
void *user_data)
{
JNIEnv *env;
- uint32_t res = 0;
(*cachedJVM)->AttachCurrentThread(cachedJVM, (void **)&env, NULL);
- jclass clz = (*env)->FindClass(env, "unicorn/Unicorn");
- if ((*env)->ExceptionCheck(env)) {
+ struct hook_wrapper *hh = user_data;
+ jint result =
+ (*env)->CallIntMethod(env, hh->hook_obj, hh->hook_meth, hh->unicorn,
+ (jint)port, (jint)size, hh->user_data);
+ if (hookErrorCheck(uc, env)) {
return 0;
}
- res = (uint32_t)(*env)->CallStaticIntMethod(
- env, clz, invokeInCallbacks, (jlong)eng, (jint)port, (jint)size);
- (*cachedJVM)->DetachCurrentThread(cachedJVM);
- return res;
+ return (uint32_t)result;
}
-// x86's handler for OUT
-// @port: port number
-// @size: data size (1/2/4) to be written to this port
-// @value: data value to be written to this port
-static void cb_insn_out(uc_engine *eng, uint32_t port, int size, uint32_t value,
+static const char *const sig_OutHook =
+ "(Lunicorn/Unicorn;IIILjava/lang/Object;)V";
+static void cb_insn_out(uc_engine *uc, uint32_t port, int size, uint32_t value,
void *user_data)
{
JNIEnv *env;
(*cachedJVM)->AttachCurrentThread(cachedJVM, (void **)&env, NULL);
- jclass clz = (*env)->FindClass(env, "unicorn/Unicorn");
- if ((*env)->ExceptionCheck(env)) {
- return;
- }
- (*env)->CallStaticVoidMethod(env, clz, invokeOutCallbacks, (jlong)eng,
- (jint)port, (jint)size, (jint)value);
- (*cachedJVM)->DetachCurrentThread(cachedJVM);
+ struct hook_wrapper *hh = user_data;
+ (*env)->CallVoidMethod(env, hh->hook_obj, hh->hook_meth, hh->unicorn,
+ (jint)port, (jint)size, (jint)value, hh->user_data);
+ hookErrorCheck(uc, env);
}
-// x86's handler for SYSCALL/SYSENTER
-static void cb_insn_syscall(uc_engine *eng, void *user_data)
+static const char *const sig_SyscallHook =
+ "(Lunicorn/Unicorn;Ljava/lang/Object;)V";
+static void cb_insn_syscall(struct uc_struct *uc, void *user_data)
{
JNIEnv *env;
(*cachedJVM)->AttachCurrentThread(cachedJVM, (void **)&env, NULL);
- jclass clz = (*env)->FindClass(env, "unicorn/Unicorn");
- if ((*env)->ExceptionCheck(env)) {
- return;
- }
- (*env)->CallStaticVoidMethod(env, clz, invokeSyscallCallbacks, (jlong)eng);
- (*cachedJVM)->DetachCurrentThread(cachedJVM);
+ struct hook_wrapper *hh = user_data;
+ (*env)->CallVoidMethod(env, hh->hook_obj, hh->hook_meth, hh->unicorn,
+ hh->user_data);
+ hookErrorCheck(uc, env);
}
-// Callback function for hooking memory (UC_HOOK_MEM_*)
-// @type: this memory is being READ, or WRITE
-// @address: address where the code is being executed
-// @size: size of data being read or written
-// @value: value of data being written to memory, or irrelevant if type = READ.
-// @user_data: user data passed to tracing APIs
-static void cb_hookmem(uc_engine *eng, uc_mem_type type, uint64_t address,
- int size, int64_t value, void *user_data)
+static const char *const sig_CpuidHook =
+ "(Lunicorn/Unicorn;Ljava/lang/Object;)I";
+static int cb_insn_cpuid(struct uc_struct *uc, void *user_data)
{
JNIEnv *env;
(*cachedJVM)->AttachCurrentThread(cachedJVM, (void **)&env, NULL);
- jclass clz = (*env)->FindClass(env, "unicorn/Unicorn");
- if ((*env)->ExceptionCheck(env)) {
- return;
+ struct hook_wrapper *hh = user_data;
+ jint result = (*env)->CallIntMethod(env, hh->hook_obj, hh->hook_meth,
+ hh->unicorn, hh->user_data);
+ if (hookErrorCheck(uc, env)) {
+ return 0;
}
- switch (type) {
- case UC_MEM_READ:
- (*env)->CallStaticVoidMethod(env, clz, invokeReadCallbacks, (jlong)eng,
- (jlong)address, (int)size);
- break;
- case UC_MEM_WRITE:
- (*env)->CallStaticVoidMethod(env, clz, invokeWriteCallbacks, (jlong)eng,
- (jlong)address, (int)size, (jlong)value);
- break;
- }
- (*cachedJVM)->DetachCurrentThread(cachedJVM);
+ return (int)result;
}
-// Callback function for handling memory events (for UC_HOOK_MEM_UNMAPPED)
-// @type: this memory is being READ, or WRITE
-// @address: address where the code is being executed
-// @size: size of data being read or written
-// @value: value of data being written to memory, or irrelevant if type = READ.
-// @user_data: user data passed to tracing APIs
-// @return: return true to continue, or false to stop program (due to invalid
-// memory).
-static bool cb_eventmem(uc_engine *eng, uc_mem_type type, uint64_t address,
+static const char *const sig_Arm64SysHook =
+ "(Lunicorn/Unicorn;ILunicorn/Arm64_CP;Ljava/lang/Object;)I";
+static uint32_t cb_insn_sys(uc_engine *uc, uc_arm64_reg reg,
+ const uc_arm64_cp_reg *cp_reg, void *user_data)
+{
+ JNIEnv *env;
+ (*cachedJVM)->AttachCurrentThread(cachedJVM, (void **)&env, NULL);
+ struct hook_wrapper *hh = user_data;
+ jobject jcp_reg = makeArm64_CP(env, cp_reg);
+ if (!jcp_reg) {
+ hookErrorCheck(uc, env);
+ return 0;
+ }
+ jint result =
+ (*env)->CallIntMethod(env, hh->hook_obj, hh->hook_meth, hh->unicorn,
+ (jint)reg, jcp_reg, hh->user_data);
+ if (hookErrorCheck(uc, env)) {
+ return 0;
+ }
+ return (uint32_t)result;
+}
+
+static const char *const sig_CodeHook =
+ "(Lunicorn/Unicorn;JILjava/lang/Object;)V";
+static void cb_hookcode(uc_engine *uc, uint64_t address, uint32_t size,
+ void *user_data)
+{
+ JNIEnv *env;
+ (*cachedJVM)->AttachCurrentThread(cachedJVM, (void **)&env, NULL);
+ struct hook_wrapper *hh = user_data;
+ (*env)->CallVoidMethod(env, hh->hook_obj, hh->hook_meth, hh->unicorn,
+ (jlong)address, (jint)size, hh->user_data);
+ hookErrorCheck(uc, env);
+}
+
+static const char *const sig_EventMemHook =
+ "(Lunicorn/Unicorn;IJIJLjava/lang/Object;)Z";
+static bool cb_eventmem(uc_engine *uc, uc_mem_type type, uint64_t address,
int size, int64_t value, void *user_data)
{
JNIEnv *env;
(*cachedJVM)->AttachCurrentThread(cachedJVM, (void **)&env, NULL);
- jclass clz = (*env)->FindClass(env, "unicorn/Unicorn");
- if ((*env)->ExceptionCheck(env)) {
+ struct hook_wrapper *hh = user_data;
+ jboolean result = (*env)->CallBooleanMethod(
+ env, hh->hook_obj, hh->hook_meth, hh->unicorn, (jint)type,
+ (jlong)address, (jint)size, (jlong)value, hh->user_data);
+ if (hookErrorCheck(uc, env)) {
return false;
}
- jboolean res = (*env)->CallStaticBooleanMethod(
- env, clz, invokeEventMemCallbacks, (jlong)eng, (int)type,
- (jlong)address, (int)size, (jlong)value);
- (*cachedJVM)->DetachCurrentThread(cachedJVM);
- return res;
+ return result != JNI_FALSE;
}
-static void throwException(JNIEnv *env, uc_err err)
+static const char *const sig_MemHook =
+ "(Lunicorn/Unicorn;IJIJLjava/lang/Object;)V";
+static void cb_hookmem(uc_engine *uc, uc_mem_type type, uint64_t address,
+ int size, int64_t value, void *user_data)
{
- // throw exception
- jclass clazz = (*env)->FindClass(env, "unicorn/UnicornException");
- if (err != UC_ERR_OK) {
- const char *msg = uc_strerror(err);
- (*env)->ThrowNew(env, clazz, msg);
+ JNIEnv *env;
+ (*cachedJVM)->AttachCurrentThread(cachedJVM, (void **)&env, NULL);
+ struct hook_wrapper *hh = user_data;
+ (*env)->CallVoidMethod(env, hh->hook_obj, hh->hook_meth, hh->unicorn,
+ (jint)type, (jlong)address, (jint)size, (jlong)value,
+ hh->user_data);
+ hookErrorCheck(uc, env);
+}
+
+static const char *const sig_InvalidInstructionHook =
+ "(Lunicorn/Unicorn;Ljava/lang/Object;)Z";
+static bool cb_hookinsn_invalid(uc_engine *uc, void *user_data)
+{
+ JNIEnv *env;
+ (*cachedJVM)->AttachCurrentThread(cachedJVM, (void **)&env, NULL);
+ struct hook_wrapper *hh = user_data;
+ jboolean result = (*env)->CallBooleanMethod(
+ env, hh->hook_obj, hh->hook_meth, hh->unicorn, hh->user_data);
+ if (hookErrorCheck(uc, env)) {
+ return false;
}
+ return result != JNI_FALSE;
}
-static uc_engine *getEngine(JNIEnv *env, jobject self)
+static const char *const sig_EdgeGeneratedHook =
+ "(Lunicorn/Unicorn;Lunicorn/TranslationBlock;"
+ "Lunicorn/TranslationBlock;Ljava/lang/Object;)V";
+static void cb_edge_gen(uc_engine *uc, uc_tb *cur_tb, uc_tb *prev_tb,
+ void *user_data)
{
- static int haveFid = 0;
- static jfieldID fid;
- if (haveFid == 0) {
- // cache the field id
- jclass clazz = (*env)->GetObjectClass(env, self);
- fid = (*env)->GetFieldID(env, clazz, "eng", "J");
- haveFid = 1;
- }
- 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)) {
+ JNIEnv *env;
+ (*cachedJVM)->AttachCurrentThread(cachedJVM, (void **)&env, NULL);
+ struct hook_wrapper *hh = user_data;
+ jobject jcur_tb = makeTranslationBlock(env, cur_tb);
+ if (!jcur_tb) {
+ hookErrorCheck(uc, 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)) {
+ jobject jprev_tb = makeTranslationBlock(env, prev_tb);
+ if (!jprev_tb) {
+ hookErrorCheck(uc, 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);
- }
+ (*env)->CallVoidMethod(env, hh->hook_obj, hh->hook_meth, hh->unicorn,
+ jcur_tb, jprev_tb, hh->user_data);
+ hookErrorCheck(uc, env);
}
-/*
- * 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)
+static const char *const sig_TcgOpcodeHook =
+ "(Lunicorn/Unicorn;JJJILjava/lang/Object;)V";
+static void cb_tcg_op_2(uc_engine *uc, uint64_t address, uint64_t arg1,
+ uint64_t arg2, uint32_t size, void *user_data)
{
- 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, "", "(J)V");
- jobject result = (*env)->NewObject(env, clz, cons, longVal);
- if ((*env)->ExceptionCheck(env)) {
- return NULL;
- }
- return result;
+ JNIEnv *env;
+ (*cachedJVM)->AttachCurrentThread(cachedJVM, (void **)&env, NULL);
+ struct hook_wrapper *hh = user_data;
+ (*env)->CallVoidMethod(env, hh->hook_obj, hh->hook_meth, hh->unicorn,
+ (jlong)address, (jlong)arg1, (jlong)arg2, (jint)size,
+ hh->user_data);
+ hookErrorCheck(uc, env);
}
-/*
- * 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)
+static const char *const sig_TlbFillHook =
+ "(Lunicorn/Unicorn;JILjava/lang/Object;)J";
+static bool cb_tlbevent(uc_engine *uc, uint64_t vaddr, uc_mem_type type,
+ uc_tlb_entry *entry, void *user_data)
{
- uc_engine *eng = getEngine(env, self);
-
- jclass clz = (*env)->FindClass(env, "unicorn/X86_MMR");
- if ((*env)->ExceptionCheck(env)) {
- return NULL;
+ JNIEnv *env;
+ (*cachedJVM)->AttachCurrentThread(cachedJVM, (void **)&env, NULL);
+ struct hook_wrapper *hh = user_data;
+ jlong result =
+ (*env)->CallLongMethod(env, hh->hook_obj, hh->hook_meth, hh->unicorn,
+ (jlong)vaddr, (jint)type, hh->user_data);
+ if (hookErrorCheck(uc, env)) {
+ return false;
}
-
- uc_x86_mmr mmr;
- uc_err err = uc_reg_read(eng, regid, &mmr);
- if (err != UC_ERR_OK) {
- throwException(env, err);
+ if (result == -1L) {
+ return false;
+ } else {
+ entry->paddr = result & ~UC_PROT_ALL;
+ entry->perms = result & UC_PROT_ALL;
+ return true;
}
+}
- jmethodID cons = (*env)->GetMethodID(env, clz, "", "(JIIS)V");
- jobject result = (*env)->NewObject(env, clz, cons, mmr.base, mmr.limit,
- mmr.flags, mmr.selector);
- if ((*env)->ExceptionCheck(env)) {
- return NULL;
+static const char *const sig_MmioReadHandler =
+ "(Lunicorn/Unicorn;JILjava/lang/Object;)J";
+static uint64_t cb_mmio_read(uc_engine *uc, uint64_t offset, unsigned size,
+ void *user_data)
+{
+ JNIEnv *env;
+ (*cachedJVM)->AttachCurrentThread(cachedJVM, (void **)&env, NULL);
+ struct hook_wrapper *hh = user_data;
+ jlong result =
+ (*env)->CallLongMethod(env, hh->hook_obj, hh->hook_meth, hh->unicorn,
+ (jlong)offset, (jint)size, hh->user_data);
+ if (hookErrorCheck(uc, env)) {
+ return 0;
}
- return result;
+ return (uint64_t)result;
+}
+
+static const char *const sig_MmioWriteHandler =
+ "(Lunicorn/Unicorn;JIJLjava/lang/Object;)V";
+static void cb_mmio_write(uc_engine *uc, uint64_t offset, unsigned size,
+ uint64_t value, void *user_data)
+{
+ JNIEnv *env;
+ (*cachedJVM)->AttachCurrentThread(cachedJVM, (void **)&env, NULL);
+ struct hook_wrapper *hh = user_data;
+ (*env)->CallVoidMethod(env, hh->hook_obj, hh->hook_meth, hh->unicorn,
+ (jlong)offset, (jint)size, (jlong)value,
+ hh->user_data);
+ hookErrorCheck(uc, env);
}
/*
* Class: unicorn_Unicorn
- * Method: open
+ * Method: _open
* Signature: (II)J
*/
-JNIEXPORT jlong JNICALL Java_unicorn_Unicorn_open(JNIEnv *env, jobject self,
- jint arch, jint mode)
+JNIEXPORT jlong JNICALL Java_unicorn_Unicorn__1open(JNIEnv *env, jclass clazz,
+ jint arch, jint mode)
{
uc_engine *eng = NULL;
- uc_err err = uc_open((uc_arch)arch, (uc_mode)mode, &eng);
+ uc_err err = uc_open(arch, mode, &eng);
if (err != UC_ERR_OK) {
- throwException(env, err);
+ throwUnicornException(env, err);
+ return 0;
}
return (jlong)eng;
}
/*
* Class: unicorn_Unicorn
- * Method: version
+ * Method: _close
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1close(JNIEnv *env, jclass clazz,
+ jlong uc)
+{
+ uc_err err = uc_close((uc_engine *)uc);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _emu_start
+ * Signature: (JJJJJ)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1emu_1start(
+ JNIEnv *env, jclass clazz, jlong uc, jlong begin, jlong until,
+ jlong timeout, jlong count)
+{
+ uc_err err =
+ uc_emu_start((uc_engine *)uc, begin, until, timeout, (size_t)count);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _emu_stop
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1emu_1stop(JNIEnv *env,
+ jclass clazz, jlong uc)
+{
+ uc_err err = uc_emu_stop((uc_engine *)uc);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+static uc_err generic_reg_read(jlong ptr, jint isContext, jint regid,
+ void *result)
+{
+ if (isContext) {
+ return uc_context_reg_read((uc_context *)ptr, regid, result);
+ } else {
+ return uc_reg_read((uc_engine *)ptr, regid, result);
+ }
+}
+
+static uc_err generic_reg_write(jlong ptr, jint isContext, jint regid,
+ const void *value)
+{
+ if (isContext) {
+ return uc_context_reg_write((uc_context *)ptr, regid, value);
+ } else {
+ return uc_reg_write((uc_engine *)ptr, regid, value);
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _reg_read_long
+ * Signature: (JII)J
+ */
+JNIEXPORT jlong JNICALL Java_unicorn_Unicorn__1reg_1read_1long(
+ JNIEnv *env, jclass clazz, jlong ptr, jint isContext, jint regid)
+{
+ /* XXX: This is just *wrong* on big-endian hosts, since a register
+ smaller than 8 bytes will be written into the MSBs. */
+ uint64_t result = 0;
+ uc_err err = generic_reg_read(ptr, isContext, regid, &result);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return 0;
+ }
+ return result;
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _reg_read_bytes
+ * Signature: (JII[B)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1reg_1read_1bytes(
+ JNIEnv *env, jclass clazz, jlong ptr, jint isContext, jint regid,
+ jbyteArray data)
+{
+ jbyte *arr = (*env)->GetByteArrayElements(env, data, NULL);
+ uc_err err = generic_reg_read(ptr, isContext, regid, arr);
+ (*env)->ReleaseByteArrayElements(env, data, arr, 0);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _reg_write_long
+ * Signature: (JIIJ)V
+ */
+JNIEXPORT void JNICALL
+Java_unicorn_Unicorn__1reg_1write_1long(JNIEnv *env, jclass clazz, jlong ptr,
+ jint isContext, jint regid, jlong value)
+{
+ uint64_t cvalue = value;
+ uc_err err = generic_reg_write(ptr, isContext, regid, &cvalue);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _reg_write_bytes
+ * Signature: (JII[B)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1reg_1write_1bytes(
+ JNIEnv *env, jclass clazz, jlong ptr, jint isContext, jint regid,
+ jbyteArray data)
+{
+ jbyte *arr = (*env)->GetByteArrayElements(env, data, NULL);
+ uc_err err = generic_reg_write(ptr, isContext, regid, arr);
+ (*env)->ReleaseByteArrayElements(env, data, arr, JNI_ABORT);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _reg_read_x86_mmr
+ * Signature: (JII)Lunicorn/X86_MMR;
+ */
+JNIEXPORT jobject JNICALL Java_unicorn_Unicorn__1reg_1read_1x86_1mmr(
+ JNIEnv *env, jclass clazz, jlong ptr, jint isContext, jint regid)
+{
+ uc_x86_mmr reg = {0};
+ uc_err err = generic_reg_read(ptr, isContext, regid, ®);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return 0;
+ }
+ return makeX86_MMR(env, ®);
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _reg_write_x86_mmr
+ * Signature: (JIISJII)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1reg_1write_1x86_1mmr(
+ JNIEnv *env, jclass clazz, jlong ptr, jint isContext, jint regid,
+ jshort selector, jlong base, jint limit, jint flags)
+{
+ uc_x86_mmr reg = {0};
+ reg.selector = selector;
+ reg.base = base;
+ reg.limit = limit;
+ reg.flags = flags;
+ uc_err err = generic_reg_write(ptr, isContext, regid, ®);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _reg_read_x86_msr
+ * Signature: (JII)J
+ */
+JNIEXPORT jlong JNICALL Java_unicorn_Unicorn__1reg_1read_1x86_1msr(
+ JNIEnv *env, jclass clazz, jlong ptr, jint isContext, jint rid)
+{
+ uc_x86_msr reg = {0};
+ reg.rid = rid;
+ uc_err err = generic_reg_read(ptr, isContext, UC_X86_REG_MSR, ®);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return 0;
+ }
+ return reg.value;
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _reg_write_x86_msr
+ * Signature: (JIIJ)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1reg_1write_1x86_1msr(
+ JNIEnv *env, jclass clazz, jlong ptr, jint isContext, jint rid, jlong value)
+{
+ uc_x86_msr reg = {0};
+ reg.rid = rid;
+ reg.value = value;
+ uc_err err = generic_reg_write(ptr, isContext, UC_X86_REG_MSR, ®);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _reg_read_arm_cp
+ * Signature: (JIIIIIIII)J
+ */
+JNIEXPORT jlong JNICALL Java_unicorn_Unicorn__1reg_1read_1arm_1cp(
+ JNIEnv *env, jclass clazz, jlong ptr, jint isContext, jint cp, jint is64,
+ jint sec, jint crn, jint crm, jint opc1, jint opc2)
+{
+ uc_arm_cp_reg reg = {0};
+ reg.cp = cp;
+ reg.is64 = is64;
+ reg.sec = sec;
+ reg.crn = crn;
+ reg.crm = crm;
+ reg.opc1 = opc1;
+ reg.opc2 = opc2;
+ uc_err err = generic_reg_read(ptr, isContext, UC_ARM_REG_CP_REG, ®);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return 0;
+ }
+ return reg.val;
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _reg_write_arm_cp
+ * Signature: (JIIIIIIIIJ)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1reg_1write_1arm_1cp(
+ JNIEnv *env, jclass clazz, jlong ptr, jint isContext, jint cp, jint is64,
+ jint sec, jint crn, jint crm, jint opc1, jint opc2, jlong value)
+{
+ uc_arm_cp_reg reg = {0};
+ reg.cp = cp;
+ reg.is64 = is64;
+ reg.sec = sec;
+ reg.crn = crn;
+ reg.crm = crm;
+ reg.opc1 = opc1;
+ reg.opc2 = opc2;
+ reg.val = value;
+ uc_err err = generic_reg_write(ptr, isContext, UC_ARM_REG_CP_REG, ®);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _reg_read_arm64_cp
+ * Signature: (JIIIIII)J
+ */
+JNIEXPORT jlong JNICALL Java_unicorn_Unicorn__1reg_1read_1arm64_1cp(
+ JNIEnv *env, jclass clazz, jlong ptr, jint isContext, jint crn, jint crm,
+ jint op0, jint op1, jint op2)
+{
+ uc_arm64_cp_reg reg = {0};
+ reg.crn = crn;
+ reg.crm = crm;
+ reg.op0 = op0;
+ reg.op1 = op1;
+ reg.op2 = op2;
+ uc_err err = generic_reg_read(ptr, isContext, UC_ARM64_REG_CP_REG, ®);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return 0;
+ }
+ return reg.val;
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _reg_write_arm64_cp
+ * Signature: (JIIIIIIJ)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1reg_1write_1arm64_1cp(
+ JNIEnv *env, jclass clazz, jlong ptr, jint isContext, jint crn, jint crm,
+ jint op0, jint op1, jint op2, jlong value)
+{
+ uc_arm64_cp_reg reg = {0};
+ reg.crn = crn;
+ reg.crm = crm;
+ reg.op0 = op0;
+ reg.op1 = op1;
+ reg.op2 = op2;
+ reg.val = value;
+ uc_err err = generic_reg_write(ptr, isContext, UC_ARM64_REG_CP_REG, ®);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _mem_read
+ * Signature: (JJ[B)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1mem_1read(JNIEnv *env,
+ jclass clazz, jlong uc,
+ jlong address,
+ jbyteArray dest)
+{
+ jsize size = (*env)->GetArrayLength(env, dest);
+ jbyte *arr = (*env)->GetByteArrayElements(env, dest, NULL);
+ uc_err err = uc_mem_read((uc_engine *)uc, address, arr, size);
+ (*env)->ReleaseByteArrayElements(env, dest, arr, 0);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _mem_write
+ * Signature: (JJ[B)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1mem_1write(JNIEnv *env,
+ jclass clazz, jlong uc,
+ jlong address,
+ jbyteArray src)
+{
+ jsize size = (*env)->GetArrayLength(env, src);
+ jbyte *arr = (*env)->GetByteArrayElements(env, src, NULL);
+ uc_err err = uc_mem_write((uc_engine *)uc, address, arr, size);
+ (*env)->ReleaseByteArrayElements(env, src, arr, JNI_ABORT);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _version
* Signature: ()I
*/
-JNIEXPORT jint JNICALL Java_unicorn_Unicorn_version(JNIEnv *env, jclass clz)
+JNIEXPORT jint JNICALL Java_unicorn_Unicorn__1version(JNIEnv *env, jclass clazz)
{
return (jint)uc_version(NULL, NULL);
}
/*
* Class: unicorn_Unicorn
- * Method: arch_supported
+ * Method: _arch_supported
* Signature: (I)Z
*/
-JNIEXPORT jboolean JNICALL Java_unicorn_Unicorn_arch_1supported(JNIEnv *env,
- jclass clz,
- jint arch)
+JNIEXPORT jboolean JNICALL Java_unicorn_Unicorn__1arch_1supported(JNIEnv *env,
+ jclass clazz,
+ jint arch)
{
return (jboolean)(uc_arch_supported((uc_arch)arch) != 0);
}
/*
* Class: unicorn_Unicorn
- * Method: _close
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_unicorn_Unicorn__1close(JNIEnv *env, jobject self)
-{
- uc_engine *eng = getEngine(env, self);
- 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;
-}
-
-/*
- * Class: unicorn_Unicorn
- * Method: errno
- * Signature: ()I
- */
-JNIEXPORT jint JNICALL Java_unicorn_Unicorn_errno(JNIEnv *env, jobject self)
-{
- uc_engine *eng = getEngine(env, self);
- return (jint)uc_errno(eng);
-}
-
-/*
- * Class: unicorn_Unicorn
- * Method: strerror
- * Signature: (I)Ljava/lang/String;
- */
-JNIEXPORT jstring JNICALL Java_unicorn_Unicorn_strerror(JNIEnv *env, jclass clz,
- jint code)
-{
- const char *err = uc_strerror((int)code);
- jstring s = (*env)->NewStringUTF(env, err);
- return s;
-}
-
-/*
- * Class: unicorn_Unicorn
- * Method: reg_write
- * Signature: (I[B)V
- */
-JNIEXPORT void JNICALL Java_unicorn_Unicorn_reg_1write(JNIEnv *env,
- jobject self, jint regid,
- jbyteArray value)
-{
- uc_engine *eng = getEngine(env, self);
- jbyte *array = (*env)->GetByteArrayElements(env, value, NULL);
- uc_err err = uc_reg_write(eng, (int)regid, (void *)array);
- if (err != UC_ERR_OK) {
- throwException(env, err);
- }
- (*env)->ReleaseByteArrayElements(env, value, array, JNI_ABORT);
-}
-
-/*
- * Class: unicorn_Unicorn
- * Method: reg_read
- * Signature: (II)[B
- */
-JNIEXPORT jbyteArray JNICALL Java_unicorn_Unicorn_reg_1read(JNIEnv *env,
- jobject self,
- jint regid,
- jint regsz)
-{
- uc_engine *eng = getEngine(env, self);
- jbyteArray regval = (*env)->NewByteArray(env, (jsize)regsz);
- jbyte *array = (*env)->GetByteArrayElements(env, regval, NULL);
- uc_err err = uc_reg_read(eng, (int)regid, (void *)array);
- if (err != UC_ERR_OK) {
- throwException(env, err);
- }
- (*env)->ReleaseByteArrayElements(env, regval, array, 0);
- return regval;
-}
-
-/*
- * Class: unicorn_Unicorn
- * Method: mem_write
- * Signature: (J[B)V
- */
-JNIEXPORT void JNICALL Java_unicorn_Unicorn_mem_1write(JNIEnv *env,
- jobject self,
- jlong address,
- jbyteArray bytes)
-{
-
- uc_engine *eng = getEngine(env, self);
- jbyte *array = (*env)->GetByteArrayElements(env, bytes, NULL);
- jsize size = (*env)->GetArrayLength(env, bytes);
- uc_err err = uc_mem_write(eng, (uint64_t)address, array, (size_t)size);
-
- if (err != UC_ERR_OK) {
- throwException(env, err);
- }
-
- (*env)->ReleaseByteArrayElements(env, bytes, array, JNI_ABORT);
-}
-
-/*
- * Class: unicorn_Unicorn
- * Method: mem_read
- * Signature: (JJ)[B
- */
-JNIEXPORT jbyteArray JNICALL Java_unicorn_Unicorn_mem_1read(JNIEnv *env,
- jobject self,
- jlong address,
- jlong size)
-{
- uc_engine *eng = getEngine(env, self);
-
- jbyteArray bytes = (*env)->NewByteArray(env, (jsize)size);
- jbyte *array = (*env)->GetByteArrayElements(env, bytes, NULL);
- uc_err err = uc_mem_read(eng, (uint64_t)address, array, (size_t)size);
- if (err != UC_ERR_OK) {
- throwException(env, err);
- }
- (*env)->ReleaseByteArrayElements(env, bytes, array, 0);
- return bytes;
-}
-
-/*
- * Class: unicorn_Unicorn
- * Method: emu_start
- * Signature: (JJJJ)V
- */
-JNIEXPORT void JNICALL Java_unicorn_Unicorn_emu_1start(JNIEnv *env,
- jobject self,
- jlong begin, jlong until,
- jlong timeout,
- jlong count)
-{
- uc_engine *eng = getEngine(env, self);
-
- uc_err err = uc_emu_start(eng, (uint64_t)begin, (uint64_t)until,
- (uint64_t)timeout, (size_t)count);
- if (err != UC_ERR_OK) {
- throwException(env, err);
- }
-}
-
-/*
- * Class: unicorn_Unicorn
- * Method: emu_stop
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_unicorn_Unicorn_emu_1stop(JNIEnv *env, jobject self)
-{
- uc_engine *eng = getEngine(env, self);
-
- uc_err err = uc_emu_stop(eng);
- if (err != UC_ERR_OK) {
- throwException(env, err);
- }
-}
-
-/*
- * Class: unicorn_Unicorn
- * Method: registerHook
+ * Method: _query
* Signature: (JI)J
*/
-JNIEXPORT jlong JNICALL Java_unicorn_Unicorn_registerHook__JI(JNIEnv *env,
- jclass clz,
- jlong eng,
- jint type)
+JNIEXPORT jlong JNICALL Java_unicorn_Unicorn__1query(JNIEnv *env, jclass clazz,
+ jlong uc, jint type)
{
- uc_hook hh = 0;
- uc_err err = 0;
- switch (type) {
- case UC_HOOK_INTR: // Hook all interrupt events
- if (invokeInterruptCallbacks == 0) {
- invokeInterruptCallbacks = (*env)->GetStaticMethodID(
- env, clz, "invokeInterruptCallbacks", "(JI)V");
+ size_t result;
+ uc_err err = uc_query((uc_engine *)uc, type, &result);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return 0;
+ }
+ return result;
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _errno
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_unicorn_Unicorn__1errno(JNIEnv *env, jclass clazz,
+ jlong uc)
+{
+ return uc_errno((uc_engine *)uc);
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _strerror
+ * Signature: (I)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_unicorn_Unicorn__1strerror(JNIEnv *env,
+ jclass clazz,
+ jint code)
+{
+ const char *err = uc_strerror((int)code);
+ return (*env)->NewStringUTF(env, err);
+}
+
+static void deleteHookWrapper(JNIEnv *env, struct hook_wrapper *hh)
+{
+ if (hh) {
+ if (hh->unicorn)
+ (*env)->DeleteGlobalRef(env, hh->unicorn);
+ if (hh->hook_obj)
+ (*env)->DeleteGlobalRef(env, hh->hook_obj);
+ if (hh->user_data)
+ (*env)->DeleteGlobalRef(env, hh->user_data);
+ free(hh);
+ }
+}
+
+static struct hook_wrapper *makeHookWrapper(JNIEnv *env, jobject self,
+ jobject callback, jobject user_data,
+ const char *hook_name,
+ const char *hook_sig)
+{
+ struct hook_wrapper *hh = calloc(1, sizeof(struct hook_wrapper));
+ if (!hh) {
+ throwOutOfMemoryError(env, "Unable to allocate hook_wrapper");
+ return NULL;
+ }
+
+ hh->unicorn = (*env)->NewGlobalRef(env, self);
+ if (!hh->unicorn) {
+ deleteHookWrapper(env, hh);
+ return NULL;
+ }
+
+ hh->hook_obj = (*env)->NewGlobalRef(env, callback);
+ if (!hh->hook_obj) {
+ deleteHookWrapper(env, hh);
+ return NULL;
+ }
+
+ jclass clazz = (*env)->GetObjectClass(env, callback);
+ if (!clazz) {
+ deleteHookWrapper(env, hh);
+ return NULL;
+ }
+
+ hh->hook_meth = (*env)->GetMethodID(env, clazz, hook_name, hook_sig);
+ if (!hh->hook_meth) {
+ deleteHookWrapper(env, hh);
+ return NULL;
+ }
+
+ if (user_data) {
+ hh->user_data = (*env)->NewGlobalRef(env, user_data);
+ if (!hh->user_data) {
+ deleteHookWrapper(env, hh);
+ return NULL;
}
- err = uc_hook_add((uc_engine *)eng, &hh, (uc_hook_type)type,
- cb_hookintr, env, 1, 0);
- break;
- case UC_HOOK_MEM_FETCH_UNMAPPED: // Hook for all invalid memory access
- // events
- case UC_HOOK_MEM_READ_UNMAPPED: // Hook for all invalid memory access events
- case UC_HOOK_MEM_WRITE_UNMAPPED: // Hook for all invalid memory access
- // events
- case UC_HOOK_MEM_FETCH_PROT: // Hook for all invalid memory access events
- case UC_HOOK_MEM_READ_PROT: // Hook for all invalid memory access events
- case UC_HOOK_MEM_WRITE_PROT: // Hook for all invalid memory access events
- if (invokeEventMemCallbacks == 0) {
- invokeEventMemCallbacks = (*env)->GetStaticMethodID(
- env, clz, "invokeEventMemCallbacks", "(JIJIJ)Z");
- }
- err = uc_hook_add((uc_engine *)eng, &hh, (uc_hook_type)type,
- cb_eventmem, env, 1, 0);
- break;
+ }
+
+ return hh;
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _hook_add
+ * Signature: (JILunicorn/Hook;Ljava/lang/Object;JJ)J
+ */
+JNIEXPORT jlong JNICALL
+Java_unicorn_Unicorn__1hook_1add__JILunicorn_Hook_2Ljava_lang_Object_2JJ(
+ JNIEnv *env, jobject self, jlong uc, jint type, jobject callback,
+ jobject user_data, jlong begin, jlong end)
+{
+ const char *hook_sig;
+ void *hook_callback;
+
+ if (type == UC_HOOK_INTR) {
+ hook_sig = sig_InterruptHook;
+ hook_callback = cb_hookintr;
+ } else if (type == UC_HOOK_CODE || type == UC_HOOK_BLOCK) {
+ hook_sig = sig_CodeHook; // also BlockHook
+ hook_callback = cb_hookcode;
+ } else if ((type & UC_HOOK_MEM_INVALID) && !(type & ~UC_HOOK_MEM_INVALID)) {
+ hook_sig = sig_EventMemHook;
+ hook_callback = cb_eventmem;
+ } else if ((type & UC_HOOK_MEM_VALID) && !(type & ~UC_HOOK_MEM_VALID)) {
+ hook_sig = sig_MemHook;
+ hook_callback = cb_hookmem;
+ } else if (type == UC_HOOK_INSN_INVALID) {
+ hook_sig = sig_InvalidInstructionHook;
+ hook_callback = cb_hookinsn_invalid;
+ } else if (type == UC_HOOK_EDGE_GENERATED) {
+ hook_sig = sig_EdgeGeneratedHook;
+ hook_callback = cb_edge_gen;
+ } else if (type == UC_HOOK_TLB_FILL) {
+ hook_sig = sig_TlbFillHook;
+ hook_callback = cb_tlbevent;
+ } else {
+ throwUnicornException(env, UC_ERR_HOOK);
+ return 0;
+ }
+
+ struct hook_wrapper *hh =
+ makeHookWrapper(env, self, callback, user_data, "hook", hook_sig);
+ uc_err err = uc_hook_add((uc_engine *)uc, &hh->uc_hh, type, hook_callback,
+ hh, begin, end);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ deleteHookWrapper(env, hh);
+ return 0;
}
return (jlong)hh;
}
/*
* Class: unicorn_Unicorn
- * Method: registerHook
- * Signature: (JII)J
+ * Method: _hook_add
+ * Signature: (JILunicorn/Hook;Ljava/lang/Object;JJI)J
*/
-JNIEXPORT jlong JNICALL Java_unicorn_Unicorn_registerHook__JII(
- JNIEnv *env, jclass clz, jlong eng, jint type, jint arg1)
+JNIEXPORT jlong JNICALL
+Java_unicorn_Unicorn__1hook_1add__JILunicorn_Hook_2Ljava_lang_Object_2JJI(
+ JNIEnv *env, jobject self, jlong uc, jint type, jobject callback,
+ jobject user_data, jlong begin, jlong end, jint arg)
{
- uc_hook hh = 0;
- uc_err err = 0;
- switch (type) {
- case UC_HOOK_INSN: // Hook a particular instruction
- switch (arg1) {
- case UC_X86_INS_OUT:
- if (invokeOutCallbacks == 0) {
- invokeOutCallbacks = (*env)->GetStaticMethodID(
- env, clz, "invokeOutCallbacks", "(JIII)V");
- }
- err = uc_hook_add((uc_engine *)eng, &hh, (uc_hook_type)type,
- cb_insn_out, env, 1, 0, arg1);
+ const char *hook_sig;
+ void *hook_callback;
+
+ if (type == UC_HOOK_INSN) {
+ switch (arg) {
case UC_X86_INS_IN:
- if (invokeInCallbacks == 0) {
- invokeInCallbacks = (*env)->GetStaticMethodID(
- env, clz, "invokeInCallbacks", "(JII)I");
- }
- err = uc_hook_add((uc_engine *)eng, &hh, (uc_hook_type)type,
- cb_insn_in, env, 1, 0, arg1);
- case UC_X86_INS_SYSENTER:
+ hook_sig = sig_InHook;
+ hook_callback = cb_insn_in;
+ break;
+ case UC_X86_INS_OUT:
+ hook_sig = sig_OutHook;
+ hook_callback = cb_insn_out;
+ break;
case UC_X86_INS_SYSCALL:
- if (invokeSyscallCallbacks == 0) {
- invokeSyscallCallbacks = (*env)->GetStaticMethodID(
- env, clz, "invokeSyscallCallbacks", "(J)V");
- }
- err = uc_hook_add((uc_engine *)eng, &hh, (uc_hook_type)type,
- cb_insn_syscall, env, 1, 0, arg1);
+ case UC_X86_INS_SYSENTER:
+ hook_sig = sig_SyscallHook;
+ hook_callback = cb_insn_syscall;
+ break;
+ case UC_X86_INS_CPUID:
+ hook_sig = sig_CpuidHook;
+ hook_callback = cb_insn_cpuid;
+ break;
+ case UC_ARM64_INS_MRS:
+ case UC_ARM64_INS_MSR:
+ case UC_ARM64_INS_SYS:
+ case UC_ARM64_INS_SYSL:
+ hook_sig = sig_Arm64SysHook;
+ hook_callback = cb_insn_sys;
+ break;
+ default:
+ throwUnicornException(env, UC_ERR_INSN_INVALID);
+ return 0;
}
- break;
+ } else {
+ throwUnicornException(env, UC_ERR_HOOK);
+ return 0;
+ }
+
+ struct hook_wrapper *hh =
+ makeHookWrapper(env, self, callback, user_data, "hook", hook_sig);
+ uc_err err = uc_hook_add((uc_engine *)uc, &hh->uc_hh, type, hook_callback,
+ hh, begin, end, arg);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ deleteHookWrapper(env, hh);
+ return 0;
}
return (jlong)hh;
}
/*
* Class: unicorn_Unicorn
- * Method: registerHook
- * Signature: (JIJJ)J
+ * Method: _hook_add
+ * Signature: (JILunicorn/Hook;Ljava/lang/Object;JJII)J
*/
-JNIEXPORT jlong JNICALL Java_unicorn_Unicorn_registerHook__JIJJ(
- JNIEnv *env, jclass clz, jlong eng, jint type, jlong arg1, jlong arg2)
+JNIEXPORT jlong JNICALL
+Java_unicorn_Unicorn__1hook_1add__JILunicorn_Hook_2Ljava_lang_Object_2JJII(
+ JNIEnv *env, jobject self, jlong uc, jint type, jobject callback,
+ jobject user_data, jlong begin, jlong end, jint arg1, jint arg2)
{
- uc_hook hh = 0;
- uc_err err = 0;
- switch (type) {
- case UC_HOOK_CODE: // Hook a range of code
- if (invokeCodeCallbacks == 0) {
- invokeCodeCallbacks = (*env)->GetStaticMethodID(
- env, clz, "invokeCodeCallbacks", "(JJI)V");
- }
- err = uc_hook_add((uc_engine *)eng, &hh, (uc_hook_type)type,
- cb_hookcode, env, 1, 0, arg1, arg2);
- break;
- case UC_HOOK_BLOCK: // Hook basic blocks
- if (invokeBlockCallbacks == 0) {
- invokeBlockCallbacks = (*env)->GetStaticMethodID(
- env, clz, "invokeBlockCallbacks", "(JJI)V");
- }
- err = uc_hook_add((uc_engine *)eng, &hh, (uc_hook_type)type,
- cb_hookblock, env, 1, 0, arg1, arg2);
- break;
- case UC_HOOK_MEM_READ: // Hook all memory read events.
- if (invokeReadCallbacks == 0) {
- invokeReadCallbacks = (*env)->GetStaticMethodID(
- env, clz, "invokeReadCallbacks", "(JJI)V");
- }
- err = uc_hook_add((uc_engine *)eng, &hh, (uc_hook_type)type, cb_hookmem,
- env, 1, 0, arg1, arg2);
- break;
- case UC_HOOK_MEM_WRITE: // Hook all memory write events.
- if (invokeWriteCallbacks == 0) {
- invokeWriteCallbacks = (*env)->GetStaticMethodID(
- env, clz, "invokeWriteCallbacks", "(JJIJ)V");
- }
- err = uc_hook_add((uc_engine *)eng, &hh, (uc_hook_type)type, cb_hookmem,
- env, 1, 0, arg1, arg2);
- break;
+ const char *hook_sig;
+ void *hook_callback;
+
+ if (type == UC_HOOK_TCG_OPCODE) {
+ hook_sig = sig_TcgOpcodeHook;
+ hook_callback = cb_tcg_op_2;
+ } else {
+ throwUnicornException(env, UC_ERR_HOOK);
+ return 0;
+ }
+
+ struct hook_wrapper *hh =
+ makeHookWrapper(env, self, callback, user_data, "hook", hook_sig);
+ uc_err err = uc_hook_add((uc_engine *)uc, &hh->uc_hh, type, hook_callback,
+ hh, begin, end, arg1, arg2);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ deleteHookWrapper(env, hh);
+ return 0;
}
return (jlong)hh;
}
/*
* Class: unicorn_Unicorn
- * Method: hook_del
- * Signature: (J)V
- */
-JNIEXPORT void JNICALL Java_unicorn_Unicorn_hook_1del(JNIEnv *env, jobject self,
- jlong hh)
-{
- uc_engine *eng = getEngine(env, self);
-
- //**** TODO remove hook from any internal hook tables as well
-
- uc_err err = uc_hook_del(eng, (uc_hook)hh);
- if (err != UC_ERR_OK) {
- throwException(env, err);
- }
-}
-
-/*
- * Class: unicorn_Unicorn
- * Method: mem_map
- * Signature: (JJI)V
- */
-JNIEXPORT void JNICALL Java_unicorn_Unicorn_mem_1map(JNIEnv *env, jobject self,
- jlong address, jlong size,
- jint perms)
-{
- uc_engine *eng = getEngine(env, self);
-
- uc_err err =
- uc_mem_map(eng, (uint64_t)address, (size_t)size, (uint32_t)perms);
- if (err != UC_ERR_OK) {
- throwException(env, err);
- }
-}
-
-/*
- * 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
+ * Method: _hook_del
* Signature: (JJ)V
*/
-JNIEXPORT void JNICALL Java_unicorn_Unicorn_mem_1unmap(JNIEnv *env,
- jobject self,
- jlong address,
- jlong size)
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1hook_1del(JNIEnv *env,
+ jclass clazz, jlong uc,
+ jlong hh)
{
- uc_engine *eng = getEngine(env, self);
-
- uc_err err = uc_mem_unmap(eng, (uint64_t)address, (size_t)size);
- if (err != UC_ERR_OK) {
- throwException(env, err);
+ struct hook_wrapper *h = (struct hook_wrapper *)hh;
+ uc_hook_del((uc_engine *)uc, h->uc_hh);
+ if (h->unicorn) {
+ (*env)->DeleteGlobalRef(env, h->unicorn);
+ h->unicorn = NULL;
+ }
+ if (h->hook_obj) {
+ (*env)->DeleteGlobalRef(env, h->hook_obj);
+ h->hook_obj = NULL;
+ }
+ if (h->user_data) {
+ (*env)->DeleteGlobalRef(env, h->user_data);
+ h->user_data = NULL;
}
-
- // If a region was mapped using uc_mem_map_ptr, we also need to
- // ReleaseByteArrayElements for that region
}
/*
* Class: unicorn_Unicorn
- * Method: mem_protect
- * Signature: (JJI)V
+ * Method: _hookwrapper_free
+ * Signature: (J)V
*/
-JNIEXPORT void JNICALL Java_unicorn_Unicorn_mem_1protect(JNIEnv *env,
- jobject self,
- jlong address,
- jlong size, jint perms)
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1hookwrapper_1free(JNIEnv *env,
+ jclass clazz,
+ jlong hh)
{
- uc_engine *eng = getEngine(env, self);
+ deleteHookWrapper(env, (struct hook_wrapper *)hh);
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _mmio_map
+ * Signature:
+ * (JJJLunicorn/MmioReadHandler;Ljava/lang/Object;Lunicorn/MmioWriteHandler;Ljava/lang/Object;)[J
+ */
+JNIEXPORT jlongArray JNICALL Java_unicorn_Unicorn__1mmio_1map(
+ JNIEnv *env, jobject self, jlong uc, jlong address, jlong size,
+ jobject read_cb, jobject user_data_read, jobject write_cb,
+ jobject user_data_write)
+{
+ struct hook_wrapper *hooks[2] = {0};
+
+ if (read_cb) {
+ hooks[0] = makeHookWrapper(env, self, read_cb, user_data_read, "read",
+ sig_MmioReadHandler);
+ if (!hooks[0]) {
+ goto fail;
+ }
+ }
+
+ if (write_cb) {
+ hooks[1] = makeHookWrapper(env, self, write_cb, user_data_write,
+ "write", sig_MmioWriteHandler);
+ if (!hooks[1]) {
+ goto fail;
+ }
+ }
+
+ jlong hooksLong[2];
+ size_t hooksCount = 0;
+ if (hooks[0])
+ hooksLong[hooksCount++] = (jlong)hooks[0];
+ if (hooks[1])
+ hooksLong[hooksCount++] = (jlong)hooks[1];
+
+ jlongArray result = (*env)->NewLongArray(env, hooksCount);
+ if (result == NULL) {
+ goto fail;
+ }
+ (*env)->SetLongArrayRegion(env, result, 0, hooksCount, hooksLong);
+
+ uc_err err = uc_mmio_map((uc_engine *)uc, address, size,
+ (hooks[0] ? cb_mmio_read : NULL), hooks[0],
+ (hooks[1] ? cb_mmio_write : NULL), hooks[1]);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ goto fail;
+ }
+ return result;
+fail:
+ deleteHookWrapper(env, hooks[0]);
+ deleteHookWrapper(env, hooks[1]);
+ return NULL;
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _mem_map
+ * Signature: (JJJI)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1mem_1map(JNIEnv *env,
+ jclass clazz, jlong uc,
+ jlong address,
+ jlong size, jint perms)
+{
+ uc_err err = uc_mem_map((uc_engine *)uc, address, size, perms);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _mem_map_ptr
+ * Signature: (JJLjava/nio/Buffer;I)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1mem_1map_1ptr(
+ JNIEnv *env, jclass clazz, jlong uc, jlong address, jobject buf, jint perms)
+{
+ jlong size = (*env)->GetDirectBufferCapacity(env, buf);
+ void *host_address = (*env)->GetDirectBufferAddress(env, buf);
+ if (size < 0 || host_address == NULL) {
+ throwCustomUnicornException(env,
+ "mem_map_ptr requires a direct buffer");
+ return;
+ }
uc_err err =
- uc_mem_protect(eng, (uint64_t)address, (size_t)size, (uint32_t)perms);
+ uc_mem_map_ptr((uc_engine *)uc, address, size, perms, host_address);
if (err != UC_ERR_OK) {
- throwException(env, err);
+ throwUnicornException(env, err);
+ return;
}
}
/*
* Class: unicorn_Unicorn
- * Method: mem_regions
- * Signature: ()[Lunicorn/MemRegion;
+ * Method: _mem_unmap
+ * Signature: (JJJ)V
*/
-JNIEXPORT jobjectArray JNICALL Java_unicorn_Unicorn_mem_1regions(JNIEnv *env,
- jobject self)
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1mem_1unmap(JNIEnv *env,
+ jclass clazz, jlong uc,
+ jlong address,
+ jlong size)
{
- uc_engine *eng = getEngine(env, self);
+ uc_err err = uc_mem_unmap((uc_engine *)uc, address, size);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _mem_protect
+ * Signature: (JJJI)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1mem_1protect(
+ JNIEnv *env, jclass clazz, jlong uc, jlong address, jlong size, jint perms)
+{
+ uc_err err = uc_mem_protect((uc_engine *)uc, address, size, perms);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _mem_regions
+ * Signature: (J)[Lunicorn/MemRegion;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_unicorn_Unicorn__1mem_1regions(JNIEnv *env, jclass uc_clazz, jlong uc)
+{
+ static jclass clazz;
+ if (!clazz) {
+ clazz = (*env)->FindClass(env, "unicorn/MemRegion");
+ if (!clazz)
+ return NULL;
+ clazz = (*env)->NewGlobalRef(env, clazz);
+ if (!clazz)
+ return NULL;
+ }
+
+ static jmethodID clazzInit;
+ if (!clazzInit) {
+ clazzInit = (*env)->GetMethodID(env, clazz, "", "(JJI)V");
+ if (!clazzInit)
+ return NULL;
+ }
uc_mem_region *regions = NULL;
uint32_t count = 0;
uint32_t i;
- uc_err err = uc_mem_regions(eng, ®ions, &count);
+ uc_err err = uc_mem_regions((uc_engine *)uc, ®ions, &count);
if (err != UC_ERR_OK) {
- throwException(env, err);
- }
- jclass clz = (*env)->FindClass(env, "unicorn/MemRegion");
- if ((*env)->ExceptionCheck(env)) {
+ throwUnicornException(env, err);
return NULL;
}
- jobjectArray result = (*env)->NewObjectArray(env, (jsize)count, clz, NULL);
- jmethodID cons = (*env)->GetMethodID(env, clz, "", "(JJI)V");
+
+ jobjectArray result =
+ (*env)->NewObjectArray(env, (jsize)count, clazz, NULL);
+ if (!result) {
+ uc_free(regions);
+ return NULL;
+ }
+
for (i = 0; i < count; i++) {
- jobject mr = (*env)->NewObject(env, clz, cons, regions[i].begin,
- regions[i].end, regions[i].perms);
+ jobject mr =
+ (*env)->NewObject(env, clazz, clazzInit, (jlong)regions[i].begin,
+ (jlong)regions[i].end, (jint)regions[i].perms);
+ if (!mr) {
+ uc_free(regions);
+ return NULL;
+ }
(*env)->SetObjectArrayElement(env, result, (jsize)i, mr);
}
uc_free(regions);
@@ -823,79 +1262,362 @@ JNIEXPORT jobjectArray JNICALL Java_unicorn_Unicorn_mem_1regions(JNIEnv *env,
/*
* Class: unicorn_Unicorn
- * Method: context_alloc
- * Signature: ()J
+ * Method: _context_alloc
+ * Signature: (J)J
*/
-JNIEXPORT jlong JNICALL Java_unicorn_Unicorn_context_1alloc(JNIEnv *env,
- jobject self)
+JNIEXPORT jlong JNICALL Java_unicorn_Unicorn__1context_1alloc(JNIEnv *env,
+ jclass clazz,
+ jlong uc)
{
- uc_engine *eng = getEngine(env, self);
uc_context *ctx;
- uc_err err = uc_context_alloc(eng, &ctx);
+ uc_err err = uc_context_alloc((uc_engine *)uc, &ctx);
if (err != UC_ERR_OK) {
- throwException(env, err);
+ throwUnicornException(env, err);
+ return 0;
}
- return (jlong)(uint64_t)ctx;
+ return (jlong)ctx;
}
/*
* Class: unicorn_Unicorn
- * Method: free
+ * Method: _context_free
* Signature: (J)V
*/
-JNIEXPORT void JNICALL Java_unicorn_Unicorn_free(JNIEnv *env, jobject self,
- jlong ctx)
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1context_1free(JNIEnv *env,
+ jclass clazz,
+ jlong ctx)
{
- uc_err err = uc_free((void *)ctx);
+ uc_err err = uc_context_free((uc_context *)ctx);
if (err != UC_ERR_OK) {
- throwException(env, err);
+ throwUnicornException(env, err);
+ return;
}
}
/*
* Class: unicorn_Unicorn
- * Method: context_save
+ * Method: _context_save
+ * Signature: (JJ)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1context_1save(JNIEnv *env,
+ jclass clazz,
+ jlong uc, jlong ctx)
+{
+ uc_err err = uc_context_save((uc_engine *)uc, (uc_context *)ctx);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _context_restore
+ * Signature: (JJ)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1context_1restore(JNIEnv *env,
+ jclass clazz,
+ jlong uc,
+ jlong ctx)
+{
+ uc_err err = uc_context_restore((uc_engine *)uc, (uc_context *)ctx);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _ctl_get_mode
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_unicorn_Unicorn__1ctl_1get_1mode(JNIEnv *env,
+ jclass clazz,
+ jlong uc)
+{
+ int mode;
+ uc_err err = uc_ctl_get_mode((uc_engine *)uc, &mode);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return 0;
+ }
+ return mode;
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _ctl_get_arch
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_unicorn_Unicorn__1ctl_1get_1arch(JNIEnv *env,
+ jclass clazz,
+ jlong uc)
+{
+ int arch;
+ uc_err err = uc_ctl_get_arch((uc_engine *)uc, &arch);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return 0;
+ }
+ return arch;
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _ctl_get_timeout
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_unicorn_Unicorn__1ctl_1get_1timeout(JNIEnv *env,
+ jclass clazz,
+ jlong uc)
+{
+ uint64_t timeout;
+ uc_err err = uc_ctl_get_timeout((uc_engine *)uc, &timeout);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return 0;
+ }
+ return timeout;
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _ctl_get_page_size
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_unicorn_Unicorn__1ctl_1get_1page_1size(JNIEnv *env,
+ jclass clazz,
+ jlong uc)
+{
+ uint32_t page_size;
+ uc_err err = uc_ctl_get_page_size((uc_engine *)uc, &page_size);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return 0;
+ }
+ return page_size;
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _ctl_set_page_size
+ * Signature: (JI)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1ctl_1set_1page_1size(
+ JNIEnv *env, jclass clazz, jlong uc, jint page_size)
+{
+ uc_err err = uc_ctl_set_page_size((uc_engine *)uc, (uint32_t)page_size);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _ctl_set_use_exits
+ * Signature: (JZ)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1ctl_1set_1use_1exits(
+ JNIEnv *env, jclass clazz, jlong uc, jboolean value)
+{
+ uc_err err;
+ if (value) {
+ err = uc_ctl_exits_enable((uc_engine *)uc);
+ } else {
+ err = uc_ctl_exits_disable((uc_engine *)uc);
+ }
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _ctl_get_exits_cnt
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL
+Java_unicorn_Unicorn__1ctl_1get_1exits_1cnt(JNIEnv *env, jclass clazz, jlong uc)
+{
+ size_t exits_cnt;
+ uc_err err = uc_ctl_get_exits_cnt((uc_engine *)uc, &exits_cnt);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return 0;
+ }
+ return exits_cnt;
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _ctl_get_exits
+ * Signature: (J)[J
+ */
+JNIEXPORT jlongArray JNICALL
+Java_unicorn_Unicorn__1ctl_1get_1exits(JNIEnv *env, jclass clazz, jlong uc)
+{
+ size_t exits_cnt;
+ uc_err err = uc_ctl_get_exits_cnt((uc_engine *)uc, &exits_cnt);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return 0;
+ }
+
+ jlongArray result = (*env)->NewLongArray(env, (jsize)exits_cnt);
+ if (!result)
+ return NULL;
+
+ jlong *resultArr = (*env)->GetLongArrayElements(env, result, NULL);
+ if (!resultArr)
+ return NULL;
+
+ err = uc_ctl_get_exits((uc_engine *)uc, (uint64_t *)resultArr, exits_cnt);
+ (*env)->ReleaseLongArrayElements(env, result, resultArr, 0);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return 0;
+ }
+ return result;
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _ctl_set_exits
+ * Signature: (J[J)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1ctl_1set_1exits(JNIEnv *env,
+ jclass clazz,
+ jlong uc,
+ jlongArray exits)
+{
+ jsize count = (*env)->GetArrayLength(env, exits);
+ jlong *arr = (*env)->GetLongArrayElements(env, exits, NULL);
+ if (!arr)
+ return;
+
+ uc_err err =
+ uc_ctl_set_exits((uc_engine *)uc, (uint64_t *)arr, (size_t)count);
+ (*env)->ReleaseLongArrayElements(env, exits, arr, JNI_ABORT);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _ctl_get_cpu_model
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_unicorn_Unicorn__1ctl_1get_1cpu_1model(JNIEnv *env,
+ jclass clazz,
+ jlong uc)
+{
+ int cpu_model;
+ uc_err err = uc_ctl_get_cpu_model((uc_engine *)uc, &cpu_model);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return 0;
+ }
+ return cpu_model;
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _ctl_set_cpu_model
+ * Signature: (JI)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1ctl_1set_1cpu_1model(
+ JNIEnv *env, jclass clazz, jlong uc, jint cpu_model)
+{
+ uc_err err = uc_ctl_set_cpu_model((uc_engine *)uc, (int)cpu_model);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _ctl_request_cache
+ * Signature: (JJ)Lunicorn/TranslationBlock;
+ */
+JNIEXPORT jobject JNICALL Java_unicorn_Unicorn__1ctl_1request_1cache(
+ JNIEnv *env, jclass clazz, jlong uc, jlong address)
+{
+ uc_tb tb;
+ uc_err err = uc_ctl_request_cache((uc_engine *)uc, (uint64_t)address, &tb);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return NULL;
+ }
+ return makeTranslationBlock(env, &tb);
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _ctl_remove_cache
+ * Signature: (JJJ)V
+ */
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1ctl_1remove_1cache(
+ JNIEnv *env, jclass clazz, jlong uc, jlong address, jlong end)
+{
+ uc_err err =
+ uc_ctl_remove_cache((uc_engine *)uc, (uint64_t)address, (uint64_t)end);
+ if (err != UC_ERR_OK) {
+ throwUnicornException(env, err);
+ return;
+ }
+}
+
+/*
+ * Class: unicorn_Unicorn
+ * Method: _ctl_flush_tb
* Signature: (J)V
*/
-JNIEXPORT void JNICALL Java_unicorn_Unicorn_context_1save(JNIEnv *env,
- jobject self,
- jlong ctx)
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1ctl_1flush_1tb(JNIEnv *env,
+ jclass clazz,
+ jlong uc)
{
- uc_engine *eng = getEngine(env, self);
- uc_err err = uc_context_save(eng, (uc_context *)ctx);
+ uc_err err = uc_ctl_flush_tb((uc_engine *)uc);
if (err != UC_ERR_OK) {
- throwException(env, err);
+ throwUnicornException(env, err);
+ return;
}
}
/*
* Class: unicorn_Unicorn
- * Method: context_restore
+ * Method: _ctl_flush_tlb
* Signature: (J)V
*/
-JNIEXPORT void JNICALL Java_unicorn_Unicorn_context_1restore(JNIEnv *env,
- jobject self,
- jlong ctx)
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1ctl_1flush_1tlb(JNIEnv *env,
+ jclass clazz,
+ jlong uc)
{
- uc_engine *eng = getEngine(env, self);
- uc_err err = uc_context_restore(eng, (uc_context *)ctx);
+ uc_err err = uc_ctl_flush_tlb((uc_engine *)uc);
if (err != UC_ERR_OK) {
- throwException(env, err);
+ throwUnicornException(env, err);
+ return;
}
}
/*
* Class: unicorn_Unicorn
- * Method: ctl_set_cpu_model
- * Signature: (I)V
+ * Method: _ctl_tlb_mode
+ * Signature: (JI)V
*/
-JNIEXPORT void JNICALL Java_unicorn_Unicorn_ctl_1set_1cpu_1model(JNIEnv *env,
- jobject self,
- jint cpu_model)
+JNIEXPORT void JNICALL Java_unicorn_Unicorn__1ctl_1tlb_1mode(JNIEnv *env,
+ jclass clazz,
+ jlong uc,
+ jint mode)
{
- uc_engine *eng = getEngine(env, self);
- uc_err err = uc_ctl_set_cpu_model(eng, cpu_model);
+ uc_err err = uc_ctl_tlb_mode((uc_engine *)uc, (int)mode);
if (err != UC_ERR_OK) {
- throwException(env, err);
+ throwUnicornException(env, err);
+ return;
}
}