/* Java bindings for the Unicorn Emulator Engine Copyright(c) 2015 Chris Eagle 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. */ /* Unicorn Emulator Engine */ /* By Nguyen Anh Quynh & Dang Hoang Vu, 2015 */ /* Sample code to demonstrate how to emulate X86 code */ package samples; import java.math.BigInteger; import java.nio.ByteBuffer; import unicorn.*; public class Sample_x86 implements UnicornConst, X86Const { /** code to be emulated * {@code INC ecx; DEC edx; PXOR xmm0, xmm1} */ private static final byte[] X86_CODE32 = Utils.hexToBytes("414a660fefc1"); /** code to be emulated * {@code jmp 4; nop; nop; nop; nop; nop; nop} */ private static final byte[] X86_CODE32_JUMP = Utils.hexToBytes("eb02909090909090"); // private static final byte[] X86_CODE32_SELF = Utils.hexToBytes("eb1c5a89d68b02663dca7d7506660503038902fec23d4141414175e9ffe6e8dfffffff31d26a0b589952682f2f7368682f62696e89e3525389e1ca7d41414141"); /** code to be emulated * {@code PUSH ecx; PUSH ecx; PUSH ecx; PUSH ecx} */ // private static final byte[] X86_CODE32 = Utils.hexToBytes("51515151"); /** code to be emulated * {@code INC ecx; DEC edx; self_loop: JMP self_loop} */ private static final byte[] X86_CODE32_LOOP = Utils.hexToBytes("414aebfe"); /** code to be emulated * {@code mov [0xaaaaaaaa], ecx; INC ecx; DEC edx} */ private static final byte[] X86_CODE32_MEM_WRITE = Utils.hexToBytes("890DAAAAAAAA414a"); /** code to be emulated * {@code mov ecx, [0xaaaaaaaa]; INC ecx; DEC edx} */ private static final byte[] X86_CODE32_MEM_READ = Utils.hexToBytes("8B0DAAAAAAAA414a"); /** code to be emulated * {@code inc eax; mov ebx, [0x100000]; inc edx} */ private static final byte[] X86_CODE32_MEM_READ_IN_TB = Utils.hexToBytes("408b1d0000100042"); /** code to be emulated * {@code JMP outside; INC ecx; DEC edx} */ private static final byte[] X86_CODE32_JMP_INVALID = Utils.hexToBytes("e9e9eeeeee414a"); /** code to be emulated * {@code INC ecx; IN AL, 0x3f; DEC edx; OUT 0x46, AL; INC ebx} */ private static final byte[] X86_CODE32_INOUT = Utils.hexToBytes("41E43F4aE64643"); /** code to be emulated * {@code INC eax} */ private static final byte[] X86_CODE32_INC = Utils.hexToBytes("40"); //private static final byte[] X86_CODE64 = Utils.hexToBytes("41BC3BB0282A490FC9904D0FADCF4987FD904881D28ACE773548F7D9"); // <== still crash /** code to be emulated */ private static final byte[] X86_CODE64 = Utils.hexToBytes("41BC3BB0282A490FC9904D0FADCF4987FD90" + "4881D28ACE773548F7D94D29F44981C9F68A" + "C6534D87ED480FADD249F7D448F7E14D19C5" + "4D89C548F7D641B84F8D6B594D87D0686A1E" + "093C59"); /** code to be emulated * {@code add byte ptr [bx + si], al} */ private static final byte[] X86_CODE16 = Utils.hexToBytes("0000"); /** code to be emulated * {@code syscall} */ private static final byte[] X86_CODE64_SYSCALL = Utils.hexToBytes("0f05"); /** code to be emulated * {@code mov [0x20004], ecx; mov ecx, [0x20004]} */ private static final byte[] X86_MMIO_CODE = Utils.hexToBytes("890d040002008b0d04000200"); /** code to be emulated *
     * 0x1000 xor dword ptr [edi+0x3], eax ; edi=0x1000, eax=0xbc4177e6
     * 0x1003 dw 0x3ea98b13
     * 
*/ private static final byte[] X86_CODE32_SMC = Utils.hexToBytes("314703138ba93e"); /** memory address where emulation starts */ public static final int ADDRESS = 0x1000000; private static final BlockHook hook_block = (uc, address, size, user_data) -> { System.out.format( ">>> Tracing basic block at 0x%x, block size = 0x%x\n", address, size); }; private static final CodeHook hook_code = (uc, address, size, user_data) -> { System.out.format( ">>> Tracing instruction at 0x%x, instruction size = 0x%x\n", address, size); long eflags = uc.reg_read(UC_X86_REG_EFLAGS); System.out.format(">>> --- EFLAGS is 0x%x\n", eflags); // Uncomment below code to stop the emulation using uc_emu_stop() // if (address == 0x1000009) // uc.emu_stop(); }; private static final CodeHook hook_code64 = (uc, address, size, user_data) -> { long rip = uc.reg_read(UC_X86_REG_RIP); System.out.format( ">>> Tracing instruction at 0x%x, instruction size = 0x%x\n", address, size); System.out.format(">>> RIP is 0x%x\n", rip); }; private static final EventMemHook hook_mem_invalid = (uc, type, address, size, value, user) -> { switch (type) { default: // return false to indicate we want to stop emulation return false; case UC_MEM_WRITE_UNMAPPED: System.out.printf( ">>> Missing memory is being WRITE at 0x%x, data size = %d, data value = 0x%x\n", address, size, value); // map this memory in with 2MB in size uc.mem_map(0xaaaa0000L, 2 * 1024 * 1024, UC_PROT_ALL); // return true to indicate we want to continue return true; } }; private static final MemHook hook_mem64 = (uc, type, address, size, value, user_data) -> { switch (type) { default: break; case UC_MEM_READ: System.out.format( ">>> Memory is being READ at 0x%x, data size = %d\n", address, size); break; case UC_MEM_WRITE: System.out.format( ">>> Memory is being WRITE at 0x%x, data size = %d, data value = 0x%x\n", address, size, value); break; } }; // callback for IN instruction (X86). // this returns the data read from the port private static final InHook hook_in = (uc, port, size, user) -> { long r_eip = uc.reg_read(UC_X86_REG_EIP); System.out.printf( "--- reading from port 0x%x, size: %d, address: 0x%x\n", port, size, r_eip); switch (size) { case 1: // read 1 byte to AL return 0xf1; case 2: // read 2 byte to AX return 0xf2; case 4: // read 4 byte to EAX return 0xf4; } return 0; }; // callback for OUT instruction (X86). private static final OutHook hook_out = (uc, port, size, value, user) -> { long eip = uc.reg_read(UC_X86_REG_EIP); long tmp = 0; System.out.printf( "--- writing to port 0x%x, size: %d, value: 0x%x, address: 0x%x\n", port, size, value, eip); // confirm that value is indeed the value of AL/AX/EAX switch (size) { default: return; // should never reach this case 1: tmp = uc.reg_read(UC_X86_REG_AL); break; case 2: tmp = uc.reg_read(UC_X86_REG_AX); break; case 4: tmp = uc.reg_read(UC_X86_REG_EAX); break; } System.out.printf("--- register value = 0x%x\n", tmp); }; // callback for SYSCALL instruction (X86). private static final SyscallHook hook_syscall = (uc, user_data) -> { long rax = uc.reg_read(UC_X86_REG_RAX); if (rax == 0x100) { rax = 0x200; uc.reg_write(UC_X86_REG_RAX, rax); } else { System.out.format("ERROR: was not expecting rax=0x%x in syscall\n", rax); } }; private static final EventMemHook hook_memalloc = (uc, type, address, size, value, user_data) -> { long aligned_address = address & ~(0xFFFL); int aligned_size = ((int) (size / 0x1000) + 1) * 0x1000; System.out.format( ">>> Allocating block at 0x%x (0x%x), block size = 0x%x (0x%x)\n", address, aligned_address, size, aligned_size); uc.mem_map(aligned_address, aligned_size, UC_PROT_ALL); // write machine code to be emulated to memory uc.mem_write(aligned_address, X86_CODE32); // this recovers from missing memory, so we return true return true; }; public static void test_miss_code() { int r_ecx = 0x1234; // ECX register int r_edx = 0x7890; // EDX register System.out.println("Emulate i386 code - missing code"); // Initialize emulator in X86-32bit mode Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_32); // initialize machine registers uc.reg_write(UC_X86_REG_ECX, r_ecx); uc.reg_write(UC_X86_REG_EDX, r_edx); // tracing all instruction by having @begin > @end uc.hook_add(hook_code, 1, 0, null); // auto-allocate memory on access uc.hook_add(hook_memalloc, UC_HOOK_MEM_UNMAPPED, 1, 0, null); // emulate machine code, without having the code in yet uc.emu_start(ADDRESS, ADDRESS + X86_CODE32.length, 0, 0); // now print out some registers System.out.println(">>> Emulation done. Below is the CPU context"); System.out.format(">>> ECX = 0x%x\n", uc.reg_read(UC_X86_REG_ECX)); System.out.format(">>> EDX = 0x%x\n", uc.reg_read(UC_X86_REG_EDX)); } public static void test_i386() { int tmp; long r_ecx = 0x1234; // ECX register long r_edx = 0x7890; // EDX register // XMM0 and XMM1 registers, low qword then high qword BigInteger r_xmm0 = new BigInteger("000102030405060708090a0b0c0d0e0f", 16); BigInteger r_xmm1 = new BigInteger("00102030405060708090a0b0c0d0e0f0", 16); System.out.println("Emulate i386 code"); // Initialize emulator in X86-32bit mode Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_32); // map 2MB memory for this emulation uc.mem_map(ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL); // write machine code to be emulated to memory uc.mem_write(ADDRESS, X86_CODE32); // initialize machine registers uc.reg_write(UC_X86_REG_ECX, r_ecx); uc.reg_write(UC_X86_REG_EDX, r_edx); uc.reg_write(UC_X86_REG_XMM0, r_xmm0); uc.reg_write(UC_X86_REG_XMM1, r_xmm1); // tracing all basic blocks with customized callback uc.hook_add(hook_block, 1, 0, null); // tracing all instruction by having @begin > @end uc.hook_add(hook_code, 1, 0, null); // emulate machine code in infinite time uc.emu_start(ADDRESS, ADDRESS + X86_CODE32.length, 0, 0); // now print out some registers System.out.println(">>> Emulation done. Below is the CPU context"); r_ecx = uc.reg_read(UC_X86_REG_ECX); r_edx = uc.reg_read(UC_X86_REG_EDX); r_xmm0 = (BigInteger) uc.reg_read(UC_X86_REG_XMM0, null); System.out.format(">>> ECX = 0x%x\n", r_ecx); System.out.format(">>> EDX = 0x%x\n", r_edx); String xmm0_string = String.format("%32s", r_xmm0.toString(16)).replace(' ', '0'); System.out.format(">>> XMM0 = 0x%s\n", xmm0_string); // read from memory tmp = Utils.toInt(uc.mem_read(ADDRESS, 4)); System.out.format(">>> Read 4 bytes from [0x%x] = 0x%x\n", ADDRESS, tmp); } public static void test_i386_map_ptr() { int tmp; int r_ecx = 0x1234; // ECX register int r_edx = 0x7890; // EDX register System.out.println("Emulate i386 code - use uc_mem_map_ptr()"); // Initialize emulator in X86-32bit mode Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_32); // malloc 2MB memory for this emulation ByteBuffer mem = ByteBuffer.allocateDirect(2 * 1024 * 1024); uc.mem_map_ptr(ADDRESS, mem, UC_PROT_ALL); mem.put(X86_CODE32); // initialize machine registers uc.reg_write(UC_X86_REG_ECX, r_ecx); uc.reg_write(UC_X86_REG_EDX, r_edx); // tracing all basic blocks with customized callback uc.hook_add(hook_block, 1, 0, null); // tracing all instruction by having @begin > @end uc.hook_add(hook_code, 1, 0, null); // emulate machine code in infinite time uc.emu_start(ADDRESS, ADDRESS + X86_CODE32.length, 0, 0); // now print out some registers System.out.println(">>> Emulation done. Below is the CPU context"); System.out.format(">>> ECX = 0x%x\n", uc.reg_read(UC_X86_REG_ECX)); System.out.format(">>> EDX = 0x%x\n", uc.reg_read(UC_X86_REG_EDX)); // read from memory tmp = Utils.toInt(uc.mem_read(ADDRESS, 4)); System.out.format(">>> Read 4 bytes from [0x%x] = 0x%x\n", ADDRESS, tmp); } public static void test_i386_jump() { System.out.println("Emulate i386 code with jump"); // Initialize emulator in X86-32bit mode Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_32); // map 2MB memory for this emulation uc.mem_map(ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL); // write machine code to be emulated to memory uc.mem_write(ADDRESS, X86_CODE32_JUMP); // tracing 1 basic block with customized callback uc.hook_add(hook_block, ADDRESS, ADDRESS, null); // tracing 1 instruction at ADDRESS uc.hook_add(hook_code, ADDRESS, ADDRESS, null); // emulate machine code in infinite time uc.emu_start(ADDRESS, ADDRESS + X86_CODE32_JUMP.length, 0, 0); System.out.println(">>> Emulation done. Below is the CPU context"); } // emulate code that loop forever public static void test_i386_loop() { int r_ecx = 0x1234; // ECX register int r_edx = 0x7890; // EDX register System.out.println("Emulate i386 code that loop forever"); // Initialize emulator in X86-32bit mode Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_32); // map 2MB memory for this emulation uc.mem_map(ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL); // write machine code to be emulated to memory uc.mem_write(ADDRESS, X86_CODE32_LOOP); // initialize machine registers uc.reg_write(UC_X86_REG_ECX, r_ecx); uc.reg_write(UC_X86_REG_EDX, r_edx); // emulate machine code in 2 seconds, so we can quit even // if the code loops uc.emu_start(ADDRESS, ADDRESS + X86_CODE32_LOOP.length, 2 * UC_SECOND_SCALE, 0); // now print out some registers System.out.println(">>> Emulation done. Below is the CPU context"); System.out.format(">>> ECX = 0x%x\n", uc.reg_read(UC_X86_REG_ECX)); System.out.format(">>> EDX = 0x%x\n", uc.reg_read(UC_X86_REG_EDX)); } // emulate code that read invalid memory public static void test_i386_invalid_mem_read() { int r_ecx = 0x1234; // ECX register int r_edx = 0x7890; // EDX register System.out.println("Emulate i386 code that read from invalid memory"); // Initialize emulator in X86-32bit mode Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_32); // map 2MB memory for this emulation uc.mem_map(ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL); // write machine code to be emulated to memory uc.mem_write(ADDRESS, X86_CODE32_MEM_READ); // initialize machine registers uc.reg_write(UC_X86_REG_ECX, r_ecx); uc.reg_write(UC_X86_REG_EDX, r_edx); // tracing all basic blocks with customized callback uc.hook_add(hook_block, 1, 0, null); // tracing all instruction by having @begin > @end uc.hook_add(hook_code, 1, 0, null); // emulate machine code in infinite time try { uc.emu_start(ADDRESS, ADDRESS + X86_CODE32_MEM_READ.length, 0, 0); throw new RuntimeException("Expected a crash!"); } catch (UnicornException e) { System.out.println("uc.emu_start failed as expected: " + e); } // now print out some registers System.out.println(">>> Emulation done. Below is the CPU context"); System.out.format(">>> ECX = 0x%x\n", uc.reg_read(UC_X86_REG_ECX)); System.out.format(">>> EDX = 0x%x\n", uc.reg_read(UC_X86_REG_EDX)); } // emulate code that write invalid memory public static void test_i386_invalid_mem_write() { int r_ecx = 0x1234; // ECX register int r_edx = 0x7890; // EDX register int tmp; System.out.println("Emulate i386 code that write to invalid memory"); // Initialize emulator in X86-32bit mode Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_32); // map 2MB memory for this emulation uc.mem_map(ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL); // write machine code to be emulated to memory uc.mem_write(ADDRESS, X86_CODE32_MEM_WRITE); // initialize machine registers uc.reg_write(UC_X86_REG_ECX, r_ecx); uc.reg_write(UC_X86_REG_EDX, r_edx); // tracing all basic blocks with customized callback uc.hook_add(hook_block, 1, 0, null); // tracing all instruction by having @begin > @end uc.hook_add(hook_code, 1, 0, null); // intercept invalid memory events uc.hook_add(hook_mem_invalid, UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, 1, 0, null); // emulate machine code in infinite time uc.emu_start(ADDRESS, ADDRESS + X86_CODE32_MEM_WRITE.length, 0, 0); // now print out some registers System.out.println(">>> Emulation done. Below is the CPU context"); System.out.format(">>> ECX = 0x%x\n", uc.reg_read(UC_X86_REG_ECX)); System.out.format(">>> EDX = 0x%x\n", uc.reg_read(UC_X86_REG_EDX)); // read from memory tmp = Utils.toInt(uc.mem_read(0xaaaaaaaaL, 4)); System.out.format(">>> Read 4 bytes from [0x%x] = 0x%x\n", 0xaaaaaaaa, tmp); try { tmp = Utils.toInt(uc.mem_read(0xffffffaaL, 4)); throw new RuntimeException("Expected mem_read to fail"); } catch (UnicornException e) { System.out.format(">>> Failed to read 4 bytes from [0x%x]\n", 0xffffffaa); } } // emulate code that jump to invalid memory public static void test_i386_jump_invalid() { int r_ecx = 0x1234; // ECX register int r_edx = 0x7890; // EDX register System.out.println("Emulate i386 code that jumps to invalid memory"); // Initialize emulator in X86-32bit mode Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_32); // map 2MB memory for this emulation uc.mem_map(ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL); // write machine code to be emulated to memory uc.mem_write(ADDRESS, X86_CODE32_JMP_INVALID); // initialize machine registers uc.reg_write(UC_X86_REG_ECX, r_ecx); uc.reg_write(UC_X86_REG_EDX, r_edx); // tracing all basic blocks with customized callback uc.hook_add(hook_block, 1, 0, null); // tracing all instructions by having @begin > @end uc.hook_add(hook_code, 1, 0, null); // emulate machine code in infinite time try { uc.emu_start(ADDRESS, ADDRESS + X86_CODE32_JMP_INVALID.length, 0, 0); throw new RuntimeException("Expected a crash!"); } catch (UnicornException e) { System.out.println("uc.emu_start failed as expected: " + e); } // now print out some registers System.out.println(">>> Emulation done. Below is the CPU context"); System.out.format(">>> ECX = 0x%x\n", uc.reg_read(UC_X86_REG_ECX)); System.out.format(">>> EDX = 0x%x\n", uc.reg_read(UC_X86_REG_EDX)); } public static void test_i386_inout() { int r_eax = 0x1234; // EAX register int r_ecx = 0x6789; // ECX register System.out.println("Emulate i386 code with IN/OUT instructions"); // Initialize emulator in X86-32bit mode Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_32); // map 2MB memory for this emulation uc.mem_map(ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL); // write machine code to be emulated to memory uc.mem_write(ADDRESS, X86_CODE32_INOUT); // initialize machine registers uc.reg_write(UC_X86_REG_EAX, r_eax); uc.reg_write(UC_X86_REG_ECX, r_ecx); // tracing all basic blocks with customized callback uc.hook_add(hook_block, 1, 0, null); // tracing all instructions uc.hook_add(hook_code, 1, 0, null); // uc IN instruction uc.hook_add(hook_in, null); // uc OUT instruction uc.hook_add(hook_out, null); // emulate machine code in infinite time uc.emu_start(ADDRESS, ADDRESS + X86_CODE32_INOUT.length, 0, 0); // now print out some registers System.out.println(">>> Emulation done. Below is the CPU context"); System.out.format(">>> EAX = 0x%x\n", uc.reg_read(UC_X86_REG_EAX)); System.out.format(">>> ECX = 0x%x\n", uc.reg_read(UC_X86_REG_ECX)); } // emulate code and save/restore the CPU context public static void test_i386_context_save() { int r_eax = 0x1; // EAX register System.out.println("Save/restore CPU context in opaque blob"); // initialize emulator in X86-32bit mode Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_32); // map 8KB memory for this emulation uc.mem_map(ADDRESS, 8 * 1024, UC_PROT_ALL); // write machine code to be emulated to memory uc.mem_write(ADDRESS, X86_CODE32_INC); // initialize machine registers uc.reg_write(UC_X86_REG_EAX, r_eax); // emulate machine code in infinite time System.out.println(">>> Running emulation for the first time"); uc.emu_start(ADDRESS, ADDRESS + X86_CODE32_INC.length, 0, 0); // now print out some registers System.out.println(">>> Emulation done. Below is the CPU context"); System.out.format(">>> EAX = 0x%x\n", uc.reg_read(UC_X86_REG_EAX)); // allocate and save the CPU context System.out.println(">>> Saving CPU context"); Unicorn.Context context = uc.context_save(); // emulate machine code again System.out.println(">>> Running emulation for the second time"); uc.emu_start(ADDRESS, ADDRESS + X86_CODE32_INC.length, 0, 0); // now print out some registers System.out.println(">>> Emulation done. Below is the CPU context"); System.out.format(">>> EAX = 0x%x\n", uc.reg_read(UC_X86_REG_EAX)); // restore CPU context uc.context_restore(context); // now print out some registers System.out .println(">>> CPU context restored. Below is the CPU context"); System.out.format(">>> EAX = 0x%x\n", uc.reg_read(UC_X86_REG_EAX)); // modify some registers of the context context.reg_write(UC_X86_REG_EAX, 0xc8); // and restore CPU context again uc.context_restore(context); // now print out some registers System.out.format( ">>> CPU context restored with modification. Below is the CPU context\n"); System.out.format(">>> EAX = 0x%x\n", uc.reg_read(UC_X86_REG_EAX)); } public static void test_x86_64() { long rax = 0x71f3029efd49d41dL; long rbx = 0xd87b45277f133ddbL; long rcx = 0xab40d1ffd8afc461L; long rdx = 0x919317b4a733f01L; long rsi = 0x4c24e753a17ea358L; long rdi = 0xe509a57d2571ce96L; long r8 = 0xea5b108cc2b9ab1fL; long r9 = 0x19ec097c8eb618c1L; long r10 = 0xec45774f00c5f682L; long r11 = 0xe17e9dbec8c074aaL; long r12 = 0x80f86a8dc0f6d457L; long r13 = 0x48288ca5671c5492L; long r14 = 0x595f72f6e4017f6eL; long r15 = 0x1efd97aea331ccccL; long rsp = ADDRESS + 0x200000L; System.out.println("Emulate x86_64 code"); // Initialize emulator in X86-64bit mode Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_64); // map 2MB memory for this emulation uc.mem_map(ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL); // write machine code to be emulated to memory uc.mem_write(ADDRESS, X86_CODE64); // initialize machine registers uc.reg_write(UC_X86_REG_RSP, rsp); uc.reg_write(UC_X86_REG_RAX, rax); uc.reg_write(UC_X86_REG_RBX, rbx); uc.reg_write(UC_X86_REG_RCX, rcx); uc.reg_write(UC_X86_REG_RDX, rdx); uc.reg_write(UC_X86_REG_RSI, rsi); uc.reg_write(UC_X86_REG_RDI, rdi); uc.reg_write(UC_X86_REG_R8, r8); uc.reg_write(UC_X86_REG_R9, r9); uc.reg_write(UC_X86_REG_R10, r10); uc.reg_write(UC_X86_REG_R11, r11); uc.reg_write(UC_X86_REG_R12, r12); uc.reg_write(UC_X86_REG_R13, r13); uc.reg_write(UC_X86_REG_R14, r14); uc.reg_write(UC_X86_REG_R15, r15); // tracing all basic blocks with customized callback uc.hook_add(hook_block, 1, 0, null); // tracing all instructions in the range [ADDRESS, ADDRESS+20] uc.hook_add(hook_code64, ADDRESS, ADDRESS + 20, null); // tracing all memory WRITE access (with @begin > @end) uc.hook_add(hook_mem64, UC_HOOK_MEM_WRITE, 1, 0, null); // tracing all memory READ access (with @begin > @end) uc.hook_add(hook_mem64, UC_HOOK_MEM_READ, 1, 0, null); // emulate machine code in infinite time (last param = 0), or when // finishing all the code. uc.emu_start(ADDRESS, ADDRESS + X86_CODE64.length, 0, 0); // now print out some registers System.out.println(">>> Emulation done. Below is the CPU context"); System.out.format(">>> RAX = 0x%x\n", uc.reg_read(UC_X86_REG_RAX)); System.out.format(">>> RBX = 0x%x\n", uc.reg_read(UC_X86_REG_RBX)); System.out.format(">>> RCX = 0x%x\n", uc.reg_read(UC_X86_REG_RCX)); System.out.format(">>> RDX = 0x%x\n", uc.reg_read(UC_X86_REG_RDX)); System.out.format(">>> RSI = 0x%x\n", uc.reg_read(UC_X86_REG_RSI)); System.out.format(">>> RDI = 0x%x\n", uc.reg_read(UC_X86_REG_RDI)); System.out.format(">>> R8 = 0x%x\n", uc.reg_read(UC_X86_REG_R8)); System.out.format(">>> R9 = 0x%x\n", uc.reg_read(UC_X86_REG_R9)); System.out.format(">>> R10 = 0x%x\n", uc.reg_read(UC_X86_REG_R10)); System.out.format(">>> R11 = 0x%x\n", uc.reg_read(UC_X86_REG_R11)); System.out.format(">>> R12 = 0x%x\n", uc.reg_read(UC_X86_REG_R12)); System.out.format(">>> R13 = 0x%x\n", uc.reg_read(UC_X86_REG_R13)); System.out.format(">>> R14 = 0x%x\n", uc.reg_read(UC_X86_REG_R14)); System.out.format(">>> R15 = 0x%x\n", uc.reg_read(UC_X86_REG_R15)); } public static void test_x86_64_syscall() { long rax = 0x100; System.out.println("Emulate x86_64 code with 'syscall' instruction"); // Initialize emulator in X86-64bit mode Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_64); // map 2MB memory for this emulation uc.mem_map(ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL); // write machine code to be emulated to memory uc.mem_write(ADDRESS, X86_CODE64_SYSCALL); // hook interrupts for syscall uc.hook_add(hook_syscall, UC_X86_INS_SYSCALL, 1, 0, null); // initialize machine registers uc.reg_write(UC_X86_REG_RAX, rax); // emulate machine code in infinite time (last param = 0), or when // finishing all the code. uc.emu_start(ADDRESS, ADDRESS + X86_CODE64_SYSCALL.length, 0, 0); // now print out some registers System.out.println(">>> Emulation done. Below is the CPU context"); System.out.format(">>> RAX = 0x%x\n", uc.reg_read(UC_X86_REG_RAX)); } public static void test_x86_16() { int eax = 7; int ebx = 5; int esi = 6; System.out.println("Emulate x86 16-bit code"); // Initialize emulator in X86-16bit mode Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_16); // map 8KB memory for this emulation uc.mem_map(0, 8 * 1024, UC_PROT_ALL); // write machine code to be emulated to memory uc.mem_write(0, X86_CODE16); // initialize machine registers uc.reg_write(UC_X86_REG_EAX, eax); uc.reg_write(UC_X86_REG_EBX, ebx); uc.reg_write(UC_X86_REG_ESI, esi); // emulate machine code in infinite time (last param = 0), or when // finishing all the code. uc.emu_start(0, X86_CODE16.length, 0, 0); // now print out some registers System.out.println(">>> Emulation done. Below is the CPU context"); // read from memory byte[] result = uc.mem_read(11, 1); System.out.format(">>> Read 1 bytes from [0x%x] = 0x%x\n", 11, result[0] & 0xff); } public static void test_i386_invalid_mem_read_in_tb() { int r_eax = 0x1234; // EAX register int r_edx = 0x7890; // EDX register System.out.format( "Emulate i386 code that read invalid memory in the middle of a TB\n"); // Initialize emulator in X86-32bit mode Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_32); // map 2MB memory for this emulation uc.mem_map(ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL); // write machine code to be emulated to memory uc.mem_write(ADDRESS, X86_CODE32_MEM_READ_IN_TB); // initialize machine registers uc.reg_write(UC_X86_REG_EAX, r_eax); uc.reg_write(UC_X86_REG_EDX, r_edx); // Add a dummy callback. // Note: if this callback is not added, the EIP will not be updated, // and EIP will read as ADDRESS after emu_start fails. uc.hook_add((MemHook) (u, type, address, size, value, user) -> { }, UC_HOOK_MEM_READ, 1, 0, null); // Let it crash by design. try { uc.emu_start(ADDRESS, ADDRESS + X86_CODE32_MEM_READ_IN_TB.length, 0, 0); throw new RuntimeException("Expected uc.emu_start to fail"); } catch (UnicornException e) { System.out.println( "uc.emu_start() failed BY DESIGN with error returned: " + e); } System.out.println(">>> Emulation done. Below is the CPU context"); long r_eip = uc.reg_read(UC_X86_REG_EIP); System.out.format(">>> EIP = 0x%x\n", r_eip); if (r_eip != ADDRESS + 1) { System.out.format( ">>> ERROR: Wrong PC 0x%x when reading unmapped memory in the middle of TB!\n", r_eip); } else { System.out.format( ">>> The PC is correct after reading unmapped memory in the middle of TB.\n"); } } public static void test_i386_smc_xor() { long r_edi = ADDRESS; // ECX register long r_eax = 0xbc4177e6L; // EDX register System.out.println("Emulate i386 code that modfies itself"); // Initialize emulator in X86-32bit mode Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_32); // map 1KB memory for this emulation uc.mem_map(ADDRESS, 0x1000, UC_PROT_ALL); // write machine code to be emulated to memory uc.mem_write(ADDRESS, X86_CODE32_SMC); // initialize machine registers uc.reg_write(UC_X86_REG_EDI, r_edi); uc.reg_write(UC_X86_REG_EAX, r_eax); // **Important Note** // // Since SMC code will cause TB regeneration, the XOR in fact would executed // twice (the first execution won't take effect.). Thus, if you would like // to use count to control the emulation, the count should be set to 2. // // uc.emu_start(ADDRESS, ADDRESS + 3, 0, 0); uc.emu_start(ADDRESS, 0, 0, 2); System.out.println(">>> Emulation done. Below is the result."); int result = Utils.toInt(uc.mem_read(ADDRESS + 3, 4)); if (result == (0x3ea98b13 ^ 0xbc4177e6)) { System.out.format( ">>> SMC emulation is correct. 0x3ea98b13 ^ 0xbc4177e6 = 0x%x\n", result); } else { System.out.format( ">>> SMC emulation is wrong. 0x3ea98b13 ^ 0xbc4177e6 = 0x%x\n", result); } } private static final MmioReadHandler mmio_read_callback = (uc, offset, size, user_data) -> { System.out.format( ">>> Read IO memory at offset 0x%d with 0x%d bytes and return 0x19260817\n", offset, size); // The value returned here would be written to ecx. return 0x19260817; }; private static final MmioWriteHandler mmio_write_callback = (uc, offset, size, value, user_data) -> { System.out.format( ">>> Write value 0x%d to IO memory at offset 0x%d with 0x%d bytes\n", value, offset, size); }; public static void test_i386_mmio() { long r_ecx = 0xdeadbeefL; System.out.println("Emulate i386 code that uses MMIO"); // Initialize emulator in X86-32bit mode Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_32); // map 1KB memory for this emulation uc.mem_map(ADDRESS, 0x1000, UC_PROT_ALL); // write machine code to be emulated to memory uc.mem_write(ADDRESS, X86_MMIO_CODE); uc.mmio_map(0x20000, 0x4000, mmio_read_callback, null, mmio_write_callback, null); // prepare ecx uc.reg_write(UC_X86_REG_ECX, r_ecx); uc.emu_start(ADDRESS, ADDRESS + X86_MMIO_CODE.length, 0, 0); System.out.format(">>> Emulation done. ECX=0x%x\n", uc.reg_read(UC_X86_REG_ECX)); } private static final EventMemHook test_i386_hook_mem_invalid_cb = (uc, type, address, size, value, user_data) -> { if (type == UC_MEM_READ_UNMAPPED || type == UC_MEM_WRITE_UNMAPPED) { System.out.format( ">>> We have to add a map at 0x%x before continue execution!\n", address); uc.mem_map(address, 0x1000, UC_PROT_ALL); } // If you really would like to continue the execution, make sure the memory // is already mapped properly! return true; }; public static void test_i386_hook_mem_invalid() { // mov eax, 0xdeadbeef; // mov [0x8000], eax; // mov eax, [0x10000]; byte[] code = Utils.hexToBytes("b8efbeaddea300800000a100000100"); System.out.println( "Emulate i386 code that triggers invalid memory read/write."); Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_32); uc.mem_map(ADDRESS, 0x1000, UC_PROT_ALL); uc.mem_write(ADDRESS, code); long hook = uc.hook_add(test_i386_hook_mem_invalid_cb, UC_HOOK_MEM_INVALID, 1, 0, null); uc.emu_start(ADDRESS, ADDRESS + code.length, 0, 0); uc.hook_del(hook); } public static void main(String args[]) { if (args.length == 1) { if (args[0].equals("-16")) { test_x86_16(); } else if (args[0].equals("-32")) { test_miss_code(); System.out.println("==================================="); test_i386(); System.out.println("==================================="); test_i386_map_ptr(); System.out.println("==================================="); test_i386_inout(); System.out.println("==================================="); test_i386_context_save(); System.out.println("==================================="); test_i386_jump(); System.out.println("==================================="); test_i386_loop(); System.out.println("==================================="); test_i386_invalid_mem_read(); System.out.println("==================================="); test_i386_invalid_mem_write(); System.out.println("==================================="); test_i386_jump_invalid(); // test_i386_invalid_c6c7(); } else if (args[0].equals("-64")) { test_x86_64(); System.out.println("==================================="); test_x86_64_syscall(); } else if (args[0].equals("-h")) { System.out.println( "Syntax: java samples.Sample_x86 <-16|-32|-64>"); } } else { test_x86_16(); System.out.println("==================================="); test_miss_code(); System.out.println("==================================="); test_i386(); System.out.println("==================================="); test_i386_map_ptr(); System.out.println("==================================="); test_i386_inout(); System.out.println("==================================="); test_i386_context_save(); System.out.println("==================================="); test_i386_jump(); System.out.println("==================================="); test_i386_loop(); System.out.println("==================================="); test_i386_invalid_mem_read(); System.out.println("==================================="); test_i386_invalid_mem_write(); System.out.println("==================================="); test_i386_jump_invalid(); // test_i386_invalid_c6c7(); System.out.println("==================================="); test_x86_64(); System.out.println("==================================="); test_x86_64_syscall(); System.out.println("==================================="); test_i386_invalid_mem_read_in_tb(); System.out.println("==================================="); test_i386_smc_xor(); System.out.println("==================================="); test_i386_mmio(); System.out.println("==================================="); test_i386_hook_mem_invalid(); } } }