int32_t env_linux_hook_chmod(struct emu_env *env, struct emu_env_hook *hook) { printf("sys_chmod(2)\n"); struct emu_cpu *c = emu_cpu_get(env->emu); emu_profile_function_add(env->profile, "chmod"); if (hook->hook.lin->userhook != NULL) { uint32_t r = hook->hook.lin->userhook(env, hook, c->reg[ebx]); emu_cpu_reg32_set(c, eax, r); }else emu_cpu_reg32_set(c, eax, 0); return 0; }
int32_t env_linux_hook_exit(struct emu_env *env, struct emu_env_hook *hook) { printf("sys_exit(2)\n"); struct emu_cpu *c = emu_cpu_get(env->emu); emu_profile_function_add(env->profile, "exit"); emu_profile_argument_add_int(env->profile, "int", "status", c->reg[ebx]); if (hook->hook.lin->userhook != NULL) { uint32_t r = hook->hook.lin->userhook(env, hook, c->reg[ebx]); emu_cpu_reg32_set(c, eax, r); }else emu_cpu_reg32_set(c, eax, 0); return 0; }
int32_t env_linux_hook_fork(struct emu_env *env, struct emu_env_hook *hook) { printf("sys_fork(2)\n"); struct emu_cpu *c = emu_cpu_get(env->emu); emu_profile_function_add(env->profile, "fork"); emu_cpu_reg32_set(c, eax, 4711); return 0; }
void Emulator_LibEmu::begin(char* & buf, const uint len, const uint entry_point, const uint base) { im_base=base; for (int i=0; i<8; i++) { emu_cpu_reg32_set(cpu, (emu_reg32) i, 0); } emu_cpu_reg32_set(cpu, esp, int(im_base/2)); emu_memory_clear(mem); emu_memory_write_block(mem, im_base, (void*)buf, len); /* int i=0; for (i=0; i<len; i++){ emu_memory_write_byte(mem, 0x20000000L+i, buf[i]); } */ emu_memory_write_byte(mem, im_base+len, '\xcc'); jump(entry_point); }
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); }
int32_t env_linux_hook_dup2(struct emu_env *env, struct emu_env_hook *hook) { struct emu_cpu *c = emu_cpu_get(env->emu); printf("int dup2(int oldfd=%i, int newfd=%i);\n", c->reg[ebx], c->reg[ecx]); if ( env->profile != NULL ) { emu_profile_function_add(env->profile, "dup2"); emu_profile_argument_add_int(env->profile, "int", "oldfd", c->reg[ebx]); emu_profile_argument_add_int(env->profile, "int", "newfd", c->reg[ecx]); emu_profile_function_returnvalue_int_set(env->profile, "int", c->reg[ecx]); } emu_cpu_reg32_set(c, eax, c->reg[ecx]); return 0; }
int32_t env_w32_hook_fclose(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); /* int _fcloseall( void ); int fclose( FILE *stream ); */ uint32_t p_stream; MEM_DWORD_READ(c, c->reg[esp], &p_stream); logDebug(env->emu, "fclose(0x%08x)\n", p_stream); emu_cpu_reg32_set(c, eax, 0); if (env->profile != NULL) { emu_profile_function_add(env->profile, "fclose"); emu_profile_argument_add_ptr(env->profile, "FILE *", "stream", p_stream); emu_profile_argument_add_none(env->profile); emu_profile_function_returnvalue_int_set(env->profile, "int", 0); } emu_cpu_eip_set(c, eip_save); 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; }
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; }
int32_t env_w32_hook_fopen(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); /* FILE *fopen( const char *filename, const char *mode ); FILE *_wfopen( const wchar_t *filename, const wchar_t *mode ); */ uint32_t p_filename; MEM_DWORD_READ(c, c->reg[esp], &p_filename); struct emu_string *filename = emu_string_new(); emu_memory_read_string(c->mem, p_filename, filename, 512); uint32_t p_mode; MEM_DWORD_READ(c, c->reg[esp]+4, &p_mode); struct emu_string *mode = emu_string_new(); emu_memory_read_string(c->mem, p_mode, mode, 512); // printf("fopen(%s, %s)\n", emu_string_char(filename), (char *)mode->data); uint32_t returnvalue; if ( hook->hook.win->userhook != NULL ) { returnvalue = hook->hook.win->userhook(env, hook, emu_string_char(filename), emu_string_char(mode)); }else { returnvalue = 0x89898989; } emu_cpu_reg32_set(c, eax, returnvalue); if (env->profile != NULL) { emu_profile_function_add(env->profile, "fopen"); emu_profile_argument_add_ptr(env->profile, "const char *", "filename", p_filename); emu_profile_argument_add_string(env->profile, "", "", emu_string_char(filename)); emu_profile_argument_add_ptr(env->profile, "const char *", "mode", p_mode); emu_profile_argument_add_string(env->profile, "", "", emu_string_char(mode)); emu_profile_function_returnvalue_ptr_set(env->profile, "FILE *", returnvalue); emu_profile_argument_add_none(env->profile); } emu_string_free(filename); emu_string_free(mode); emu_cpu_eip_set(c, eip_save); return 0; }