unicorn/bindings/vb6/main.cpp

459 lines
12 KiB
C++

/*
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 <david.zimmer@fireeye.com>, <dzzie@yahoo.com>
License: Apache 2.0
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 <io.h>
#include <windows.h>
#ifdef _WIN64
#error vb6 is 32bit only
#endif
#include <unicorn/unicorn.h>
#pragma comment(lib, "unicorn.lib")
//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 <libdasm/libdasm.h>
#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, &regions, &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;
}