1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
|
/*
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, uint64_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, uint64_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, uint64_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, uint64_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, uint64_t size){
#pragma EXPORT
return uc_mem_unmap(uc, address, size);
}
uc_err __stdcall ucs_mem_protect(uc_engine *uc, uint64_t address, uint64_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, uint64_t size, uint32_t perm){
#pragma EXPORT
uc_err x;
uint64_t base = address;
uint64_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;
}
|