Files
unicorn/bindings/java/samples/Sample_mmu.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

225 lines
7.7 KiB
Java

package samples;
import unicorn.*;
public class Sample_mmu implements UnicornConst, X86Const {
/** Code:
* <pre>
* mov rax, 57
* syscall
* test rax, rax
* jz child
* xor rax, rax
* mov rax, 60
* mov [0x4000], rax
* syscall
*
* child:
* xor rcx, rcx
* mov rcx, 42
* mov [0x4000], rcx
* mov rax, 60
* syscall
* </pre>
*/
private static final byte[] CODE = Utils.hexToBytes(
"B8390000000F054885C0740FB83C00000048890425004000000F05B92A00000048890C2500400000B83C0000000F05");
private static final MemHook mmu_write_callback =
(uc, type, address, size, value, user_data) -> {
System.out.format("write at 0x%x: 0x%x\n", address, value);
};
private static void x86_mmu_prepare_tlb(Unicorn uc, long vaddr,
long tlb_base) {
long cr0;
long cr4;
X86_MSR msr = new X86_MSR(0xC0000080);
long pml4o = ((vaddr & 0x00ff8000000000L) >> 39) * 8;
long pdpo = ((vaddr & 0x00007fc0000000L) >> 30) * 8;
long pdo = ((vaddr & 0x0000003fe00000L) >> 21) * 8;
long pml4e = (tlb_base + 0x1000L) | 1 | (1 << 2);
long pdpe = (tlb_base + 0x2000L) | 1 | (1 << 2);
long pde = (tlb_base + 0x3000L) | 1 | (1 << 2);
uc.mem_write(tlb_base + pml4o, Utils.toBytes(pml4e));
uc.mem_write(tlb_base + 0x1000 + pdpo, Utils.toBytes(pdpe));
uc.mem_write(tlb_base + 0x2000 + pdo, Utils.toBytes(pde));
uc.reg_write(UC_X86_REG_CR3, tlb_base);
cr0 = uc.reg_read(UC_X86_REG_CR0);
cr4 = uc.reg_read(UC_X86_REG_CR4);
msr.value = (Long) uc.reg_read(UC_X86_REG_MSR, msr);
cr0 |= 1; //enable protected mode
cr0 |= 1l << 31; //enable paging
cr4 |= 1l << 5; //enable physical address extension
msr.value |= 1l << 8; //enable long mode
uc.reg_write(UC_X86_REG_CR0, cr0);
uc.reg_write(UC_X86_REG_CR4, cr4);
uc.reg_write(UC_X86_REG_MSR, msr);
}
private static void x86_mmu_pt_set(Unicorn uc, long vaddr, long paddr,
long tlb_base) {
long pto = ((vaddr & 0x000000001ff000L) >> 12) * 8;
long pte = (paddr) | 1 | (1 << 2);
uc.mem_write(tlb_base + 0x3000 + pto, Utils.toBytes((int) pte));
}
private static SyscallHook x86_mmu_syscall_callback = (uc, userdata) -> {
boolean[] parent_done = (boolean[]) userdata;
long rax = uc.reg_read(UC_X86_REG_RAX);
switch ((int) rax) {
case 57:
/* fork */
break;
case 60:
/* exit */
parent_done[0] = true;
uc.emu_stop();
return;
default:
System.out.println("unknown syscall");
System.exit(1);
}
if (!parent_done[0]) {
rax = 27;
uc.reg_write(UC_X86_REG_RAX, rax);
uc.emu_stop();
}
};
public static void cpu_tlb() {
long tlb_base = 0x3000;
long rip;
boolean[] parent_done = { false };
System.out.println(
"Emulate x86 amd64 code with mmu enabled and switch mappings");
Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_64);
uc.ctl_tlb_mode(UC_TLB_CPU);
Unicorn.Context context = uc.context_save();
uc.hook_add(x86_mmu_syscall_callback, UC_X86_INS_SYSCALL, 1, 0,
parent_done);
// Memory hooks are called after the mmu translation, so hook the physicall addresses
uc.hook_add(mmu_write_callback, UC_HOOK_MEM_WRITE, 0x1000, 0x3000,
null);
System.out.println("map code");
uc.mem_map(0x0, 0x1000, UC_PROT_ALL); // Code
uc.mem_write(0x0, CODE);
System.out.println("map parent memory");
uc.mem_map(0x1000, 0x1000, UC_PROT_ALL); // Parrent
System.out.println("map child memory");
uc.mem_map(0x2000, 0x1000, UC_PROT_ALL); // Child
System.out.println("map tlb memory");
uc.mem_map(tlb_base, 0x4000, UC_PROT_ALL); // TLB
System.out.println("set up the tlb");
x86_mmu_prepare_tlb(uc, 0x0, tlb_base);
x86_mmu_pt_set(uc, 0x2000, 0x0, tlb_base);
x86_mmu_pt_set(uc, 0x4000, 0x1000, tlb_base);
uc.ctl_flush_tlb();
System.out.println("run the parent");
uc.emu_start(0x2000, 0x0, 0, 0);
System.out.println("save the context for the child");
uc.context_update(context);
System.out.println("finish the parent");
rip = uc.reg_read(UC_X86_REG_RIP);
uc.emu_start(rip, 0x0, 0, 0);
System.out.println("restore the context for the child");
uc.context_restore(context);
x86_mmu_prepare_tlb(uc, 0x0, tlb_base);
x86_mmu_pt_set(uc, 0x4000, 0x2000, tlb_base);
uc.reg_write(UC_X86_REG_RAX, 0L);
uc.ctl_flush_tlb();
uc.emu_start(rip, 0x0, 0, 0);
long parent = Utils.toLong(uc.mem_read(0x1000, Long.BYTES));
long child = Utils.toLong(uc.mem_read(0x2000, Long.BYTES));
System.out.format("parent result == %d\n", parent);
System.out.format("child result == %d\n", child);
}
private static final TlbFillHook virtual_tlb_callback =
(uc, addr, type, user_data) -> {
boolean[] parent_done = (boolean[]) user_data;
System.out.format("tlb lookup for address: 0x%X\n", addr);
switch ((int) (addr & ~(0xfffL))) {
case 0x2000:
return 0x0L | UC_PROT_EXEC;
case 0x4000:
if (parent_done[0]) {
return (0x2000L) | UC_PROT_READ | UC_PROT_WRITE;
} else {
return (0x1000L) | UC_PROT_READ | UC_PROT_WRITE;
}
default:
return -1L;
}
};
public static void virtual_tlb() {
long rip;
boolean[] parent_done = { false };
System.out.println("Emulate x86 amd64 code with virtual mmu");
Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_64);
uc.ctl_tlb_mode(UC_TLB_VIRTUAL);
Unicorn.Context context = uc.context_save();
uc.hook_add(x86_mmu_syscall_callback, UC_X86_INS_SYSCALL, 1, 0,
parent_done);
// Memory hooks are called after the mmu translation, so hook the physicall addresses
uc.hook_add(mmu_write_callback, UC_HOOK_MEM_WRITE, 0x1000, 0x3000,
null);
System.out.println("map code");
uc.mem_map(0x0, 0x1000, UC_PROT_ALL); // Code
uc.mem_write(0x0, CODE);
System.out.println("map parent memory");
uc.mem_map(0x1000, 0x1000, UC_PROT_ALL); // Parrent
System.out.println("map child memory");
uc.mem_map(0x2000, 0x1000, UC_PROT_ALL); // Child
uc.hook_add(virtual_tlb_callback, 1, 0, parent_done);
System.out.println("run the parent");
uc.emu_start(0x2000, 0x0, 0, 0);
System.out.println("save the context for the child");
uc.context_update(context);
System.out.println("finish the parent");
rip = uc.reg_read(UC_X86_REG_RIP);
uc.emu_start(rip, 0x0, 0, 0);
System.out.println("restore the context for the child");
uc.context_restore(context);
parent_done[0] = true;
uc.reg_write(UC_X86_REG_RAX, 0);
uc.ctl_flush_tlb();
uc.emu_start(rip, 0x0, 0, 0);
long parent = Utils.toLong(uc.mem_read(0x1000, Long.BYTES));
long child = Utils.toLong(uc.mem_read(0x2000, Long.BYTES));
System.out.format("parent result == %d\n", parent);
System.out.format("child result == %d\n", child);
}
public static final void main(String[] args) {
cpu_tlb();
System.out.println("------------------");
virtual_tlb();
}
}