From 9977054a15419fba5541c0d224ed08bd07b84ed0 Mon Sep 17 00:00:00 2001 From: Chris Eagle Date: Wed, 3 Feb 2016 09:22:29 -0800 Subject: [PATCH] add support for setting gdtr, idtr, ldtr, and tr programatically --- include/unicorn/x86.h | 3 + qemu/target-i386/unicorn.c | 144 ++++++++++++++++++++++++++++++++++ tests/unit/test_gdt_idt_x86.c | 137 ++++++++++++++++++++++++++++++++ 3 files changed, 284 insertions(+) create mode 100755 tests/unit/test_gdt_idt_x86.c diff --git a/include/unicorn/x86.h b/include/unicorn/x86.h index b279de58..7d18c96d 100644 --- a/include/unicorn/x86.h +++ b/include/unicorn/x86.h @@ -64,6 +64,9 @@ typedef enum uc_x86_reg { UC_X86_REG_R9D, UC_X86_REG_R10D, UC_X86_REG_R11D, UC_X86_REG_R12D, UC_X86_REG_R13D, UC_X86_REG_R14D, UC_X86_REG_R15D, UC_X86_REG_R8W, UC_X86_REG_R9W, UC_X86_REG_R10W, UC_X86_REG_R11W, UC_X86_REG_R12W, UC_X86_REG_R13W, UC_X86_REG_R14W, UC_X86_REG_R15W, + UC_X86_REG_IDTR_LIMIT, UC_X86_REG_IDTR_BASE, UC_X86_REG_GDTR_LIMIT, UC_X86_REG_GDTR_BASE, + UC_X86_REG_LDTR_SS, UC_X86_REG_LDTR_LIMIT, UC_X86_REG_LDTR_BASE, UC_X86_REG_LDTR_ATTR, + UC_X86_REG_TR_SS, UC_X86_REG_TR_LIMIT, UC_X86_REG_TR_BASE, UC_X86_REG_TR_ATTR, UC_X86_REG_ENDING // <-- mark the end of the list of registers } uc_x86_reg; diff --git a/qemu/target-i386/unicorn.c b/qemu/target-i386/unicorn.c index 0af5763e..ce495c36 100644 --- a/qemu/target-i386/unicorn.c +++ b/qemu/target-i386/unicorn.c @@ -277,6 +277,42 @@ int x86_reg_read(struct uc_struct *uc, unsigned int regid, void *value) case UC_X86_REG_GS: *(int32_t *)value = X86_CPU(uc, mycpu)->env.segs[R_GS].base; break; + case UC_X86_REG_IDTR_LIMIT: + *(int16_t *)value = READ_WORD(X86_CPU(uc, mycpu)->env.idt.limit); + break; + case UC_X86_REG_IDTR_BASE: + *(int32_t *)value = X86_CPU(uc, mycpu)->env.idt.base; + break; + case UC_X86_REG_GDTR_LIMIT: + *(int16_t *)value = READ_WORD(X86_CPU(uc, mycpu)->env.gdt.limit); + break; + case UC_X86_REG_GDTR_BASE: + *(int32_t *)value = X86_CPU(uc, mycpu)->env.gdt.base; + break; + case UC_X86_REG_LDTR_SS: + *(int16_t *)value = READ_WORD(X86_CPU(uc, mycpu)->env.ldt.selector); + break; + case UC_X86_REG_LDTR_LIMIT: + *(int32_t *)value = X86_CPU(uc, mycpu)->env.ldt.limit; + break; + case UC_X86_REG_LDTR_BASE: + *(int32_t *)value = X86_CPU(uc, mycpu)->env.ldt.base; + break; + case UC_X86_REG_LDTR_ATTR: + *(int32_t *)value = X86_CPU(uc, mycpu)->env.ldt.flags; + break; + case UC_X86_REG_TR_SS: + *(int16_t *)value = READ_WORD(X86_CPU(uc, mycpu)->env.tr.selector); + break; + case UC_X86_REG_TR_LIMIT: + *(int32_t *)value = X86_CPU(uc, mycpu)->env.tr.limit; + break; + case UC_X86_REG_TR_BASE: + *(int32_t *)value = X86_CPU(uc, mycpu)->env.tr.base; + break; + case UC_X86_REG_TR_ATTR: + *(int32_t *)value = X86_CPU(uc, mycpu)->env.tr.flags; + break; } break; @@ -525,6 +561,42 @@ int x86_reg_read(struct uc_struct *uc, unsigned int regid, void *value) case UC_X86_REG_R15B: *(int8_t *)value = READ_BYTE_L(X86_CPU(uc, mycpu)->env.regs[15]); break; + case UC_X86_REG_IDTR_LIMIT: + *(int16_t *)value = READ_WORD(X86_CPU(uc, mycpu)->env.idt.limit); + break; + case UC_X86_REG_IDTR_BASE: + *(int64_t *)value = X86_CPU(uc, mycpu)->env.idt.base; + break; + case UC_X86_REG_GDTR_LIMIT: + *(int16_t *)value = READ_WORD(X86_CPU(uc, mycpu)->env.gdt.limit); + break; + case UC_X86_REG_GDTR_BASE: + *(int64_t *)value = X86_CPU(uc, mycpu)->env.gdt.base; + break; + case UC_X86_REG_LDTR_SS: + *(int16_t *)value = READ_WORD(X86_CPU(uc, mycpu)->env.ldt.selector); + break; + case UC_X86_REG_LDTR_LIMIT: + *(int32_t *)value = X86_CPU(uc, mycpu)->env.ldt.limit; + break; + case UC_X86_REG_LDTR_BASE: + *(int64_t *)value = X86_CPU(uc, mycpu)->env.ldt.base; + break; + case UC_X86_REG_LDTR_ATTR: + *(int32_t *)value = X86_CPU(uc, mycpu)->env.ldt.flags; + break; + case UC_X86_REG_TR_SS: + *(int16_t *)value = READ_WORD(X86_CPU(uc, mycpu)->env.tr.selector); + break; + case UC_X86_REG_TR_LIMIT: + *(int32_t *)value = X86_CPU(uc, mycpu)->env.tr.limit; + break; + case UC_X86_REG_TR_BASE: + *(int64_t *)value = X86_CPU(uc, mycpu)->env.tr.base; + break; + case UC_X86_REG_TR_ATTR: + *(int32_t *)value = X86_CPU(uc, mycpu)->env.tr.flags; + break; } break; #endif @@ -684,6 +756,42 @@ int x86_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) case UC_X86_REG_GS: X86_CPU(uc, mycpu)->env.segs[R_GS].base = *(uint32_t *)value; break; + case UC_X86_REG_IDTR_LIMIT: + WRITE_WORD(X86_CPU(uc, mycpu)->env.idt.limit, *(uint16_t *)value); + break; + case UC_X86_REG_IDTR_BASE: + X86_CPU(uc, mycpu)->env.idt.base = *(uint32_t *)value; + break; + case UC_X86_REG_GDTR_LIMIT: + WRITE_WORD(X86_CPU(uc, mycpu)->env.gdt.limit, *(uint16_t *)value); + break; + case UC_X86_REG_GDTR_BASE: + X86_CPU(uc, mycpu)->env.gdt.base = *(uint32_t *)value; + break; + case UC_X86_REG_LDTR_SS: + WRITE_WORD(X86_CPU(uc, mycpu)->env.ldt.selector, *(uint16_t *)value); + break; + case UC_X86_REG_LDTR_LIMIT: + X86_CPU(uc, mycpu)->env.ldt.limit = *(uint32_t *)value; + break; + case UC_X86_REG_LDTR_BASE: + X86_CPU(uc, mycpu)->env.ldt.base = *(uint32_t *)value; + break; + case UC_X86_REG_LDTR_ATTR: + X86_CPU(uc, mycpu)->env.ldt.flags = *(uint32_t *)value; + break; + case UC_X86_REG_TR_SS: + WRITE_WORD(X86_CPU(uc, mycpu)->env.tr.selector, *(uint16_t *)value); + break; + case UC_X86_REG_TR_LIMIT: + X86_CPU(uc, mycpu)->env.tr.limit = *(uint32_t *)value; + break; + case UC_X86_REG_TR_BASE: + X86_CPU(uc, mycpu)->env.tr.base = *(uint32_t *)value; + break; + case UC_X86_REG_TR_ATTR: + X86_CPU(uc, mycpu)->env.tr.flags = *(uint32_t *)value; + break; } break; @@ -942,6 +1050,42 @@ int x86_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) case UC_X86_REG_R15B: WRITE_BYTE_L(X86_CPU(uc, mycpu)->env.regs[15], *(uint8_t *)value); break; + case UC_X86_REG_IDTR_LIMIT: + WRITE_WORD(X86_CPU(uc, mycpu)->env.idt.limit, *(uint16_t *)value); + break; + case UC_X86_REG_IDTR_BASE: + X86_CPU(uc, mycpu)->env.idt.base = *(uint64_t *)value; + break; + case UC_X86_REG_GDTR_LIMIT: + WRITE_WORD(X86_CPU(uc, mycpu)->env.gdt.limit, *(uint16_t *)value); + break; + case UC_X86_REG_GDTR_BASE: + X86_CPU(uc, mycpu)->env.gdt.base = *(uint64_t *)value; + break; + case UC_X86_REG_LDTR_SS: + WRITE_WORD(X86_CPU(uc, mycpu)->env.ldt.selector, *(uint16_t *)value); + break; + case UC_X86_REG_LDTR_LIMIT: + WRITE_DWORD(X86_CPU(uc, mycpu)->env.ldt.limit, *(uint32_t *)value); + break; + case UC_X86_REG_LDTR_BASE: + X86_CPU(uc, mycpu)->env.ldt.base = *(uint64_t *)value; + break; + case UC_X86_REG_LDTR_ATTR: + WRITE_DWORD(X86_CPU(uc, mycpu)->env.ldt.flags, *(uint32_t *)value); + break; + case UC_X86_REG_TR_SS: + WRITE_WORD(X86_CPU(uc, mycpu)->env.tr.selector, *(uint16_t *)value); + break; + case UC_X86_REG_TR_LIMIT: + WRITE_DWORD(X86_CPU(uc, mycpu)->env.tr.limit, *(uint32_t *)value); + break; + case UC_X86_REG_TR_BASE: + X86_CPU(uc, mycpu)->env.tr.base = *(uint64_t *)value; + break; + case UC_X86_REG_TR_ATTR: + WRITE_DWORD(X86_CPU(uc, mycpu)->env.tr.flags, *(uint32_t *)value); + break; } break; #endif diff --git a/tests/unit/test_gdt_idt_x86.c b/tests/unit/test_gdt_idt_x86.c new file mode 100755 index 00000000..03c66bb9 --- /dev/null +++ b/tests/unit/test_gdt_idt_x86.c @@ -0,0 +1,137 @@ +#include +#include +#include +#include +#include +#include + +/** + * Assert that err matches expect + */ +#define uc_assert_err(expect, err) \ +do { \ + uc_err __err = err; \ + if (__err != expect) { \ + fprintf(stderr, "%s", uc_strerror(__err)); \ + exit(1); \ + } \ +} while (0) + +/** + * Assert that err is UC_ERR_OK + */ +#define uc_assert_success(err) uc_assert_err(UC_ERR_OK, err) + +/** + * Assert that err is anything but UC_ERR_OK + * + * Note: Better to use uc_assert_err(, err), + * as this serves to document which errors a function will return + * in various scenarios. + */ +#define uc_assert_fail(err) \ +do { \ + uc_err __err = err; \ + if (__err == UC_ERR_OK) { \ + fprintf(stderr, "%s", uc_strerror(__err)); \ + exit(1); \ + } \ +} while (0) + +#define OK(x) uc_assert_success(x) + +/******************************************************************************/ + +static void test_idt_gdt_i386(/*void **state*/) +{ + uc_engine *uc; + uc_err err; + uint8_t buf[6]; + + const uint8_t code[] = "\x0f\x01\x0c\x24\x0f\x01\x44\x24\x06"; // sidt [esp]; sgdt [esp+6] + const uint64_t address = 0x1000000; + + int r_esp = address + 0x1000 - 0x100; // initial esp + + int idt_base = 0x12345678; + int idt_limit = 0xabcd; + int gdt_base = 0x87654321; + int gdt_limit = 0xdcba; + + // Initialize emulator in X86-32bit mode + err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc); + uc_assert_success(err); + + // map 1 page memory for this emulation + err = uc_mem_map(uc, address, 0x1000, UC_PROT_ALL); + uc_assert_success(err); + + // write machine code to be emulated to memory + err = uc_mem_write(uc, address, code, sizeof(code)-1); + uc_assert_success(err); + + // initialize machine registers + err = uc_reg_write(uc, UC_X86_REG_ESP, &r_esp); + uc_assert_success(err); + err = uc_reg_write(uc, UC_X86_REG_IDTR_BASE, &idt_base); + uc_assert_success(err); + err = uc_reg_write(uc, UC_X86_REG_IDTR_LIMIT, &idt_limit); + uc_assert_success(err); + err = uc_reg_write(uc, UC_X86_REG_GDTR_BASE, &gdt_base); + uc_assert_success(err); + err = uc_reg_write(uc, UC_X86_REG_GDTR_LIMIT, &gdt_limit); + uc_assert_success(err); + + idt_base = 0; + idt_limit = 0; + gdt_base = 0; + gdt_limit = 0; + + // emulate machine code in infinite time + err = uc_emu_start(uc, address, address+sizeof(code)-1, 0, 0); + uc_assert_success(err); + + + uc_reg_read(uc, UC_X86_REG_IDTR_BASE, &idt_base); + assert(idt_base == 0x12345678); + + uc_reg_read(uc, UC_X86_REG_IDTR_LIMIT, &idt_limit); + assert(idt_limit == 0xabcd); + + uc_reg_read(uc, UC_X86_REG_GDTR_BASE, &gdt_base); + assert(gdt_base == 0x87654321); + + uc_reg_read(uc, UC_X86_REG_GDTR_LIMIT, &gdt_limit); + assert(gdt_limit == 0xdcba); + + // read from memory + err = uc_mem_read(uc, r_esp, buf, 6); + uc_assert_success(err); + + assert(memcmp(buf, "\xcd\xab\x78\x56\x34\x12", 6) == 0); + + // read from memory + err = uc_mem_read(uc, r_esp + 6, buf, 6); + uc_assert_success(err); + + assert(memcmp(buf, "\xba\xdc\x21\x43\x65\x87", 6) == 0); + + uc_close(uc); + +} + +/******************************************************************************/ + +int main(void) { +/* + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_idt_gdt_i386) + }; + return cmocka_run_group_tests(tests, NULL, NULL); +*/ + test_idt_gdt_i386(); + + fprintf(stderr, "success\n"); + + return 0; +}