Port sample_x86_32_gdt_and_seg_regs over to Sample_x86_mmr

This commit is contained in:
Robert Xiao 2023-05-14 17:16:24 -07:00
parent 4f563490e2
commit edd80ddeda
2 changed files with 205 additions and 2 deletions

View File

@ -23,11 +23,83 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
package samples; package samples;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import unicorn.*; import unicorn.*;
public class Sample_x86_mmr implements UnicornConst, X86Const { 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() { public static void test_x86_mmr() {
System.out.println("Test x86 MMR read/write");
// Initialize emulator in X86-32bit mode // Initialize emulator in X86-32bit mode
Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_32); Unicorn uc = new Unicorn(UC_ARCH_X86, UC_MODE_32);
@ -41,7 +113,7 @@ public class Sample_x86_mmr implements UnicornConst, X86Const {
(short) 0xaaaa); (short) 0xaaaa);
X86_MMR gdtr2; X86_MMR gdtr2;
int eax; long eax;
// initialize machine registers // initialize machine registers
@ -50,7 +122,7 @@ public class Sample_x86_mmr implements UnicornConst, X86Const {
uc.reg_write(UC_X86_REG_EAX, 0xddddddddL); uc.reg_write(UC_X86_REG_EAX, 0xddddddddL);
// read the registers back out // 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); ldtr2 = (X86_MMR) uc.reg_read(UC_X86_REG_LDTR, null);
gdtr2 = (X86_MMR) uc.reg_read(UC_X86_REG_GDTR, 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); 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[]) { public static void main(String args[]) {
test_x86_mmr(); test_x86_mmr();
System.out.println("===================================");
gdt_demo();
} }
} }

View File

@ -1172,4 +1172,40 @@ public class TestSamples implements UnicornConst {
">>> We have to add a map at 0x10000 before continue execution!\n", ">>> We have to add a map at 0x10000 before continue execution!\n",
outContent.toString()); 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());
}
} }