From aa430587ccb2dec83da36fe01aa15ddf52262bf5 Mon Sep 17 00:00:00 2001 From: Robert Xiao Date: Sat, 6 May 2023 17:49:41 -0700 Subject: [PATCH] Rewrite the Java bindings. This brings the Java API up to par with Python feature-wise and substantially simplifies the hook implementation, enabling proper bounds-checked hooks. The rewrite strives for compatibility with the previous API, but there are some breaking changes. It is possible to push closer to full backwards compatibility if required, at the cost of reintroducing some of the suboptimal designs. Here are the main points of breakage: - ReadHook and WriteHook are gone, replaced simply by MemHook. Hooking valid memory accesses now requires a type parameter. This enables fetch and read-after hooks with a unified API and a single callback object. - mem_read now takes an int, not a long. We are unable to allocate more than 2GB in a single request anyway (Java limitation). - Instruction hooks now require specifying the instruction explicitly, instead of guessing based on the hook type. This is necessary to distinguish sysenter/syscall and ARM64 mrs/msr/sys/sysl, without excessively bloating the library with redundant hook types. Bounds must also be specified, to support bounds-checked instruction hooks. - Reading object-type registers (any register larger than 64 bits, or registers with special formats) requires a second argument to reg_read. This allows us to provide a fast reg_read that returns a long for the common cases, while still supporting a more general reg_read for other registers. - mem_map_ptr is rewritten to take a *direct* java.nio.Buffer, which enables many more use cases than a simple byte array, and improves performance (a byte array cannot really be used as a mapped buffer without GC-pinning it, which hurts the GC performance). - Context handling API is redesigned to be safer and more object-oriented. A lot of bugs are fixed with this implementation: - Unicorn instances can be properly garbage-collected, instead of hanging around forever in the Unicorn.unicorns table. - Hooks no longer fire outside of their bounds (#1164), and in fact, hook bounds are properly respected (previously, all hooks were just registered globally to all addresses). - Hooks are substantially faster, as they are now dispatched directly via a single method call rather than being indirected through invokeCallbacks. - Loading vector registers works now, rather than crashing the VM (#1539). Several features are now enabled in the Java implementation: - All of the current ctl_* calls are implemented. - mmio_map is implemented. - New virtual TLB mode is implemented. - reading/writing Context registers is implemented. - New hook types are added: TcgOpcodeHook, EdgeGeneratedHook, InvalidInstructionHook, TlbFillHook, and the instruction hooks Arm64SysHook, CpuidHook. - All known special registers are supported. --- bindings/java/Makefile | 4 +- .../java/samples/SampleNetworkAuditing.java | 10 +- bindings/java/samples/Sample_x86.java | 22 +- bindings/java/samples/Sample_x86_mmr.java | 4 +- bindings/java/tests/FunctionalityTests.java | 184 ++ bindings/java/tests/RegressionTests.java | 13 +- bindings/java/unicorn/Arm64SysHook.java | 40 + bindings/java/unicorn/Arm64_CP.java | 43 + bindings/java/unicorn/Arm_CP.java | 26 + bindings/java/unicorn/BlockHook.java | 8 + bindings/java/unicorn/CodeHook.java | 8 + bindings/java/unicorn/CpuidHook.java | 34 + ...{WriteHook.java => EdgeGeneratedHook.java} | 9 +- bindings/java/unicorn/EventMemHook.java | 19 +- bindings/java/unicorn/Hook.java | 5 +- bindings/java/unicorn/InHook.java | 11 +- .../{ReadHook.java => InstructionHook.java} | 5 +- bindings/java/unicorn/InterruptHook.java | 7 + .../java/unicorn/InvalidInstructionHook.java | 36 + bindings/java/unicorn/MemHook.java | 20 +- bindings/java/unicorn/MemRegion.java | 6 + bindings/java/unicorn/MmioReadHandler.java | 37 + bindings/java/unicorn/MmioWriteHandler.java | 37 + bindings/java/unicorn/OutHook.java | 10 +- bindings/java/unicorn/SyscallHook.java | 9 +- bindings/java/unicorn/TcgOpcodeHook.java | 40 + bindings/java/unicorn/TlbFillHook.java | 42 + bindings/java/unicorn/TranslationBlock.java | 35 + bindings/java/unicorn/Unicorn.java | 1517 ++++++------ bindings/java/unicorn/X86_Float80.java | 72 + bindings/java/unicorn/X86_MMR.java | 7 + bindings/java/unicorn/X86_MSR.java | 38 + bindings/java/unicorn_Unicorn.c | 2054 +++++++++++------ 33 files changed, 2991 insertions(+), 1421 deletions(-) create mode 100644 bindings/java/tests/FunctionalityTests.java create mode 100644 bindings/java/unicorn/Arm64SysHook.java create mode 100644 bindings/java/unicorn/Arm64_CP.java create mode 100644 bindings/java/unicorn/Arm_CP.java create mode 100644 bindings/java/unicorn/CpuidHook.java rename bindings/java/unicorn/{WriteHook.java => EdgeGeneratedHook.java} (75%) rename bindings/java/unicorn/{ReadHook.java => InstructionHook.java} (86%) create mode 100644 bindings/java/unicorn/InvalidInstructionHook.java create mode 100644 bindings/java/unicorn/MmioReadHandler.java create mode 100644 bindings/java/unicorn/MmioWriteHandler.java create mode 100644 bindings/java/unicorn/TcgOpcodeHook.java create mode 100644 bindings/java/unicorn/TlbFillHook.java create mode 100644 bindings/java/unicorn/TranslationBlock.java create mode 100644 bindings/java/unicorn/X86_Float80.java create mode 100644 bindings/java/unicorn/X86_MSR.java 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; } }