Files
unicorn/bindings/java/samples/Sample_x86.java
Robert Xiao 4f563490e2 Update Java samples to match C samples.
Also add all of the samples as Java tests, referencing the output of the C
samples.
2023-06-17 14:19:10 -07:00

1061 lines
39 KiB
Java

/*
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
* <pre>
* 0x1000 xor dword ptr [edi+0x3], eax ; edi=0x1000, eax=0xbc4177e6
* 0x1003 dw 0x3ea98b13
* </pre>
*/
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();
}
}
}