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(); }
/** * 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; }