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; } }