Refactor tests and add a few more
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
130
bindings/java/tests/HookTests.java
Normal file
130
bindings/java/tests/HookTests.java
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
138
bindings/java/tests/MemTests.java
Normal file
138
bindings/java/tests/MemTests.java
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
50
bindings/java/tests/RegTests.java
Normal file
50
bindings/java/tests/RegTests.java
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user