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); +}