int32_t env_hook_SHGetSpecialFolderPathA(struct emu_env *env, struct emu_env_hook *hook) { struct emu_cpu *c = emu_cpu_get(env->emu); struct emu_memory *mem = emu_memory_get(env->emu); uint32_t eip_save; POP_DWORD(c, &eip_save); /* CopyBOOL SHGetSpecialFolderPath( HWND hwndOwner, __out LPTSTR lpszPath, __in int csidl, __in BOOL fCreate ); */ uint32_t hwnd; POP_DWORD(c, &hwnd); uint32_t buf; POP_DWORD(c, &buf); uint32_t csidl; POP_DWORD(c, &csidl); uint32_t fCreate; POP_DWORD(c, &fCreate); char buf255[255]; memset(buf255,0,254); GetSHFolderName(csidl, (char*)&buf255); emu_memory_write_block(mem,buf,buf255,strlen(buf255)); emu_cpu_reg32_set(c, eax, 0); if ( env->profile != NULL ) { emu_profile_function_add(env->profile, "SHGetSpecialFolderPath"); emu_profile_argument_add_int(env->profile, "HWND", "hwndOwner", hwnd); emu_profile_argument_add_ptr(env->profile, "LPCSTR", "lpszPath", buf); emu_profile_argument_add_string(env->profile, "", "", buf255); emu_profile_argument_add_int(env->profile, "int", "csidl", csidl); emu_profile_argument_add_int(env->profile, "BOOL", "fCreate", fCreate); emu_profile_function_returnvalue_int_set(env->profile, "BOOL", c->reg[eax]); } emu_cpu_eip_set(c, eip_save); return 0; }
void profile(struct emu_config *conf, struct connection *con, void *data, unsigned int size, unsigned int offset) { struct emu *e = emu_new(); struct emu_env *env = emu_env_new(e); env->profile = emu_profile_new(); // struct emu_cpu *cpu = emu_cpu_get(e); struct emu_memory *mem = emu_memory_get(e); emu_cpu_reg32_set(emu_cpu_get(e), esp, 0x0012fe98); emu_memory_write_block(mem, CODE_OFFSET, data, size); emu_cpu_eip_set(emu_cpu_get(e), CODE_OFFSET + offset); run(e, env); bool needemu = false; struct emu_profile_function *function; for( function = emu_profile_functions_first(env->profile->functions); !emu_profile_functions_istail(function); function = emu_profile_functions_next(function) ) { if( strcmp("recv", function->fnname) == 0 ) { g_message("Can not profile %s, emulating instead", function->fnname); needemu = true; } } if( needemu == true ) { emulate(conf, con, data, size, offset); } else { GString *str = g_string_new(NULL); json_profile_debug(env->profile, str); //printf("%s", str->str); struct incident *i = incident_new("dionaea.module.emu.profile"); incident_value_string_set(i, "profile", str); incident_value_con_set(i, "con", con); connection_ref(con); GAsyncQueue *aq = g_async_queue_ref(g_dionaea->threads->cmds); g_async_queue_push(aq, async_cmd_new(async_incident_report, i)); g_async_queue_unref(aq); ev_async_send(g_dionaea->loop, &g_dionaea->threads->trigger); } emu_env_free(env); emu_free(e); }
struct emu_cpu *emu_cpu_new(struct emu *e) { struct emu_cpu *c = (struct emu_cpu *)malloc(sizeof(struct emu_cpu)); if( c == NULL ) { return NULL; } memset((void *)c, 0, sizeof(struct emu_cpu)); c->emu = e; c->mem = emu_memory_get(e); int i = 1; if( *((uint8_t *)&i) == 1 ) { logDebug(e,"little endian\n"); for( i = 0; i < 8; i++ ) { c->reg16[i] = (uint16_t *)&c->reg[i]; if( i < 4 ) { c->reg8[i] = (uint8_t *)&c->reg[i]; } else { c->reg8[i] = (uint8_t *)&c->reg[i & 3] + 1; } } c->instr.cpu.imm16 = (uint16_t *)((void *)&c->instr.cpu.imm); c->instr.cpu.imm8 = (uint8_t *)&c->instr.cpu.imm; } else { logDebug(e,"big endian\n"); for( i = 0; i < 8; i++ ) { c->reg16[i] = (uint16_t *)&c->reg[i] + 1; if( i < 4 ) { c->reg8[i] = (uint8_t *)&c->reg[i] + 3; } else { c->reg8[i] = (uint8_t *)&c->reg[i & 3] + 2; } } c->instr.cpu.imm16 = (uint16_t *)((unsigned char *)&c->instr.cpu.imm + 1); c->instr.cpu.imm8 = (uint8_t *)&c->instr.cpu.imm + 3; } c->instr_string = (char *)malloc(92); c->repeat_current_instr = false; init_prefix_map(); return c; }
void __cdecl main(void){ e = emu_new(); cpu = emu_cpu_get(e); mem = emu_memory_get(e); env = emu_env_new(e); //emu_log_level_set( emu_logging_get(e), EMU_LOG_DEBUG); if ( env == 0 ){ printf("%s\n%s\n", emu_strerror(e), strerror(emu_errno(e))); exit(-1);} int i = 0; void* stack; int stacksz; // 0 1 2 3 4 5 6 7 int regs[] = {0, 0, 0, 0, 0x12fe00,0x12fff0 ,0, 0}; //*regm[] = {"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"}; for (i=0;i<8;i++) cpu->reg[(emu_reg32)i] = regs[i]; stacksz = regs[ebp] - regs[esp] + 500; stack = malloc(stacksz); memset(stack, 0, stacksz); //printf("writing initial stack space\n"); emu_memory_write_block(mem, regs[esp] - 250, stack, stacksz); emu_env_w32_export_new_hook(env, "LoadLibraryA", new_user_hook_LoadLibraryA, NULL); /* 00436A32 B8 00000000 MOV EAX,0 00436A37 40 INC EAX 00436A3D 68 6C333200 PUSH 32336C 00436A42 68 7368656C PUSH 6C656873 00436A47 54 PUSH ESP 00436A48 68 771D807C PUSH 7C801D77 00436A4D 59 POP ECX 00436A4E FFD1 CALL ECX 686C333200687368656C5468771D807C59FFD1 */ unsigned char shellcode[20] = { 0x68, 0x6C, 0x33, 0x32, 0x00, 0x68, 0x73, 0x68, 0x65, 0x6C, 0x54, 0x68, 0x77, 0x1D, 0x80, 0x7C, 0x59, 0xFF, 0xD1, 0xCC }; //write shellcode to memory emu_memory_write_block(mem, 0x401000, shellcode, 20); emu_cpu_eip_set(cpu, 0x401000); system("cls"); int step=0; char* buf = (char*)malloc(100); while(1){ struct emu_env_w32_dll_export *ex = NULL; ex = emu_env_w32_eip_check(env); if(ex != NULL){ //eip was found to be the start address of some dll export if(ex->fnhook == NULL){ //if fnhook != 0 then handler was executed by library already printf("Unhooked call to api %s\n", ex->fnname); //can insert generic handler here break; } } else{ emu_disasm_addr(cpu, cpu->eip, buf); printf("%x\t%s\n", cpu->eip, buf); if( emu_cpu_parse(cpu) == -1){ printf("step %d parse failed error: %s", step, emu_strerror(e)); break; } if( emu_cpu_step(cpu) == -1){ printf("step %d step failed error: %s", step, emu_strerror(e)); break; } step++; } } printf("Run complete step=%d eax is: %x\n\n", step, cpu->reg[eax]); getch(); }
int32_t env_linux_hook_execve(struct emu_env *env, struct emu_env_hook *hook) { printf("execve\n"); struct emu_cpu *c = emu_cpu_get(env->emu); struct emu_string *name = emu_string_new(); emu_memory_read_string(emu_memory_get(c->emu), c->reg[ebx], name, 255); if ( env->profile != NULL ) { emu_profile_function_add(env->profile, "execve"); emu_profile_argument_add_ptr(env->profile, "const char *", "dateiname", c->reg[ebx]); emu_profile_argument_add_string(env->profile, "", "", emu_string_char(name)); // emu_profile_argument_add_ptr(env->profile, "", "", c->reg[ecx]); emu_profile_argument_array_start(env->profile, "const char *", "argv[]"); } uint32_t p_array = c->reg[ecx]; uint32_t p_arg = -1; emu_memory_read_dword(emu_memory_get(c->emu), p_array, &p_arg); int i=1; // char **argv = NULL; while (p_arg != 0) { // argv = realloc(argv, (i+1) * sizeof(char *)); // argv[i] = NULL; struct emu_string *arg = emu_string_new(); emu_memory_read_string(emu_memory_get(c->emu), p_arg, arg, 128); // argv[i-1] = strdup(emu_string_char(arg)); if( emu_string_char(arg) == NULL ) { emu_string_free(arg); break; } if ( env->profile != NULL ) { emu_profile_argument_add_ptr(env->profile, "", "", p_array+((i-1)*4)); emu_profile_argument_add_ptr(env->profile, "", "", p_arg); emu_profile_argument_add_string(env->profile, "", "", emu_string_char(arg)); } emu_string_free(arg); emu_memory_read_dword(emu_memory_get(c->emu), p_array+(i*4), &p_arg); i++; } if ( env->profile != NULL ) { emu_profile_argument_add_ptr(env->profile, "", "", p_arg); emu_profile_argument_add_none(env->profile); // printf("arg is %s\n", emu_string_char(arg)); emu_profile_argument_array_end(env->profile); emu_profile_argument_add_ptr(env->profile, "const char *", "envp[]", c->reg[edx]); emu_profile_argument_add_none(env->profile); emu_profile_function_returnvalue_int_set(env->profile, "int", 0); } printf("int execve (const char *dateiname=%08x={%s}, const char * argv[], const char *envp[]);\n", c->reg[ebx], emu_string_char(name)); emu_string_free(name); return 0; }
int32_t env_linux_hook_socketcall(struct emu_env *env, struct emu_env_hook *hook) { struct emu_cpu *c = emu_cpu_get(env->emu); #define AL(x) (x) static unsigned char nargs[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), AL(3),AL(3),AL(4),AL(4),AL(4),AL(6), AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)}; #undef AL uint32_t a[6]; int i; for ( i=0;i<nargs[c->reg[ebx]];i++ ) { emu_memory_read_dword(emu_memory_get(c->emu),c->reg[ecx]+4*i,a+i); } uint32_t returnvalue = 0; switch ( c->reg[ebx] ) { case 1: // SYS_SOCKET printf("int socket(int domain=%i, int type=%i, int protocol=%i);\n", a[0], a[1], a[2]); if (hook->hook.lin->userhook != NULL) returnvalue = hook->hook.lin->userhook(env, hook, a[0], a[1], a[2]); else returnvalue = 14; if ( env->profile != NULL ) { emu_profile_function_add(env->profile, "socket"); emu_profile_argument_add_int(env->profile, "int", "domain", a[0]); emu_profile_argument_add_int(env->profile, "int", "type", a[1]); emu_profile_argument_add_int(env->profile, "int", "protocol", a[2]); emu_profile_function_returnvalue_int_set(env->profile, "int", returnvalue); } emu_cpu_reg32_set(c, eax, returnvalue); break; case 2: // SYS_BIND { /* printf("int bind(int sockfd=%i, struct sockaddr *my_addr=%08x={host %s port %i}, int addrlen);\n", a[0], a[1], inet_ntoa(*(struct in_addr *)&((struct sockaddr_in *)&sa)->sin_addr), ntohs(((struct sockaddr_in *)&sa)->sin_port) ); */ struct sockaddr sa; memset(&sa, 0, sizeof(struct sockaddr)); emu_memory_read_block(emu_memory_get(c->emu), a[1], &sa, sizeof(struct sockaddr)); if (hook->hook.lin->userhook != NULL) returnvalue = hook->hook.lin->userhook(env, hook, a[0], &sa, a[2]); else returnvalue = 0; if (env->profile != NULL) { emu_profile_function_add(env->profile, "bind"); emu_profile_argument_add_int(env->profile, "int", "sockfd", a[0]); emu_profile_argument_add_sockaddr_ptr(env->profile, "my_addr", a[1], sa); emu_profile_argument_add_int(env->profile, "int", "addrlen", a[2]); emu_profile_function_returnvalue_int_set(env->profile, "int", returnvalue); } emu_cpu_reg32_set(c, eax, returnvalue); } break; case 3: // SYS_CONNECT { printf("connect\n"); struct sockaddr sa; memset(&sa, 0, sizeof(struct sockaddr)); emu_memory_read_block(emu_memory_get(c->emu), a[1], &sa, sizeof(struct sockaddr)); if (hook->hook.lin->userhook != NULL) returnvalue = hook->hook.lin->userhook(env, hook, a[0], &sa, a[2]); else returnvalue = 0; if (env->profile != NULL) { emu_profile_function_add(env->profile, "connect"); emu_profile_argument_add_int(env->profile, "int", "sockfd", a[0]); emu_profile_argument_add_sockaddr_ptr(env->profile, "serv_addr", a[1], sa); emu_profile_argument_add_int(env->profile, "int", "addrlen", a[2]); emu_profile_function_returnvalue_int_set(env->profile, "int", returnvalue); } emu_cpu_reg32_set(c, eax, returnvalue); } break; case 4: // SYS_LISTEN printf("int listen(int s=%i, int backlog=%i);\n", a[0], a[1]); if (hook->hook.lin->userhook != NULL) returnvalue = hook->hook.lin->userhook(env, hook, a[0], a[1]); else returnvalue = 0; if (env->profile != NULL) { emu_profile_function_add(env->profile, "listen"); emu_profile_argument_add_int(env->profile, "int", "s", a[0]); emu_profile_argument_add_int(env->profile, "int", "backlog", a[1]); emu_profile_function_returnvalue_int_set(env->profile, "int", returnvalue); } emu_cpu_reg32_set(c, eax, returnvalue); break; case 5: // SYS_ACCEPT printf("int accept(int s=%i, struct sockaddr *addr=%08x, int *addrlen=%08x);\n", a[0], a[1], a[2]); struct sockaddr sa; memset(&sa, 0, sizeof(struct sockaddr)); emu_memory_read_block(emu_memory_get(c->emu), a[1], &sa, sizeof(struct sockaddr)); if (hook->hook.lin->userhook != NULL) returnvalue = hook->hook.lin->userhook(env, hook, a[0], &sa, a[2]); else returnvalue = 19; if (env->profile != NULL) { emu_profile_function_add(env->profile, "accept"); emu_profile_argument_add_int(env->profile, "int", "sockfd", a[0]); emu_profile_argument_add_ptr(env->profile, "sockaddr_in *", "addr", a[1]); emu_profile_argument_add_none(env->profile); emu_profile_argument_add_ptr(env->profile, "int", "addrlen", a[2]); emu_profile_argument_add_none(env->profile); emu_profile_function_returnvalue_int_set(env->profile, "int", returnvalue); } emu_cpu_reg32_set(c, eax, returnvalue); break; case 6: // SYS_GETSOCKNAME printf("sys_getsockname(2)\n"); break; case 7: // SYS_GETPEERNAME printf("sys_getpeername(2)\n"); break; case 8: // SYS_SOCKETPAIR printf("sys_socketpair(2)\n"); break; case 9: // SYS_SEND printf("sys_send(2)\n"); break; case 10: // SYS_RECV printf("sys_recv(2)\n"); break; case 11: // SYS_SENDTO printf("sys_sendto(2)\n"); break; case 12: // SYS_RECVFROM printf("sys_recvfrom(2)\n"); break; case 13: // SYS_SHUTDOWN printf("sys_shutdown(2)\n"); break; case 14: // SYS_SETSOCKOPT printf("sys_setsockopt(2)\n"); break; case 15: // SYS_GETSOCKOPT printf("sys_getsockopt(2)\n"); break; case 16: // SYS_SENDMSG printf("sys_sendmsg(2)\n"); break; case 17: // SYS_RECVMSG printf("sys_recvmsg(2)\n"); break; default: printf("syscall %i (%x) unknown", c->reg[ebx], c->reg[ebx]); } return 0; }
Emulator_LibEmu::Emulator_LibEmu() { e = emu_new(); cpu = emu_cpu_get(e); mem = emu_memory_get(e); im_base=0; }
int32_t emu_shellcode_test(struct emu *e, uint8_t *data, uint16_t size) { logPF(e); /* bool found_good_candidate_after_getpc = false; uint32_t best_eip=0; */ // This check avoids a floating point exception further down the line if(size < 2) { return -1; } uint32_t offset; struct emu_list_root *el; el = emu_list_create(); for ( offset=0; offset<size ; offset++ ) { if ( emu_getpc_check(e, (uint8_t *)data, size, offset) != 0 ) { logDebug(e, "possible getpc at offset %i (%08x)\n", offset, offset); struct emu_list_item *eli = emu_list_item_create(); eli->uint32 = offset; emu_list_insert_last(el, eli); } } if ( emu_list_length(el) == 0 ) { emu_list_destroy(el); return -1; } { struct emu_cpu *cpu = emu_cpu_get(e); struct emu_memory *mem = emu_memory_get(e); /* write the code to the offset */ emu_memory_write_block(mem, STATIC_OFFSET, data, size); /* set the registers to the initial values */ int reg; for ( reg=0;reg<8;reg++ ) emu_cpu_reg32_set(cpu,reg ,0x0); emu_cpu_reg32_set(cpu, esp, 0x00120000); emu_cpu_eip_set(cpu, 0); /* set the flags */ emu_cpu_eflags_set(cpu,0x0); } struct emu_track_and_source *etas = emu_track_and_source_new(); logDebug(e, "creating static callgraph\n"); /* create the static analysis graph set memory read only so instructions can't change it*/ emu_memory_mode_ro(emu_memory_get(e)); emu_source_instruction_graph_create(e, etas, STATIC_OFFSET, size); emu_memory_mode_rw(emu_memory_get(e)); struct emu_hashtable *eh = emu_hashtable_new(size+4/4, emu_hashtable_ptr_hash, emu_hashtable_ptr_cmp); struct emu_list_item *eli; // struct emu_env_w32 *env = emu_env_w32_new(e); struct emu_list_root *results = emu_list_create(); for ( eli = emu_list_first(el); !emu_list_attail(eli); eli = emu_list_next(eli) ) { logDebug(e, "testing offset %i %08x\n", eli->uint32, eli->uint32); emu_shellcode_run_and_track(e, data, size, eli->uint32, 256, etas, eh, results, false); } /* for all positions we got, take the best, maybe take memory access into account later */ emu_list_qsort(results, tested_positions_cmp); if ( ((struct emu_stats *)emu_list_first(results)->data)->cpu.steps != 256 ) { emu_hashtable_free(eh); eh = emu_hashtable_new(size+4/4, emu_hashtable_ptr_hash, emu_hashtable_ptr_cmp); logDebug(e, "brute force!\n"); struct emu_list_root *new_results = emu_list_create(); for ( eli = emu_list_first(results); !emu_list_attail(eli); eli = emu_list_next(eli) ) { struct emu_stats *es = (struct emu_stats *)eli->data; logDebug(e, "brute at offset 0x%08x \n",es->eip - STATIC_OFFSET); emu_shellcode_run_and_track(e, data, size, es->eip - STATIC_OFFSET, 256, etas, eh, new_results, true); } emu_list_concat(results, new_results); emu_list_destroy(new_results); emu_list_qsort(results, tested_positions_cmp); /* uniq */ for ( eli = emu_list_first(results); !emu_list_attail(eli); eli = emu_list_next(eli) ) { struct emu_list_item *next = emu_list_next(eli); if (!emu_list_attail(next) && ((struct emu_stats *)eli->data)->eip == ((struct emu_stats *)next->data)->eip ) { emu_stats_free(next->data); emu_list_remove(next); free(next); } } } emu_hashtable_free(eh); emu_list_destroy(el); // emu_env_w32_free(env); emu_track_and_source_free(etas); { struct emu_list_item *eli; for ( eli = emu_list_first(results); !emu_list_attail(eli); eli = emu_list_next(eli) ) { struct emu_stats *es = (struct emu_stats *)eli->data; logDebug(e, "b offset 0x%08x steps %i\n",es->eip, es->cpu.steps); } } eli = emu_list_first(results); struct emu_stats *es = (struct emu_stats *)eli->data; if ( es->cpu.steps > 100 ) { offset = es->eip; } else { offset = -1; } for (eli = emu_list_first(results); !emu_list_attail(eli); eli = emu_list_next(eli)) { emu_stats_free((struct emu_stats *)eli->data); } emu_list_destroy(results); return offset - STATIC_OFFSET; }
/** * This function takes the emu, the offset and tries to run * steps iterations. If it fails due to uninitialized * registers/eflags it will try to use the information passed by * etas to traverse the instruction tree and find an instruction * path in the tree which satisfies the initialization * requirements. * * To avoid testing the same positions over and over, the * already-tested positions are held in the known_positions * hashtable. * * The result is returned in the tested_positions_list. * * * The function is called for every GetPC candidate in the * suspected shellcode, therefore the known_positions prevent * testing the same locations for different starting points in * the data too. * * It is the vital part of libemu's shellcode detection, hard to * understand, hard to improve and hard to fix. * * Messing this function up, destroys libemu's shellcode * detection. * * The function is a mess, given the complexity of the loops it * is too long and the variable names do not provide any help. * * The bruteforce flag is useful, as it allows bfs even if some * instructions initializations are not set properly, but you'll * definitely miss its impact on the behaviour when making a * guess why something does not work. * * short explanation of the logic: * * the first starting point is our offset * * while we have starting points: * run the shellcode: error? * no! * continue * yes! * use the current starting eip as starting point to bfs * look for instructions which satisfy the requirements OR * brutefore * queue these new instructions as starting points * * * * History has proven the the function to be susceptible to * denial of service attacks, running the system out of memory * or cycles. * So, if you experience a 'problem', this is the first place to * look at, and the last one you want to look at, if you want to * cause a 'problem', same rules apply. * * This comment was written when patching one of these dos * problems * The known_positions arg has been unused for the time before, * seems like there was a good idea when writing the function * initially, but this good idea was abandoned once 'it worked' * * * * @param e the emu to run * @param data the data we run * @param datasize the data size * @param eipoffset the offset for eip * @param steps how many steps to try running * @param etas the track and source tree - the substantial * information to run the breath first search * @param known_positions * already tested positions * @param stats_tested_positions_list * the result list * @param brute_force * be aggressive? * * @return */ int32_t emu_shellcode_run_and_track(struct emu *e, uint8_t *data, uint16_t datasize, uint16_t eipoffset, uint32_t steps, // struct emu_env_w32 *env, struct emu_track_and_source *etas, struct emu_hashtable *known_positions, struct emu_list_root *stats_tested_positions_list, bool brute_force ) { struct emu_cpu *cpu = emu_cpu_get(e); struct emu_memory *mem = emu_memory_get(e); struct emu_queue *eq = emu_queue_new(); emu_queue_enqueue(eq, (void *)((uintptr_t)(uint32_t)eipoffset+STATIC_OFFSET)); // struct emu_list_root *tested_positions = emu_list_create(); struct emu_env *env = NULL; { // mark all vertexes white struct emu_vertex *x; for ( x= emu_vertexes_first(etas->static_instr_graph->vertexes); !emu_vertexes_attail(x); x = emu_vertexes_next(x)) { x->color = white; } } while ( !emu_queue_empty(eq) ) { uint32_t current_offset = (uint32_t)(uintptr_t)emu_queue_dequeue(eq); /* init the cpu/memory * scooped to keep number of used varnames small */ { logDebug(e, "running at offset %i %08x\n", current_offset, current_offset); emu_memory_clear(mem); if (env) emu_env_free(env); /* write the code to the offset */ emu_memory_write_block(mem, STATIC_OFFSET, data, datasize); env = emu_env_new(e); /* set the registers to the initial values */ int reg; for ( reg=0;reg<8;reg++ ) emu_cpu_reg32_set(cpu,reg ,0x0); emu_cpu_reg32_set(cpu, esp, 0x00120000); emu_cpu_eip_set(cpu, current_offset); /* set the flags */ emu_cpu_eflags_set(cpu,0x0); cpu->tracking = etas; } emu_tracking_info_clear(&etas->track); int j; for ( j=0;j<steps;j++ ) { // emu_cpu_debug_print(cpu); uint32_t eipsave; eipsave = emu_cpu_eip_get(cpu); struct emu_env_hook *hook = NULL; hook = emu_env_w32_eip_check(env); if ( hook != NULL ) { if ( hook->hook.win->fnhook == NULL ) break; } else { int32_t ret = emu_cpu_parse(emu_cpu_get(e)); if ( ret == -1 ) { logDebug(e, "error at %s\n", cpu->instr_string); break; } ret = emu_cpu_step(emu_cpu_get(e)); if ( ret == -1 ) { logDebug(e, "error at %s (%s)\n", cpu->instr_string, strerror(emu_errno(e))); if (brute_force) { logDebug(e, "goto traversal\n"); goto traversal; } else break; } if ( emu_track_instruction_check(e, etas) == -1 ) { traversal: logDebug(e, "failed instr %s\n", cpu->instr_string); logDebug(e, "tracking at eip %08x\n", eipsave); if ( 0 && cpu->instr.is_fpu ) { } else { /* save the requirements of the failed instruction */ // struct emu_tracking_info *instruction_needs_ti = emu_tracking_info_new(); // emu_tracking_info_copy(&cpu->instr.cpu.track.need, instruction_needs_ti); struct emu_queue *bfs_queue = emu_queue_new(); /* * the current starting point is the first position used to bfs * scooped to avoid varname collisions */ { struct emu_tracking_info *eti = emu_tracking_info_new(); emu_tracking_info_diff(&cpu->instr.track.need, &etas->track, eti); eti->eip = current_offset; emu_tracking_info_debug_print(eti); if( emu_hashtable_search(known_positions, (void *)(uintptr_t)(uint32_t)current_offset) != NULL) { logDebug(e, "Known %p %x\n", eti, eti->eip); emu_tracking_info_free(eti); emu_queue_free(bfs_queue); break; } emu_queue_enqueue(bfs_queue, eti); } while ( !emu_queue_empty(bfs_queue) ) { logDebug(e, "loop %s:%i\n", __FILE__, __LINE__); struct emu_tracking_info *current_pos_ti_diff = (struct emu_tracking_info *)emu_queue_dequeue(bfs_queue); struct emu_hashtable_item *current_pos_ht = emu_hashtable_search(etas->static_instr_table, (void *)(uintptr_t)(uint32_t)current_pos_ti_diff->eip); if (current_pos_ht == NULL) { logDebug(e, "current_pos_ht is NULL?\n"); exit(-1); } struct emu_vertex *current_pos_v = (struct emu_vertex *)current_pos_ht->value; struct emu_source_and_track_instr_info *current_pos_satii = (struct emu_source_and_track_instr_info *)current_pos_v->data; if( emu_hashtable_search(known_positions, (void *)(uintptr_t)(uint32_t)current_pos_satii->eip) != NULL ) { logDebug(e, "Known Again %p %x\n", current_pos_satii, current_pos_satii->eip); current_pos_v->color = red; emu_tracking_info_free(current_pos_ti_diff); continue; } if (current_pos_v->color == red) { logDebug(e, "is red %p %x: %s\n", (uintptr_t)current_pos_v, current_pos_satii->eip, current_pos_satii->instrstring); emu_tracking_info_free(current_pos_ti_diff); continue; } logDebug(e, "marking red %p %x: %s \n", (uintptr_t)current_pos_v, current_pos_satii->eip, current_pos_satii->instrstring); current_pos_v->color = red; emu_hashtable_insert(known_positions, (void *)(uintptr_t)(uint32_t)current_pos_satii->eip, NULL); while ( !emu_tracking_info_covers(¤t_pos_satii->track.init, current_pos_ti_diff) || brute_force ) { logDebug(e, "loop %s:%i\n", __FILE__, __LINE__); if ( current_pos_v->backlinks == 0 ) { break; } else if ( current_pos_v->backlinks > 1 ) { /* queue all to diffs to the bfs queue */ struct emu_edge *ee; struct emu_vertex *ev; for ( ee = emu_edges_first(current_pos_v->backedges); !emu_edges_attail(ee); ee=emu_edges_next(ee) ) { ev = ee->destination; /** * ignore positions we've visited already * avoids dos for jz 0 * * try the next position instead */ if( ev->color == red ) continue; struct emu_source_and_track_instr_info *next_pos_satii = (struct emu_source_and_track_instr_info *)ev->data; logDebug(e, "EnqueueLoop %p %x %s\n", next_pos_satii, next_pos_satii->eip, next_pos_satii->instrstring); struct emu_tracking_info *eti = emu_tracking_info_new(); emu_tracking_info_diff(current_pos_ti_diff, ¤t_pos_satii->track.init, eti); eti->eip = next_pos_satii->eip; emu_queue_enqueue(bfs_queue, eti); } /** * the new possible positions and requirements got queued into the bfs queue, * we break here, so the new queued positions can try to work it out */ break; } else if ( current_pos_v->backlinks == 1 ) { /* follow the single link */ /** * ignore loops to self * avoids dos for "\xe3\xfe\xe8" * breaks the upper loop * */ if( current_pos_v == emu_edges_first(current_pos_v->backedges)->destination ) break; current_pos_v = emu_edges_first(current_pos_v->backedges)->destination; /** * again, ignore already visited positions * breaks the upper loop */ if( current_pos_v->color == red ) break; current_pos_v->color = red; struct emu_source_and_track_instr_info *next_pos_satii = (struct emu_source_and_track_instr_info *)current_pos_v->data; logDebug(e, "FollowSingle %p %i %x %s\n", next_pos_satii, current_pos_v->color, next_pos_satii->eip, next_pos_satii->instrstring); current_pos_satii = (struct emu_source_and_track_instr_info *)current_pos_v->data; emu_tracking_info_diff(current_pos_ti_diff, ¤t_pos_satii->track.init, current_pos_ti_diff); } } if ( emu_tracking_info_covers(¤t_pos_satii->track.init, current_pos_ti_diff) || brute_force ) { /** * we have a new starting point, this starting point may fail * too - if further backwards traversal is required * therefore we mark it white, so it can be processed again */ logDebug(e, "found position which satiesfies the requirements %i %08x\n", current_pos_satii->eip, current_pos_satii->eip); current_pos_ht = emu_hashtable_search(etas->static_instr_table, (void *)(uintptr_t)(uint32_t)current_pos_satii->eip); current_pos_v = (struct emu_vertex *)current_pos_ht->value; if(current_pos_satii->eip != current_offset ) { logDebug(e, "marking white %p %x: %s \n", (uintptr_t)current_pos_v, current_pos_satii->eip, current_pos_satii->instrstring); current_pos_v->color = white; } emu_tracking_info_debug_print(¤t_pos_satii->track.init); emu_queue_enqueue(eq, (void *)((uintptr_t)(uint32_t)current_pos_satii->eip)); } //discard: emu_tracking_info_free( current_pos_ti_diff); } emu_queue_free(bfs_queue); } /** * the shellcode did not run correctly as he was missing instructions initializing required registers * we did what we could do in the prev lines of code to find better offsets to start from * the working offsets got queued into the main queue, so we break here to give them a chance */ break; }else { logDebug(e, "%s\n", cpu->instr_string); } } } struct emu_stats *es = emu_stats_new(); es->eip = current_offset; es->cpu.steps = j; struct emu_list_item *eli = emu_list_item_create(); eli->data = es; logDebug(e, "INSERT %i %x steps %i\n", current_offset, current_offset, j); emu_list_insert_last(stats_tested_positions_list, eli); } emu_queue_free(eq); emu_env_free(env); /* sort all tested positions by the number of steps ascending */ emu_list_qsort(stats_tested_positions_list, tested_positions_cmp); struct emu_list_item *eli = emu_list_first(stats_tested_positions_list); struct emu_stats *es = (struct emu_stats *)eli->data; uint32_t best_offset = es->eip; return best_offset - STATIC_OFFSET; }
void emulate(struct emu_config *conf, struct connection *con, void *data, unsigned int size, unsigned int offset) { struct emu_emulate_ctx *ctx = g_malloc0(sizeof(struct emu_emulate_ctx)); ctx->config = conf; ctx->sockets = g_hash_table_new(g_int_hash, g_int_equal); ctx->processes = g_hash_table_new(g_int_hash, g_int_equal); ctx->files = g_hash_table_new(g_int_hash, g_int_equal); ctx->ctxcon = con; if( con ) connection_ref(ctx->ctxcon); ctx->emu = emu_new(); ctx->env = emu_env_new(ctx->emu); struct emu_env * env = ctx->env; struct emu *e = ctx->emu; struct emu_cpu *cpu = emu_cpu_get(ctx->emu); ctx->env->userdata = ctx; g_mutex_init(&ctx->mutex); ctx->serial = 67; emu_env_w32_load_dll(env->env.win,"ws2_32.dll"); emu_ll_w32_export_hook(env, "accept", ll_win_hook_accept, NULL); emu_env_w32_export_hook(env, "bind", user_hook_bind, NULL); emu_env_w32_export_hook(env, "closesocket", user_hook_close, NULL); emu_env_w32_export_hook(env, "connect", user_hook_connect, NULL); emu_env_w32_export_hook(env, "listen", user_hook_listen, NULL); emu_ll_w32_export_hook(env, "recv", ll_win_hook_recv, NULL); emu_env_w32_export_hook(env, "send", user_hook_send, NULL); emu_env_w32_export_hook(env, "socket", user_hook_socket, NULL); emu_env_w32_export_hook(env, "WSASocketA", user_hook_WSASocket, NULL); emu_env_w32_export_hook(env, "CreateProcessA", user_hook_CreateProcess, NULL); emu_env_w32_export_hook(env, "WaitForSingleObject", user_hook_WaitForSingleObject, NULL); emu_env_w32_export_hook(env, "CreateFileA", user_hook_CreateFile, NULL); emu_env_w32_export_hook(env, "WriteFile", user_hook_WriteFile, NULL); emu_env_w32_export_hook(env, "CloseHandle", user_hook_CloseHandle, NULL); emu_env_w32_export_hook(env, "_lcreat", user_hook__lcreat, NULL); emu_env_w32_export_hook(env, "_lwrite", user_hook__lwrite, NULL); emu_env_w32_export_hook(env, "_lclose", user_hook__lclose, NULL); // emu_env_linux_syscall_hook(env, "exit", user_hook_exit, NULL); // emu_env_linux_syscall_hook(env, "socket", user_hook_socket, NULL); // emu_env_linux_syscall_hook(env, "bind", user_hook_bind, NULL); // emu_env_linux_syscall_hook(env, "listen", user_hook_listen, NULL); // emu_env_linux_syscall_hook(env, "accept", user_hook_accept, NULL); #define CODE_OFFSET 0x417000 int j; for( j=0; j<8; j++ ) emu_cpu_reg32_set(cpu,j , 0); // set flags emu_cpu_eflags_set(cpu, 0); // write code to offset emu_memory_write_block(emu_memory_get(ctx->emu), CODE_OFFSET, data, size); // set eip to code emu_cpu_eip_set(emu_cpu_get(e), CODE_OFFSET + offset); emu_cpu_reg32_set(emu_cpu_get(e), esp, 0x0012fe98); emulate_thread(NULL, ctx); }
int32_t env_w32_hook_fwrite(struct emu_env *env, struct emu_env_hook *hook) { logDebug(env->emu, "Hook me Captain Cook!\n"); logDebug(env->emu, "%s:%i %s\n",__FILE__,__LINE__,__FUNCTION__); struct emu_cpu *c = emu_cpu_get(env->emu); uint32_t eip_save; POP_DWORD(c, &eip_save); /* size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream ); */ uint32_t p_buffer; MEM_DWORD_READ(c, c->reg[esp], &p_buffer); uint32_t size; MEM_DWORD_READ(c, (c->reg[esp]+4), &size); uint32_t count; MEM_DWORD_READ(c, (c->reg[esp]+8), &count); unsigned char *buffer = malloc(size*count); emu_memory_read_block(emu_memory_get(env->emu), p_buffer, buffer, size*count); uint32_t p_stream; MEM_DWORD_READ(c, c->reg[esp]+12, &p_stream); uint32_t returnvalue; if ( hook->hook.win->userhook != NULL ) { returnvalue = hook->hook.win->userhook(env, hook, buffer, size, count, p_stream); }else { returnvalue = size*count; } logDebug(env->emu, "fwrite(0x%08x, %d, %d, 0x%08x)\n", p_buffer, size, count, p_stream); emu_cpu_reg32_set(c, eax, returnvalue); if ( env->profile != NULL ) { emu_profile_function_add(env->profile, "fwrite"); emu_profile_function_returnvalue_int_set(env->profile, "size_t", returnvalue); emu_profile_argument_add_ptr(env->profile, "const void *", "buffer", p_buffer); emu_profile_argument_add_bytea(env->profile, "", "", buffer, size*count); emu_profile_argument_add_int(env->profile, "size_t", "size", size); emu_profile_argument_add_int(env->profile, "count_t", "count", count); emu_profile_argument_add_ptr(env->profile, "FILE *", "stream", p_stream); emu_profile_argument_add_none(env->profile); } free(buffer); emu_cpu_eip_set(c, eip_save); return 0; }