From d4df61b4c5a2227213c09c28020aedd10f842a67 Mon Sep 17 00:00:00 2001 From: Robert Xiao Date: Sat, 13 May 2023 10:55:13 -0700 Subject: [PATCH] Refactor tests and add a few more --- bindings/java/tests/FunctionalityTests.java | 352 +++++++++----------- bindings/java/tests/HookTests.java | 130 ++++++++ bindings/java/tests/MemTests.java | 138 ++++++++ bindings/java/tests/RegTests.java | 50 +++ bindings/java/unicorn/MemRegion.java | 4 +- bindings/java/unicorn/TranslationBlock.java | 6 + bindings/java/unicorn/Unicorn.java | 3 + 7 files changed, 488 insertions(+), 195 deletions(-) create mode 100644 bindings/java/tests/HookTests.java create mode 100644 bindings/java/tests/MemTests.java create mode 100644 bindings/java/tests/RegTests.java diff --git a/bindings/java/tests/FunctionalityTests.java b/bindings/java/tests/FunctionalityTests.java index 2c8bd79c..38e7908d 100644 --- a/bindings/java/tests/FunctionalityTests.java +++ b/bindings/java/tests/FunctionalityTests.java @@ -2,226 +2,192 @@ package tests; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; +import static org.junit.Assert.assertTrue; import org.junit.Test; -import unicorn.CodeHook; -import unicorn.MemRegion; -import unicorn.TlbFillHook; import unicorn.Unicorn; import unicorn.UnicornException; -import unicorn.X86_Float80; +/** Test miscellaneous features that don't fall into the register, memory + * or hook API */ 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(); + public void testStatics() { + assertEquals(true, Unicorn.arch_supported(Unicorn.UC_ARCH_X86)); + assertEquals(false, Unicorn.arch_supported(Unicorn.UC_ARCH_MAX + 1)); + assertTrue("version check", Unicorn.version() >= 0x02000100); + assertEquals("OK (UC_ERR_OK)", Unicorn.strerror(Unicorn.UC_ERR_OK)); + assertEquals("Invalid handle (UC_ERR_HANDLE)", + Unicorn.strerror(Unicorn.UC_ERR_HANDLE)); + } + + @Test + public void testCreation() { + assertThrows(UnicornException.class, + () -> new Unicorn(Unicorn.UC_ARCH_MAX + 1, 0)); + + if (Unicorn.arch_supported(Unicorn.UC_ARCH_X86)) { + new Unicorn(Unicorn.UC_ARCH_X86, Unicorn.UC_MODE_16); + new Unicorn(Unicorn.UC_ARCH_X86, Unicorn.UC_MODE_32); + new Unicorn(Unicorn.UC_ARCH_X86, Unicorn.UC_MODE_64); + assertThrows(UnicornException.class, + () -> new Unicorn(Unicorn.UC_ARCH_X86, + Unicorn.UC_MODE_BIG_ENDIAN)); + } + + if (Unicorn.arch_supported(Unicorn.UC_ARCH_M68K)) { + new Unicorn(Unicorn.UC_ARCH_M68K, Unicorn.UC_MODE_BIG_ENDIAN); + assertThrows(UnicornException.class, + () -> new Unicorn(Unicorn.UC_ARCH_M68K, + Unicorn.UC_MODE_LITTLE_ENDIAN)); + } + + if (Unicorn.arch_supported(Unicorn.UC_ARCH_ARM)) { + new Unicorn(Unicorn.UC_ARCH_ARM, 0); + new Unicorn(Unicorn.UC_ARCH_ARM, Unicorn.UC_MODE_BIG_ENDIAN); + new Unicorn(Unicorn.UC_ARCH_ARM, Unicorn.UC_MODE_THUMB); + } + + if (Unicorn.arch_supported(Unicorn.UC_ARCH_ARM64)) { + new Unicorn(Unicorn.UC_ARCH_ARM64, 0); + new Unicorn(Unicorn.UC_ARCH_ARM64, Unicorn.UC_MODE_BIG_ENDIAN); + } + + if (Unicorn.arch_supported(Unicorn.UC_ARCH_MIPS)) { + new Unicorn(Unicorn.UC_ARCH_MIPS, + Unicorn.UC_MODE_BIG_ENDIAN | Unicorn.UC_MODE_32); + new Unicorn(Unicorn.UC_ARCH_MIPS, + Unicorn.UC_MODE_LITTLE_ENDIAN | Unicorn.UC_MODE_32); + new Unicorn(Unicorn.UC_ARCH_MIPS, + Unicorn.UC_MODE_BIG_ENDIAN | Unicorn.UC_MODE_64); + new Unicorn(Unicorn.UC_ARCH_MIPS, + Unicorn.UC_MODE_LITTLE_ENDIAN | Unicorn.UC_MODE_64); + assertThrows(UnicornException.class, + () -> new Unicorn(Unicorn.UC_ARCH_MIPS, Unicorn.UC_MODE_16)); + } + + if (Unicorn.arch_supported(Unicorn.UC_ARCH_SPARC)) { + new Unicorn(Unicorn.UC_ARCH_SPARC, + Unicorn.UC_MODE_BIG_ENDIAN | Unicorn.UC_MODE_32); + new Unicorn(Unicorn.UC_ARCH_SPARC, + Unicorn.UC_MODE_BIG_ENDIAN | Unicorn.UC_MODE_64); + assertThrows(UnicornException.class, + () -> new Unicorn(Unicorn.UC_ARCH_SPARC, + Unicorn.UC_MODE_LITTLE_ENDIAN | Unicorn.UC_MODE_32)); + } + + if (Unicorn.arch_supported(Unicorn.UC_ARCH_PPC)) { + new Unicorn(Unicorn.UC_ARCH_PPC, + Unicorn.UC_MODE_BIG_ENDIAN | Unicorn.UC_MODE_32); + new Unicorn(Unicorn.UC_ARCH_PPC, + Unicorn.UC_MODE_BIG_ENDIAN | Unicorn.UC_MODE_64); + assertThrows(UnicornException.class, + () -> new Unicorn(Unicorn.UC_ARCH_PPC, + Unicorn.UC_MODE_LITTLE_ENDIAN | Unicorn.UC_MODE_32)); + } + + if (Unicorn.arch_supported(Unicorn.UC_ARCH_RISCV)) { + new Unicorn(Unicorn.UC_ARCH_RISCV, Unicorn.UC_MODE_32); + new Unicorn(Unicorn.UC_ARCH_RISCV, Unicorn.UC_MODE_64); + } + + if (Unicorn.arch_supported(Unicorn.UC_ARCH_S390X)) { + new Unicorn(Unicorn.UC_ARCH_S390X, Unicorn.UC_MODE_BIG_ENDIAN); + assertThrows(UnicornException.class, + () -> new Unicorn(Unicorn.UC_ARCH_S390X, + Unicorn.UC_MODE_LITTLE_ENDIAN)); + + new Unicorn(Unicorn.UC_ARCH_TRICORE, 0); + } + } + + @Test + public void testThreading() { + // EB FE - label: jmp label + final byte[] X86_CODE = { -21, -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); + new Thread(() -> { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + u.emu_stop(); + }).start(); + u.emu_start(ADDRESS, ADDRESS + X86_CODE.length, 0, 0); + + u.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); + uc.reg_write(Unicorn.UC_ARM64_REG_X0, 0xdeadbeefL); 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)); + assertEquals(0xdeadbeefL, uc.reg_read(Unicorn.UC_ARM64_REG_X0)); + assertEquals(0xdeadbeefL, ctx.reg_read(Unicorn.UC_ARM64_REG_X0)); + + uc.reg_write(Unicorn.UC_ARM64_REG_X0, 0xfeedfaceL); + assertEquals(0xfeedfaceL, uc.reg_read(Unicorn.UC_ARM64_REG_X0)); + assertEquals(0xdeadbeefL, ctx.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); + assertEquals(0xdeadbeefL, uc.reg_read(Unicorn.UC_ARM64_REG_X0)); + assertEquals(0xdeadbeefL, ctx.reg_read(Unicorn.UC_ARM64_REG_X0)); + + uc.reg_write(Unicorn.UC_ARM64_REG_X0, 0xfee1deadL); + assertEquals(0xfee1deadL, uc.reg_read(Unicorn.UC_ARM64_REG_X0)); + assertEquals(0xdeadbeefL, ctx.reg_read(Unicorn.UC_ARM64_REG_X0)); + 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)); + assertEquals(0xfee1deadL, uc.reg_read(Unicorn.UC_ARM64_REG_X0)); + assertEquals(0xfee1deadL, ctx.reg_read(Unicorn.UC_ARM64_REG_X0)); + + uc.reg_write(Unicorn.UC_ARM64_REG_X0, 0xdeadbeefL); + assertEquals(0xdeadbeefL, uc.reg_read(Unicorn.UC_ARM64_REG_X0)); + assertEquals(0xfee1deadL, ctx.reg_read(Unicorn.UC_ARM64_REG_X0)); + uc.context_restore(ctx); - assertEquals("X0 restored", 0xfee1dead, - uc.reg_read(Unicorn.UC_ARM64_REG_X0)); + assertEquals(0xfee1deadL, uc.reg_read(Unicorn.UC_ARM64_REG_X0)); + assertEquals(0xfee1deadL, ctx.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 }; + public void testOldContext() { + Unicorn uc = new Unicorn(Unicorn.UC_ARCH_ARM64, Unicorn.UC_MODE_ARM); + uc.reg_write(Unicorn.UC_ARM64_REG_X0, 0xdeadbeefL); + long ctx = uc.context_alloc(); + uc.context_save(ctx); + assertEquals(0xdeadbeefL, uc.reg_read(Unicorn.UC_ARM64_REG_X0)); - long ADDRESS = 0x100000; + uc.reg_write(Unicorn.UC_ARM64_REG_X0, 0xfeedfaceL); + assertEquals(0xfeedfaceL, uc.reg_read(Unicorn.UC_ARM64_REG_X0)); - 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); + uc.context_restore(ctx); + assertEquals(0xdeadbeefL, uc.reg_read(Unicorn.UC_ARM64_REG_X0)); - // write machine code to be emulated to memory - u.mem_write(ADDRESS, X86_CODE32_MEM_READ_WRITE); + uc.reg_write(Unicorn.UC_ARM64_REG_X0, 0xfee1deadL); + assertEquals(0xfee1deadL, uc.reg_read(Unicorn.UC_ARM64_REG_X0)); - // initialize machine registers - u.reg_write(Unicorn.UC_X86_REG_ECX, 0x12345678); - u.reg_write(Unicorn.UC_X86_REG_EDX, 0x22334455); + uc.context_save(ctx); + assertEquals(0xfee1deadL, uc.reg_read(Unicorn.UC_ARM64_REG_X0)); - 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"); + uc.reg_write(Unicorn.UC_ARM64_REG_X0, 0xdeadbeefL); + assertEquals(0xdeadbeefL, uc.reg_read(Unicorn.UC_ARM64_REG_X0)); - u.emu_start(ADDRESS, ADDRESS + X86_CODE32_MEM_READ_WRITE.length, 0, 0); + uc.context_restore(ctx); + assertEquals(0xfee1deadL, uc.reg_read(Unicorn.UC_ARM64_REG_X0)); - 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(); - } - - @Test - public void testRemoveHook() { - byte[] X86_CODE = { 0x40, 0x40, 0x40, 0x40 }; // (inc eax) x 4 - int ADDRESS = 0x10000; - final int[] hook_accum = { 0 }; - - 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); - - CodeHook hook = - (uc, address, size, user) -> hook_accum[0] += (int) user; - long h1 = u.hook_add(hook, ADDRESS, ADDRESS, 1); - long h2 = u.hook_add(hook, ADDRESS + 1, ADDRESS + 1, 2); - long h3 = u.hook_add(hook, ADDRESS + 2, ADDRESS + 2, 4); - long h4 = u.hook_add(hook, ADDRESS + 3, ADDRESS + 3, 8); - - hook_accum[0] = 0; - u.emu_start(ADDRESS, ADDRESS + X86_CODE.length, 0, 0); - assertEquals(15, hook_accum[0]); - - u.hook_del(h2); - - hook_accum[0] = 0; - u.emu_start(ADDRESS, ADDRESS + X86_CODE.length, 0, 0); - assertEquals(13, hook_accum[0]); - - u.hook_del(hook); - - hook_accum[0] = 0; - u.emu_start(ADDRESS, ADDRESS + X86_CODE.length, 0, 0); - assertEquals(0, hook_accum[0]); - - assertThrows(UnicornException.class, () -> u.hook_del(h1)); - assertThrows(UnicornException.class, () -> u.hook_del(h3)); - assertThrows(UnicornException.class, () -> u.hook_del(h4)); - - u.close(); + uc.free(ctx); + uc.close(); } } diff --git a/bindings/java/tests/HookTests.java b/bindings/java/tests/HookTests.java new file mode 100644 index 00000000..7b0500fc --- /dev/null +++ b/bindings/java/tests/HookTests.java @@ -0,0 +1,130 @@ +package tests; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import org.junit.Test; + +import unicorn.CodeHook; +import unicorn.EdgeGeneratedHook; +import unicorn.TlbFillHook; +import unicorn.TranslationBlock; +import unicorn.Unicorn; +import unicorn.UnicornException; + +public class HookTests { + private static void assertTranslationBlock(TranslationBlock expected, + TranslationBlock actual) { + assertEquals(expected.pc, actual.pc); + assertEquals(expected.icount, actual.icount); + assertEquals(expected.size, actual.size); + } + + @Test + public void testEdgeHook() { + /* + 00000000 83FB01 cmp ebx,byte +0x1 + 00000003 7405 jz 0xa + 00000005 B802000000 mov eax,0x2 + 0000000A 40 inc eax + 0000000B EBFE jmp short 0xb + */ + final byte[] X86_CODE = + { -125, -5, 1, 116, 5, -72, 2, 0, 0, 0, 64, -21, -2 }; + final TranslationBlock[] expectedTb = { null, null }; + + 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); + expectedTb[1] = new TranslationBlock(ADDRESS, 2, 5); + u.hook_add((EdgeGeneratedHook) (uc, cur_tb, prev_tb, user) -> { + assertTranslationBlock(expectedTb[0], cur_tb); + assertTranslationBlock(expectedTb[1], prev_tb); + assertEquals("user data", user); + }, ADDRESS, ADDRESS + 10, "user data"); + + // TODO(nneonneo): why is icount 2/3 in the subsequent blocks? + expectedTb[0] = new TranslationBlock(ADDRESS + 10, 2, 1); + u.reg_write(Unicorn.UC_X86_REG_EBX, 1); + u.emu_start(ADDRESS, ADDRESS + 11, 0, 0); + + expectedTb[0] = new TranslationBlock(ADDRESS + 5, 3, 6); + u.reg_write(Unicorn.UC_X86_REG_EBX, 0); + u.emu_start(ADDRESS, ADDRESS + 11, 0, 0); + + assertTranslationBlock(new TranslationBlock(ADDRESS, 2, 5), + u.ctl_request_cache(ADDRESS)); + // TODO(nneonneo): I don't totally understand this output! Why 8 bytes at address 5? + assertTranslationBlock(new TranslationBlock(ADDRESS + 5, 3, 8), + u.ctl_request_cache(ADDRESS + 5)); + 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 testRemoveHook() { + byte[] X86_CODE = { 0x40, 0x40, 0x40, 0x40 }; // (inc eax) x 4 + int ADDRESS = 0x10000; + final int[] hook_accum = { 0 }; + + 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); + + CodeHook hook = + (uc, address, size, user) -> hook_accum[0] += (int) user; + long h1 = u.hook_add(hook, ADDRESS, ADDRESS, 1); + long h2 = u.hook_add(hook, ADDRESS + 1, ADDRESS + 1, 2); + long h3 = u.hook_add(hook, ADDRESS + 2, ADDRESS + 2, 4); + long h4 = u.hook_add(hook, ADDRESS + 3, ADDRESS + 3, 8); + + hook_accum[0] = 0; + u.emu_start(ADDRESS, ADDRESS + X86_CODE.length, 0, 0); + assertEquals(15, hook_accum[0]); + + u.hook_del(h2); + + hook_accum[0] = 0; + u.emu_start(ADDRESS, ADDRESS + X86_CODE.length, 0, 0); + assertEquals(13, hook_accum[0]); + + u.hook_del(hook); + + hook_accum[0] = 0; + u.emu_start(ADDRESS, ADDRESS + X86_CODE.length, 0, 0); + assertEquals(0, hook_accum[0]); + + assertThrows(UnicornException.class, () -> u.hook_del(h1)); + assertThrows(UnicornException.class, () -> u.hook_del(h3)); + assertThrows(UnicornException.class, () -> u.hook_del(h4)); + + u.close(); + } +} diff --git a/bindings/java/tests/MemTests.java b/bindings/java/tests/MemTests.java new file mode 100644 index 00000000..976f5d7f --- /dev/null +++ b/bindings/java/tests/MemTests.java @@ -0,0 +1,138 @@ +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.Unicorn; + +public class MemTests { + private static void assertMemRegion(long address, long size, + int perms, MemRegion actual) { + assertEquals(address, actual.begin); + assertEquals(address + size - 1, actual.end); + assertEquals(perms, actual.perms); + } + + @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); + assertMemRegion(ADDR1, 2 * 1024 * 1024, Unicorn.UC_PROT_ALL, arr[0]); + assertMemRegion(ADDR2, 4096, Unicorn.UC_PROT_READ, arr[1]); + uc.close(); + } + + @Test + public void testMemRegions2() { + Unicorn u = new Unicorn(Unicorn.UC_ARCH_TRICORE, 0); + u.mem_map(0x10000, 0x10000, Unicorn.UC_PROT_ALL); + u.mem_map(0x30000, 0x10000, Unicorn.UC_PROT_READ); + u.mem_map(0x50000, 0x10000, + Unicorn.UC_PROT_READ | Unicorn.UC_PROT_WRITE); + u.mem_map(0x70000, 0x20000, 0); + u.mem_protect(0x80000, 0x10000, Unicorn.UC_PROT_EXEC); + + ByteBuffer buf = ByteBuffer.allocateDirect(0x10000); + u.mem_map_ptr(0x110000, buf, Unicorn.UC_PROT_ALL); + + u.mmio_map(0x210000, 0x10000, + (uc, offset, size, user_data) -> 0x41414141, + null, (uc, offset, size, value, user_data) -> { + }, null); + u.mmio_map(0x230000, 0x10000, + (uc, offset, size, user_data) -> 0x41414141, + null, null, null); + u.mmio_map(0x250000, 0x10000, null, null, + (uc, offset, size, value, user_data) -> { + }, null); + u.mmio_map(0x270000, 0x10000, null, null, null, null); + + MemRegion[] mrs = u.mem_regions(); + assertEquals(10, mrs.length); + assertMemRegion(0x10000, 0x10000, Unicorn.UC_PROT_ALL, mrs[0]); + assertMemRegion(0x30000, 0x10000, Unicorn.UC_PROT_READ, mrs[1]); + assertMemRegion(0x50000, 0x10000, + Unicorn.UC_PROT_READ | Unicorn.UC_PROT_WRITE, mrs[2]); + assertMemRegion(0x70000, 0x10000, Unicorn.UC_PROT_NONE, mrs[3]); + assertMemRegion(0x80000, 0x10000, Unicorn.UC_PROT_EXEC, mrs[4]); + assertMemRegion(0x110000, 0x10000, Unicorn.UC_PROT_ALL, mrs[5]); + assertMemRegion(0x210000, 0x10000, + Unicorn.UC_PROT_READ | Unicorn.UC_PROT_WRITE, mrs[6]); + assertMemRegion(0x230000, 0x10000, Unicorn.UC_PROT_READ, mrs[7]); + assertMemRegion(0x250000, 0x10000, Unicorn.UC_PROT_WRITE, mrs[8]); + assertMemRegion(0x270000, 0x10000, Unicorn.UC_PROT_NONE, mrs[9]); + + u.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(); + } +} diff --git a/bindings/java/tests/RegTests.java b/bindings/java/tests/RegTests.java new file mode 100644 index 00000000..9eb653f3 --- /dev/null +++ b/bindings/java/tests/RegTests.java @@ -0,0 +1,50 @@ +package tests; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import unicorn.Unicorn; +import unicorn.X86_Float80; + +public class RegTests { + @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/unicorn/MemRegion.java b/bindings/java/unicorn/MemRegion.java index 20fa9200..033651f9 100644 --- a/bindings/java/unicorn/MemRegion.java +++ b/bindings/java/unicorn/MemRegion.java @@ -34,7 +34,7 @@ public class MemRegion { @Override public String toString() { - return "MemRegion [begin=" + begin + ", end=" + end + ", perms=" + - perms + "]"; + return String.format("MemRegion [begin=0x%x, end=0x%x, perms=%d]", + begin, end, perms); } } diff --git a/bindings/java/unicorn/TranslationBlock.java b/bindings/java/unicorn/TranslationBlock.java index 755534c1..721d192d 100644 --- a/bindings/java/unicorn/TranslationBlock.java +++ b/bindings/java/unicorn/TranslationBlock.java @@ -32,4 +32,10 @@ public class TranslationBlock { this.icount = icount; this.size = size; } + + @Override + public String toString() { + return String.format("TranslationBlock [pc=0x%x, icount=%d, size=%d]", + pc, icount, size); + } } diff --git a/bindings/java/unicorn/Unicorn.java b/bindings/java/unicorn/Unicorn.java index f2840cce..fb23320b 100644 --- a/bindings/java/unicorn/Unicorn.java +++ b/bindings/java/unicorn/Unicorn.java @@ -563,8 +563,11 @@ public class Unicorn * {@code errno} may not retain its old value once accessed. * * @return Error code, one of the {@code UC_ERR_*} constants. + * @deprecated Not actually useful in Java; error numbers are always + * converted into {@link UnicornException} exceptions. * @see UnicornConst */ + @Deprecated public int errno() { return _errno(nativePtr); }