From 6c3eecb2a36d202d583687199d01846537646785 Mon Sep 17 00:00:00 2001 From: samothtronicien Date: Sat, 30 Jul 2016 04:18:12 +0200 Subject: [PATCH] added unit test for x86 This test highlight the issue with the SHL instruction in the form (SHL r, CL), the flags values retrieved in the code hook are not correct. --- tests/unit/test_x86_shl.cpp | 228 ++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 tests/unit/test_x86_shl.cpp diff --git a/tests/unit/test_x86_shl.cpp b/tests/unit/test_x86_shl.cpp new file mode 100644 index 00000000..809390db --- /dev/null +++ b/tests/unit/test_x86_shl.cpp @@ -0,0 +1,228 @@ +#include +#include + +extern "C" +{ +#include "unicorn_test.h" +} + + +#define OK(x) uc_assert_success(x) + +#define CF_MASK (1<<0) +#define PF_MASK (1<<2) +#define ZF_MASK (1<<6) +#define SF_MASK (1<<7) +#define OF_MASK (1<<11) +#define ALL_MASK (OF_MASK|SF_MASK|ZF_MASK|PF_MASK|CF_MASK) + + +struct instruction +{ + std::vector code; + const char* asmStr; + struct reg_value { + uint32_t regId, regValue, mask; + reg_value(uint32_t _rid, uint32_t _rval, uint32_t _msk = 0xFFFFFFFF) : regId(_rid), regValue(_rval), mask(_msk) + {} + }; + std::vector values; +}; + +struct exec_state +{ + uint32_t curr; + std::vector insts; +}; + +const char* getRegisterName(uint32_t _regid) +{ + switch (_regid) + { + //8 + case UC_X86_REG_AH: return "AH"; + case UC_X86_REG_AL: return "AL"; + case UC_X86_REG_BH: return "BH"; + case UC_X86_REG_BL: return "BL"; + case UC_X86_REG_CL: return "CL"; + case UC_X86_REG_CH: return "CH"; + case UC_X86_REG_DH: return "DH"; + case UC_X86_REG_DL: return "DL"; + //16 + case UC_X86_REG_AX: return "AX"; + case UC_X86_REG_BX: return "BX"; + case UC_X86_REG_CX: return "CX"; + case UC_X86_REG_DX: return "DX"; + //32 + case UC_X86_REG_EAX: return "EAX"; + case UC_X86_REG_EBX: return "EBX"; + case UC_X86_REG_ECX: return "ECX"; + case UC_X86_REG_EDX: return "EDX"; + case UC_X86_REG_EDI: return "EDI"; + case UC_X86_REG_ESI: return "ESI"; + case UC_X86_REG_EBP: return "EBP"; + case UC_X86_REG_ESP: return "ESP"; + case UC_X86_REG_EIP: return "EIP"; + case UC_X86_REG_EFLAGS: return "EFLAGS"; + + default: fail(); + } +} + +uint32_t getRegisterValue(uc_engine *uc, uint32_t _regid) +{ + switch (_regid) + { + //8 + case UC_X86_REG_AH: case UC_X86_REG_AL: + case UC_X86_REG_BH: case UC_X86_REG_BL: + case UC_X86_REG_CL: case UC_X86_REG_CH: + case UC_X86_REG_DH: case UC_X86_REG_DL: + { + uint8_t val = 0; + uc_reg_read(uc, _regid, &val); + return val; + } + //16 + case UC_X86_REG_AX: case UC_X86_REG_BX: + case UC_X86_REG_CX: case UC_X86_REG_DX: + { + uint16_t val = 0; + uc_reg_read(uc, _regid, &val); + return val; + } + //32 + case UC_X86_REG_EAX: case UC_X86_REG_EBX: + case UC_X86_REG_ECX: case UC_X86_REG_EDX: + case UC_X86_REG_EDI: case UC_X86_REG_ESI: + case UC_X86_REG_EBP: case UC_X86_REG_ESP: + case UC_X86_REG_EIP: case UC_X86_REG_EFLAGS: + { + uint32_t val = 0; + uc_reg_read(uc, _regid, &val); + return val; + } + + default: fail(); + } +} + +void hook_code_test_i386_shl(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) +{ + exec_state* es = (exec_state*)user_data; + + print_message("\teip=%08x - %s\n", (uint32_t)address, es->insts[es->curr].asmStr); + + std::vector& rval = es->insts[es->curr].values; + for (auto& v : rval) + { + uint32_t regValue = getRegisterValue(uc, v.regId); + print_message("\t\ttesting %s : ", getRegisterName(v.regId)); + assert_int_equal(regValue & v.mask, v.regValue); + print_message("ok\n"); + } + + es->curr++; +} + +std::vector codeSHL_prob = { + { { 0xBB, 0x3C, 0x00, 0x00, 0x00 }, "mov ebx, 3Ch", {} }, + { { 0xB1, 0x02 }, "mov cl, 2", { { UC_X86_REG_EBX, 0x3C } } }, + { { 0xD3, 0xE3 }, "shl ebx, cl", { { UC_X86_REG_EBX, 0x3C }, { UC_X86_REG_CL, 0x2 } } }, + { { 0x9F }, "lahf", { { UC_X86_REG_EBX, 0xF0 }, { UC_X86_REG_CL, 0x2 }, { UC_X86_REG_EFLAGS, 0x4, ALL_MASK } } }, + { { 0xCC }, "int3", { { UC_X86_REG_AH, 0x4, PF_MASK }, { UC_X86_REG_EBX, 0xF0 }, { UC_X86_REG_CL, 0x2 }, { UC_X86_REG_EFLAGS, 0x4, ALL_MASK } } } +}; + +std::vector codeSHL_ok = { + { { 0xBB, 0x3C, 0x00, 0x00, 0x00 }, "mov ebx, 3Ch", {} }, + { { 0xC1, 0xE3, 0x02 }, "shl ebx, 2", { { UC_X86_REG_EBX, 0x3C } } }, + { { 0x9F }, "lahf", { { UC_X86_REG_EBX, 0xF0 }, { UC_X86_REG_EFLAGS, 0x4, ALL_MASK } } }, + { { 0xCC }, "int3", { { UC_X86_REG_AH, 0x4, PF_MASK }, { UC_X86_REG_EBX, 0xF0 }, { UC_X86_REG_EFLAGS, 0x4, ALL_MASK } } } +}; + +uint32_t loadCode(uc_engine *_uc, const std::vector& _insts, uint32_t _at) +{ + std::vector code; + for (auto& inst : _insts) + code.insert(code.end(), inst.code.begin(), inst.code.end()); + + OK(uc_mem_write(_uc, _at, code.data(), code.size())); + + return code.size(); +} + +#define ADDR_START 0x100000 + +static void test_i386_shl_prob(void **state) +{ + uc_engine *uc; + uc_err err; + uc_hook trace1; + + // Initialize emulator in X86-32bit mode + OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); + OK(uc_mem_map(uc, ADDR_START, 0x1000, UC_PROT_ALL)); + + uint32_t codeSize = loadCode(uc, codeSHL_prob, ADDR_START); + + exec_state es; + es.curr = 0; + es.insts = codeSHL_prob; + + // initialize machine registers + uint32_t zero = 0; + OK(uc_reg_write(uc, UC_X86_REG_EAX, &zero)); + OK(uc_reg_write(uc, UC_X86_REG_EBX, &zero)); + OK(uc_reg_write(uc, UC_X86_REG_ECX, &zero)); + OK(uc_reg_write(uc, UC_X86_REG_EDX, &zero)); + + OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, hook_code_test_i386_shl, &es, 1, 0)); + + // emulate machine code in infinite time + OK(uc_emu_start(uc, ADDR_START, ADDR_START + codeSize - 1, 0, 0)); + + uc_close(uc); +} + +static void test_i386_shl_ok(void **state) +{ + uc_engine *uc; + uc_err err; + uc_hook trace1; + + // Initialize emulator in X86-32bit mode + OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); + OK(uc_mem_map(uc, ADDR_START, 0x1000, UC_PROT_ALL)); + + uint32_t codeSize = loadCode(uc, codeSHL_ok, ADDR_START); + + exec_state es; + es.curr = 0; + es.insts = codeSHL_ok; + + // initialize machine registers + uint32_t zero = 0; + OK(uc_reg_write(uc, UC_X86_REG_EAX, &zero)); + OK(uc_reg_write(uc, UC_X86_REG_EBX, &zero)); + OK(uc_reg_write(uc, UC_X86_REG_ECX, &zero)); + OK(uc_reg_write(uc, UC_X86_REG_EDX, &zero)); + + OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, hook_code_test_i386_shl, &es, 1, 0)); + + // emulate machine code in infinite time + OK(uc_emu_start(uc, ADDR_START, ADDR_START + codeSize - 1, 0, 0)); + + uc_close(uc); +} + +/******************************************************************************/ + +int main(void) { + const struct CMUnitTest tests[] = { + + cmocka_unit_test(test_i386_shl_prob), + cmocka_unit_test(test_i386_shl_ok), + + }; + return cmocka_run_group_tests(tests, NULL, NULL); +}