/* stdcall unicorn engine shim layer for use with VB6 or C# code ripped from unicorn_dynload.c Contributed by: FireEye FLARE team Author: David Zimmer , License: Apache Disassembler support can be optionally compiled in using: libdasm (c) 2004 - 2006 jt / nologin.org this project has been built with vs2008 precompiled binaries with disasm support available here: https://github.com/dzzie/libs/tree/master/unicorn_emu */ #include #include #ifdef _WIN64 #error vb6 is 32bit only #endif #ifdef DYNLOAD #include "./../msvc/unicorn_dynload.h" #else #include #pragma comment(lib, "unicorn_staload.lib") #endif //if you compile with VS2008 you will need to add stdint.h and inttypes.h to your compiler include directory //you can find examples here: https://github.com/dzzie/VS_LIBEMU/tree/master/libemu/include //if you want to include disassembler support: // 1) install libdasm in your compilers include directory // 2) add libdasm.h/.c to the project (drag and drop into VS project explorer), // 3) remove the comment from the define below. //The vb code detects the changes at runtime. //#define INCLUDE_DISASM #ifdef INCLUDE_DISASM #include #endif #include "msvbvm60.tlh" //so we can use the vb6 collection object #define EXPORT comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__) enum hookCatagory{hc_code = 0, hc_block = 1, hc_inst = 2, hc_int = 3, hc_mem = 4, hc_memInvalid = 5}; //tracing UC_HOOK_CODE & UC_HOOK_BLOCK typedef void (__stdcall *vb_cb_hookcode_t) (uc_engine *uc, uint64_t address, uint32_t size, void *user_data); vb_cb_hookcode_t vbHookcode = 0; vb_cb_hookcode_t vbHookBlock = 0; //hooking memory UC_MEM_READ/WRITE/FETCH typedef void (__stdcall *vb_cb_hookmem_t) (uc_engine *uc, uc_mem_type type, uint64_t address, int size,int64_t value, void *user_data); vb_cb_hookmem_t vbHookMem = 0; //invalid memory access UC_MEM_*_UNMAPPED and UC_MEM_*PROT events typedef bool (__stdcall *vb_cb_eventmem_t) (uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, void *user_data); vb_cb_eventmem_t vbInvalidMem = 0; //tracing interrupts for uc_hook_intr() typedef void (__stdcall *vb_cb_hookintr_t) (uc_engine *uc, uint32_t intno, void *user_data); vb_cb_hookintr_t vbHookInt = 0; /* typedef uint32_t (__stdcall *uc_cb_insn_in_t)(uc_engine *uc, uint32_t port, int size, void *user_data); tracing IN instruction of X86 typedef void (__stdcall *uc_cb_insn_out_t) (uc_engine *uc, uint32_t port, int size, uint32_t value, void *user_data); tracing OUT instruction of X86 */ //------------------ [ call back proxies ] ------------------------- static void c_hook_code(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) { if(vbHookcode==0) return; vbHookcode(uc,address,size,user_data); } static void c_hook_mem(uc_engine *uc, uc_mem_type type,uint64_t address, int size, int64_t value, void *user_data) { if(vbHookMem==0) return; vbHookMem(uc,type,address,size,value,user_data); } static bool c_hook_mem_invalid(uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, void *user_data) { if(vbInvalidMem==0) return false; return vbInvalidMem(uc,type,address,size,value,user_data); } static void c_hook_block(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) { if(vbHookBlock==0) return; vbHookBlock(uc,address,size,user_data); } static void c_hook_intr(uc_engine *uc, uint32_t intno, void *user_data) { if(vbHookInt==0) return; vbHookInt(uc,intno,user_data); } /* static uint32_t hook_in(uc_engine *uc, uint32_t port, int size, void *user_data) { } static void hook_out(uc_engine *uc, uint32_t port, int size, uint32_t value, void *user_data) { } */ //------------------------------------------------------------- //uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, uint64_t begin, uint64_t end, ...); //we need to use a C stub cdecl callback then proxy to the stdcall vb one.. //we could get cute with an asm thunk in vb but not worth complexity there are only a couple of them to support.. //cdecl callback to vb stdcall callback for tracing uc_err __stdcall ucs_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, uint64_t begin, uint64_t end, int catagory, int instr_id){ #pragma EXPORT if(catagory == hc_code){ if(vbHookcode == 0){ if((int)callback==0) return UC_ERR_FETCH_UNMAPPED; vbHookcode = (vb_cb_hookcode_t)callback; } return uc_hook_add(uc,hh,type,c_hook_code,user_data,begin,end); } if(catagory == hc_block){ if(vbHookBlock == 0){ if((int)callback==0) return UC_ERR_FETCH_UNMAPPED; vbHookBlock = (vb_cb_hookcode_t)callback; } return uc_hook_add(uc,hh,type,c_hook_block,user_data,begin,end); } if(catagory == hc_mem){ //then it is some combination of memory access hook flags.. if(vbHookMem == 0){ if((int)callback==0) return UC_ERR_FETCH_UNMAPPED; vbHookMem = (vb_cb_hookmem_t)callback; } return uc_hook_add(uc,hh,type,c_hook_mem,user_data,begin,end); } if(catagory == hc_memInvalid){ //then it is some combination of invalid memory access hook flags.. if(vbInvalidMem == 0){ if((int)callback==0) return UC_ERR_FETCH_UNMAPPED; vbInvalidMem = (vb_cb_eventmem_t)callback; } return uc_hook_add(uc,hh,type,c_hook_mem_invalid,user_data,begin,end); } if(catagory == hc_int){ if(vbHookInt == 0){ if((int)callback==0) return UC_ERR_FETCH_UNMAPPED; vbHookInt = (vb_cb_hookintr_t)callback; } return uc_hook_add(uc,hh,UC_HOOK_INTR,c_hook_intr,user_data,begin,end); } return UC_ERR_ARG; } unsigned int __stdcall ucs_dynload(char *path){ #pragma EXPORT #ifdef DYNLOAD return uc_dyn_load(path, 0); #else return 1; #endif } unsigned int __stdcall ucs_version(unsigned int *major, unsigned int *minor){ #pragma EXPORT return uc_version(major, minor); } bool __stdcall ucs_arch_supported(uc_arch arch){ #pragma EXPORT return uc_arch_supported(arch); } uc_err __stdcall ucs_open(uc_arch arch, uc_mode mode, uc_engine **uc){ #pragma EXPORT return uc_open(arch, mode, uc); } uc_err __stdcall ucs_close(uc_engine *uc){ #pragma EXPORT return uc_close(uc); } uc_err __stdcall ucs_query(uc_engine *uc, uc_query_type type, size_t *result){ #pragma EXPORT return uc_query(uc, type, result); } uc_err __stdcall ucs_errno(uc_engine *uc){ #pragma EXPORT return uc_errno(uc); } const char *__stdcall ucs_strerror(uc_err code){ #pragma EXPORT return uc_strerror(code); } uc_err __stdcall ucs_reg_write(uc_engine *uc, int regid, const void *value){ #pragma EXPORT return uc_reg_write(uc, regid, value); } uc_err __stdcall ucs_reg_read(uc_engine *uc, int regid, void *value){ #pragma EXPORT return uc_reg_read(uc, regid, value); } uc_err __stdcall ucs_reg_write_batch(uc_engine *uc, int *regs, void *const *vals, int count){ #pragma EXPORT return uc_reg_write_batch(uc, regs, vals, count); } uc_err __stdcall ucs_reg_read_batch(uc_engine *uc, int *regs, void **vals, int count){ #pragma EXPORT return uc_reg_read_batch(uc, regs, vals, count); } uc_err __stdcall ucs_mem_write(uc_engine *uc, uint64_t address, const void *bytes, size_t size){ #pragma EXPORT return uc_mem_write(uc, address, bytes, size); } uc_err __stdcall ucs_mem_read(uc_engine *uc, uint64_t address, void *bytes, size_t size){ #pragma EXPORT return uc_mem_read(uc, address, bytes, size); } uc_err __stdcall ucs_emu_start(uc_engine *uc, uint64_t begin, uint64_t until, uint64_t timeout, size_t count){ #pragma EXPORT return uc_emu_start(uc, begin, until, timeout, count); } uc_err __stdcall ucs_emu_stop(uc_engine *uc){ #pragma EXPORT return uc_emu_stop(uc); } uc_err __stdcall ucs_hook_del(uc_engine *uc, uc_hook hh){ #pragma EXPORT return uc_hook_del(uc, hh); } uc_err __stdcall ucs_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms){ #pragma EXPORT return uc_mem_map(uc, address, size, perms); } //requires link against v1.0 uc_err __stdcall ucs_mem_map_ptr(uc_engine *uc, uint64_t address, size_t size, uint32_t perms, void *ptr){ #pragma EXPORT return uc_mem_map_ptr(uc, address, size, perms, ptr); } uc_err __stdcall ucs_mem_unmap(uc_engine *uc, uint64_t address, size_t size){ #pragma EXPORT return uc_mem_unmap(uc, address, size); } uc_err __stdcall ucs_mem_protect(uc_engine *uc, uint64_t address, size_t size, uint32_t perms){ #pragma EXPORT return uc_mem_protect(uc, address, size, perms); } uc_err __stdcall ucs_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count){ #pragma EXPORT return uc_mem_regions(uc, regions, count); } uc_err __stdcall ucs_context_alloc(uc_engine *uc, uc_context **context){ #pragma EXPORT return uc_context_alloc(uc, context); } uc_err __stdcall ucs_free(void *mem){ #pragma EXPORT return uc_free(mem); } uc_err __stdcall ucs_context_save(uc_engine *uc, uc_context *context){ #pragma EXPORT return uc_context_save(uc, context); } uc_err __stdcall ucs_context_restore(uc_engine *uc, uc_context *context){ #pragma EXPORT return uc_context_restore(uc, context); } /* char* asprintf(char* format, ...){ char *ret = 0; if(!format) return 0; va_list args; va_start(args,format); int size = _vscprintf(format, args); if(size > 0){ size++; //for null ret = (char*)malloc(size+2); if(ret) _vsnprintf(ret, size, format, args); } va_end(args); return ret; }*/ #ifdef INCLUDE_DISASM int __stdcall disasm_addr(uc_engine *uc, uint32_t va, char *str, int bufLen){ #pragma EXPORT uint32_t instr_len = 0; int readLen = 15; uint8_t data[32]; INSTRUCTION inst; if(bufLen < 100) return -1; //longest x86 instruction is 15 bytes, what if at the tail end of an allocation? try to read as much as we can.. while(uc_mem_read(uc,va,data,readLen) != 0){ readLen--; if(readLen == 0) return -2; } instr_len = get_instruction(&inst, data, MODE_32); if( instr_len == 0 ) return -3; get_instruction_string(&inst, FORMAT_INTEL, va, str, bufLen); /* if(inst.type == INSTRUCTION_TYPE_JMP || inst.type == INSTRUCTION_TYPE_JMPC){ if(inst.op1.type == OPERAND_TYPE_IMMEDIATE){ if(strlen(str) + 6 < bufLen){ if(getJmpTarget(str) < va){ strcat(str," ^^"); }else{ strcat(str," vv"); } } } }*/ return instr_len; } #endif //maps and write in one shot, auto handles alignment.. uc_err __stdcall mem_write_block(uc_engine *uc, uint64_t address, void* data, uint32_t size, uint32_t perm){ #pragma EXPORT uc_err x; uint64_t base = address; uint32_t sz = size; while(base % 0x1000 !=0){ base--; if(base==0) break; } sz += address-base; //if data starts mid block, we need to alloc more than just size.. while(sz % 0x1000 !=0){ sz++; } x = uc_mem_map(uc, base, sz, perm); if(x) return x; x = uc_mem_write(uc, address, (void*)data, size); if(x) return x; return UC_ERR_OK; } void addStr(_CollectionPtr p , char* str){ _variant_t vv; vv.SetString(str); p->Add( &vv.GetVARIANT() ); } uc_err __stdcall get_memMap(uc_engine *uc, _CollectionPtr *pColl){ #pragma EXPORT uc_mem_region *regions; uint32_t count; char tmp[200]; //max 46 chars used uc_err err = uc_mem_regions(uc, ®ions, &count); if (err != UC_ERR_OK) return err; for (uint32_t i = 0; i < count; i++) { sprintf(tmp,"&h%llx,&h%llx,&h%x", regions[i].begin, regions[i].end, regions[i].perms); addStr(*pColl,tmp); } //free(regions); //https://github.com/unicorn-engine/unicorn/pull/373#issuecomment-271187118 uc_free((void*)regions); return err; } enum op{ op_add = 0, op_sub = 1, op_div = 2, op_mul = 3, op_mod = 4, op_xor = 5, op_and = 6, op_or = 7, op_rsh = 8, op_lsh = 9, op_gt = 10, op_lt = 11, op_gteq = 12, op_lteq = 13 }; unsigned int __stdcall ULong(unsigned int v1, unsigned int v2, int operation){ #pragma EXPORT switch(operation){ case op_add: return v1 + v2; case op_sub: return v1 - v2; case op_div: return v1 / v2; case op_mul: return v1 * v2; case op_mod: return v1 % v2; case op_xor: return v1 ^ v2; case op_and: return v1 & v2; case op_or: return v1 | v2; case op_rsh: return v1 >> v2; case op_lsh: return v1 << v2; case op_gt: return (v1 > v2 ? 1 : 0); case op_lt: return (v1 < v2 ? 1 : 0); case op_gteq: return (v1 >= v2 ? 1 : 0); case op_lteq: return (v1 <= v2 ? 1 : 0); } return -1; }