#include "../config.h"#include "emu/emu_shellcode.h"#include "emu/emu.h"#include "emu/emu_memory.h"#include "emu/emu_cpu.h"#include "emu/emu_cpu_data.h"#include "emu/emu_track.h"#include "emu/emu_source.h"#include "emu/emu_getpc.h"#include "emu/environment/emu_env.h"#include "emu/environment/win32/emu_env_w32.h"#include "emu/environment/win32/emu_env_w32_dll_export.h"#include "emu/emu_hashtable.h"#include "emu/emu_graph.h"#include "emu/emu_list.h"#include "emu/emu_queue.h"#include "emu/emu_log.h"
Defines | |
| #define | STATIC_OFFSET 0x00471000 |
| #define | EMU_SHELLCODE_TEST_MAX_STEPS 128 |
Enumerations | |
| enum | { EMU_SCTEST_SUSPECT_GETPC, EMU_SCTEST_SUSPECT_MOVFS } |
Functions | |
| int | tested_positions_cmp (struct emu_list_item *a, struct emu_list_item *b) |
| int32_t | emu_shellcode_run_and_track (struct emu *e, uint8_t *data, uint16_t datasize, uint16_t eipoffset, uint32_t steps, struct emu_track_and_source *etas, struct emu_hashtable *known_positions, struct emu_list_root *stats_tested_positions_list, bool brute_force) |
| int32_t | emu_shellcode_test (struct emu *e, uint8_t *data, uint16_t size) |
| struct emu_stats * | emu_stats_new (void) |
| void | emu_stats_free (struct emu_stats *es) |
Variables | |
| enum { ... } | emu_shellcode_suspect |
| #define EMU_SHELLCODE_TEST_MAX_STEPS 128 |
| #define STATIC_OFFSET 0x00471000 |
Referenced by emu_shellcode_run_and_track(), and emu_shellcode_test().
| int32_t emu_shellcode_run_and_track | ( | struct emu * | e, | |
| uint8_t * | data, | |||
| uint16_t | datasize, | |||
| uint16_t | eipoffset, | |||
| uint32_t | steps, | |||
| struct emu_track_and_source * | etas, | |||
| struct emu_hashtable * | known_positions, | |||
| struct emu_list_root * | stats_tested_positions_list, | |||
| bool | brute_force | |||
| ) |
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'
| e | the emu to run | |
| data | the data we run | |
| datasize | the data size | |
| eipoffset | the offset for eip | |
| steps | how many steps to try running | |
| etas | the track and source tree - the substantial information to run the breath first search | |
| known_positions | already tested positions | |
| stats_tested_positions_list | the result list | |
| brute_force | be aggressive? |
ignore positions we've visited already avoids dos for jz 0
try the next position instead
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
ignore loops to self avoids dos for "\xe3\xfe\xe8" breaks the upper loop
again, ignore already visited positions breaks the upper loop
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
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
References emu_vertex::color, emu_stats::cpu, emu_list_item::data, emu_vertex::data, emu_edge::destination, emu_stats::eip, emu_source_and_track_instr_info::eip, emu_tracking_info::eip, emu_cpu_eflags_set(), emu_cpu_eip_get(), emu_cpu_eip_set(), emu_cpu_get(), emu_cpu_parse(), emu_cpu_reg32_set(), emu_cpu_step(), emu_env_w32_eip_check(), emu_errno(), emu_hashtable_insert(), emu_hashtable_search(), emu_list_item_create(), emu_memory_clear(), emu_memory_get(), emu_memory_write_block(), emu_queue_dequeue(), emu_queue_empty(), emu_queue_enqueue(), emu_queue_free(), emu_queue_new(), emu_stats_new(), emu_track_instruction_check(), emu_tracking_info_clear(), emu_tracking_info_covers(), emu_tracking_info_debug_print(), emu_tracking_info_diff(), emu_tracking_info_free(), emu_tracking_info_new(), esp, emu_cpu::instr, emu_cpu::instr_string, emu_source_and_track_instr_info::instrstring, emu_instruction::is_fpu, logDebug, emu_instruction::need, red, emu_track_and_source::static_instr_graph, emu_track_and_source::static_instr_table, STATIC_OFFSET, emu_stats::steps, tested_positions_cmp(), emu_instruction::track, emu_track_and_source::track, emu_cpu::tracking, emu_hashtable_item::value, emu_graph::vertexes, and white.
Referenced by emu_shellcode_test().

| int32_t emu_shellcode_test | ( | struct emu * | e, | |
| uint8_t * | data, | |||
| uint16_t | size | |||
| ) |
Tests a given buffer for possible shellcodes
| e | the emu | |
| data | the buffer to test | |
| size | the size of the buffer |
References emu_stats::cpu, emu_list_item::data, emu_stats::eip, emu_cpu_eflags_set(), emu_cpu_eip_set(), emu_cpu_get(), emu_cpu_reg32_set(), emu_getpc_check(), emu_hashtable_free(), emu_hashtable_new(), emu_hashtable_ptr_cmp(), emu_hashtable_ptr_hash(), emu_list_item_create(), emu_memory_get(), emu_memory_mode_ro(), emu_memory_mode_rw(), emu_memory_write_block(), emu_shellcode_run_and_track(), emu_source_instruction_graph_create(), emu_stats_free(), emu_track_and_source_free(), emu_track_and_source_new(), esp, logDebug, logPF, STATIC_OFFSET, emu_stats::steps, tested_positions_cmp(), and emu_list_item::uint32.

| void emu_stats_free | ( | struct emu_stats * | es | ) |
Referenced by emu_shellcode_test().
| struct emu_stats* emu_stats_new | ( | void | ) | [read] |
Referenced by emu_shellcode_run_and_track().
| int tested_positions_cmp | ( | struct emu_list_item * | a, | |
| struct emu_list_item * | b | |||
| ) |
References emu_list_item::data.
Referenced by emu_shellcode_run_and_track(), and emu_shellcode_test().
| enum { ... } emu_shellcode_suspect |
1.6.1