From edd80ddeda78f29f177b61f78e4b9e6e19e0021c Mon Sep 17 00:00:00 2001 From: Robert Xiao Date: Sun, 14 May 2023 17:16:24 -0700 Subject: [PATCH] Port sample_x86_32_gdt_and_seg_regs over to Sample_x86_mmr --- bindings/java/samples/Sample_x86_mmr.java | 171 +++++++++++++++++++++- bindings/java/tests/TestSamples.java | 36 +++++ 2 files changed, 205 insertions(+), 2 deletions(-) diff --git a/bindings/java/samples/Sample_x86_mmr.java b/bindings/java/samples/Sample_x86_mmr.java index eaadf878..df38d216 100644 --- a/bindings/java/samples/Sample_x86_mmr.java +++ b/bindings/java/samples/Sample_x86_mmr.java @@ -23,11 +23,83 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. package samples; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Arrays; + import unicorn.*; public class Sample_x86_mmr implements UnicornConst, X86Const { + private static final MemHook hook_mem = + (uc, type, address, size, value, user_data) -> { + switch (type) { + case UC_MEM_WRITE: + System.out.format( + "mem write at 0x%x, size = %d, value = 0x%x\n", + address, size, value); + break; + default: + break; + } + }; + private static final CodeHook hook_code = + (uc, address, size, user_data) -> { + System.out.format("Executing at 0x%x, ilen = 0x%x\n", address, + size); + }; + + public static class SegmentDescriptor { + public static final int BYTES = 8; + + int base; + int limit; + + byte type; // 4 bits + byte system; // 1 bit: S flag + byte dpl; // 2 bits + byte present; // 1 bit: P flag + byte avail; // 1 bit + byte is_64_code; // 1 bit: L flag + byte db; // 1 bit: DB flag + byte granularity; // 1 bit: G flag + + public SegmentDescriptor() { + } + + // VERY basic descriptor init function, sets many fields to user space sane + // defaults + public SegmentDescriptor(int base, int limit, boolean is_code) { + this.base = base; + if (limit > 0xfffff) { + // need Giant granularity + limit >>= 12; + this.granularity = 1; + } + this.limit = limit; + + // some sane defaults + this.dpl = 3; + this.present = 1; + this.db = 1; // 32 bit + this.type = is_code ? (byte) 0xb : 3; + this.system = 1; // code or data + } + + public void appendToBuffer(ByteBuffer buf) { + buf.putShort((short) limit); + buf.putShort((short) base); + buf.put((byte) (base >>> 16)); + buf.put( + (byte) (type | (system << 4) | (dpl << 5) | (present << 7))); + buf.put((byte) (((limit >>> 16) & 0xf) | (avail << 4) | + (is_64_code << 5) | (db << 6) | (granularity << 7))); + buf.put((byte) (base >>> 24)); + } + } + public static void test_x86_mmr() { + System.out.println("Test x86 MMR read/write"); // Initialize emulator in X86-32bit mode Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_32); @@ -41,7 +113,7 @@ public class Sample_x86_mmr implements UnicornConst, X86Const { (short) 0xaaaa); X86_MMR gdtr2; - int eax; + long eax; // initialize machine registers @@ -50,7 +122,7 @@ public class Sample_x86_mmr implements UnicornConst, X86Const { uc.reg_write(UC_X86_REG_EAX, 0xddddddddL); // read the registers back out - eax = (int) uc.reg_read(UC_X86_REG_EAX); + eax = uc.reg_read(UC_X86_REG_EAX); ldtr2 = (X86_MMR) uc.reg_read(UC_X86_REG_LDTR, null); gdtr2 = (X86_MMR) uc.reg_read(UC_X86_REG_GDTR, null); @@ -65,8 +137,103 @@ public class Sample_x86_mmr implements UnicornConst, X86Const { System.out.printf(">>> GDTR.limit = 0x%x\n", gdtr2.limit); } + public static void gdt_demo() { + System.out.println("Demonstrate GDT usage"); + /* + bits 32 + + push dword 0x01234567 + push dword 0x89abcdef + + mov dword [fs:0], 0x01234567 + mov dword [fs:4], 0x89abcdef + */ + final byte[] code = + Utils.hexToBytes("686745230168efcdab8964c70500000000" + + "6745230164c70504000000efcdab89"); + final long code_address = 0x1000000L; + final long stack_address = 0x120000L; + final long gdt_address = 0xc0000000L; + final long fs_address = 0x7efdd000L; + + SegmentDescriptor[] gdt = new SegmentDescriptor[31]; + + int r_esp = (int) stack_address + 0x1000; // initial esp + int r_cs = 0x73; + int r_ss = 0x88; // ring 0 + int r_ds = 0x7b; + int r_es = 0x7b; + int r_fs = 0x83; + + X86_MMR gdtr = + new X86_MMR(gdt_address, gdt.length * SegmentDescriptor.BYTES - 1); + + gdt[14] = new SegmentDescriptor(0, 0xfffff000, true); // code segment + gdt[15] = new SegmentDescriptor(0, 0xfffff000, false); // data segment + gdt[16] = new SegmentDescriptor((int) fs_address, 0xfff, false); // one page data segment simulate fs + gdt[17] = new SegmentDescriptor(0, 0xfffff000, false); // ring 0 data + gdt[17].dpl = 0; // set descriptor privilege level + + // Initialize emulator in X86-32bit mode + Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_32); + uc.hook_add(hook_code, code_address, code_address + code.length, null); + uc.hook_add(hook_mem, UC_HOOK_MEM_WRITE, 1, 0, null); + + // map 1 page of code for this emulation + uc.mem_map(code_address, 0x1000, UC_PROT_ALL); + // map 1 page of stack for this emulation + uc.mem_map(stack_address, 0x1000, UC_PROT_READ | UC_PROT_WRITE); + // map 64k for a GDT + uc.mem_map(gdt_address, 0x10000, UC_PROT_WRITE | UC_PROT_READ); + // set up a GDT BEFORE you manipulate any segment registers + uc.reg_write(UC_X86_REG_GDTR, gdtr); + // write gdt to be emulated to memory + ByteBuffer gdt_buf = + ByteBuffer.allocate(gdt.length * SegmentDescriptor.BYTES) + .order(ByteOrder.LITTLE_ENDIAN); + for (SegmentDescriptor desc : gdt) { + if (desc == null) { + gdt_buf.put(new byte[SegmentDescriptor.BYTES]); + } else { + desc.appendToBuffer(gdt_buf); + } + } + uc.mem_write(gdt_address, gdt_buf.array()); + // map 1 page for FS + uc.mem_map(fs_address, 0x1000, UC_PROT_WRITE | UC_PROT_READ); + // write machine code to be emulated to memory + uc.mem_write(code_address, code); + // initialize machine registers + uc.reg_write(UC_X86_REG_ESP, r_esp); + // when setting SS, need rpl == cpl && dpl == cpl + // emulator starts with cpl == 0, so we need a dpl 0 descriptor and rpl 0 + // selector + uc.reg_write(UC_X86_REG_SS, r_ss); + uc.reg_write(UC_X86_REG_CS, r_cs); + uc.reg_write(UC_X86_REG_DS, r_ds); + uc.reg_write(UC_X86_REG_ES, r_es); + uc.reg_write(UC_X86_REG_FS, r_fs); + // emulate machine code in infinite time + uc.emu_start(code_address, code_address + code.length, 0, 0); + + // read from memory + byte[] buf = uc.mem_read(r_esp - 8, 8); + for (int i = 0; i < 8; i++) { + System.out.format("%02x", buf[i] & 0xff); + } + System.out.println(); + + assert Arrays.equals(buf, Utils.hexToBytes("efcdab8967452301")); + + // read from memory + buf = uc.mem_read(fs_address, 8); + assert Arrays.equals(buf, Utils.hexToBytes("67452301efcdab89")); + } + public static void main(String args[]) { test_x86_mmr(); + System.out.println("==================================="); + gdt_demo(); } } diff --git a/bindings/java/tests/TestSamples.java b/bindings/java/tests/TestSamples.java index 6c970035..ea458cd8 100644 --- a/bindings/java/tests/TestSamples.java +++ b/bindings/java/tests/TestSamples.java @@ -1172,4 +1172,40 @@ public class TestSamples implements UnicornConst { ">>> We have to add a map at 0x10000 before continue execution!\n", outContent.toString()); } + + @Test + public void testX86Mmr() { + assumeTrue(Unicorn.arch_supported(UC_ARCH_X86)); + samples.Sample_x86_mmr.test_x86_mmr(); + assertEquals( + "Test x86 MMR read/write\n" + + ">>> EAX = 0xdddddddd\n" + + ">>> LDTR.base = 0x22222222\n" + + ">>> LDTR.limit = 0x33333333\n" + + ">>> LDTR.flags = 0x44444444\n" + + ">>> LDTR.selector = 0x5555\n" + + "\n" + + ">>> GDTR.base = 0x77777777\n" + + ">>> GDTR.limit = 0x8888\n", + outContent.toString()); + } + + @Test + public void testX86Gdt() { + assumeTrue(Unicorn.arch_supported(UC_ARCH_X86)); + samples.Sample_x86_mmr.gdt_demo(); + assertEquals( + "Demonstrate GDT usage\n" + + "Executing at 0x1000000, ilen = 0x5\n" + + "mem write at 0x120ffc, size = 4, value = 0x1234567\n" + + "Executing at 0x1000005, ilen = 0x5\n" + + "mem write at 0x120ff8, size = 4, value = 0x89abcdef\n" + + "Executing at 0x100000a, ilen = 0xb\n" + + "mem write at 0x7efdd000, size = 4, value = 0x1234567\n" + + "Executing at 0x1000015, ilen = 0xb\n" + + "mem write at 0x7efdd004, size = 4, value = 0x89abcdef\n" + + "efcdab8967452301\n", + outContent.toString()); + } + }