int yr_re_compile( const char* re_string, int flags, YR_ARENA* code_arena, RE** re, RE_ERROR* error) { RE* compiled_re; YR_ARENA* arena; *re = NULL; FAIL_ON_ERROR(yr_re_parse(re_string, flags, &compiled_re, error)); if (code_arena == NULL) { FAIL_ON_ERROR_WITH_CLEANUP( yr_arena_create( RE_MAX_CODE_SIZE, ARENA_FLAGS_FIXED_SIZE, &arena), yr_re_destroy(compiled_re)); compiled_re->code_arena = arena; } else { arena = code_arena; } FAIL_ON_ERROR_WITH_CLEANUP( yr_re_emit_code(compiled_re, arena), yr_re_destroy(compiled_re)); *re = compiled_re; return ERROR_SUCCESS; }
YR_API int yr_compiler_create( YR_COMPILER** compiler) { int result; YR_COMPILER* new_compiler; new_compiler = (YR_COMPILER*) yr_calloc(1, sizeof(YR_COMPILER)); if (new_compiler == NULL) return ERROR_INSUFICIENT_MEMORY; new_compiler->errors = 0; new_compiler->callback = NULL; new_compiler->last_error = ERROR_SUCCESS; new_compiler->last_error_line = 0; new_compiler->error_line = 0; new_compiler->last_result = ERROR_SUCCESS; new_compiler->file_stack_ptr = 0; new_compiler->file_name_stack_ptr = 0; new_compiler->fixup_stack_head = NULL; new_compiler->allow_includes = 1; new_compiler->loop_depth = 0; new_compiler->loop_for_of_mem_offset = -1; new_compiler->compiled_rules_arena = NULL; new_compiler->namespaces_count = 0; new_compiler->current_rule = NULL; result = yr_hash_table_create(10007, &new_compiler->rules_table); if (result == ERROR_SUCCESS) result = yr_hash_table_create(10007, &new_compiler->objects_table); if (result == ERROR_SUCCESS) result = yr_hash_table_create(101, &new_compiler->strings_table); if (result == ERROR_SUCCESS) result = yr_arena_create(65536, 0, &new_compiler->sz_arena); if (result == ERROR_SUCCESS) result = yr_arena_create(65536, 0, &new_compiler->rules_arena); if (result == ERROR_SUCCESS) result = yr_arena_create(65536, 0, &new_compiler->strings_arena); if (result == ERROR_SUCCESS) result = yr_arena_create(65536, 0, &new_compiler->code_arena); if (result == ERROR_SUCCESS) result = yr_arena_create(65536, 0, &new_compiler->re_code_arena); if (result == ERROR_SUCCESS) result = yr_arena_create(65536, 0, &new_compiler->externals_arena); if (result == ERROR_SUCCESS) result = yr_arena_create(65536, 0, &new_compiler->namespaces_arena); if (result == ERROR_SUCCESS) result = yr_arena_create(65536, 0, &new_compiler->metas_arena); if (result == ERROR_SUCCESS) result = yr_arena_create(65536, 0, &new_compiler->automaton_arena); if (result == ERROR_SUCCESS) result = yr_arena_create(65536, 0, &new_compiler->matches_arena); if (result == ERROR_SUCCESS) result = yr_ac_automaton_create(&new_compiler->automaton); if (result == ERROR_SUCCESS) { *compiler = new_compiler; } else // if error, do cleanup { yr_compiler_destroy(new_compiler); } return result; }
int _yr_compiler_compile_rules( YR_COMPILER* compiler) { YARA_RULES_FILE_HEADER* rules_file_header = NULL; YR_ARENA* arena = NULL; YR_RULE null_rule; YR_EXTERNAL_VARIABLE null_external; YR_AC_TABLES tables; int8_t halt = OP_HALT; int result; // Write halt instruction at the end of code. yr_arena_write_data( compiler->code_arena, &halt, sizeof(int8_t), NULL); // Write a null rule indicating the end. memset(&null_rule, 0xFA, sizeof(YR_RULE)); null_rule.g_flags = RULE_GFLAGS_NULL; yr_arena_write_data( compiler->rules_arena, &null_rule, sizeof(YR_RULE), NULL); // Write a null external the end. memset(&null_external, 0xFA, sizeof(YR_EXTERNAL_VARIABLE)); null_external.type = EXTERNAL_VARIABLE_TYPE_NULL; yr_arena_write_data( compiler->externals_arena, &null_external, sizeof(YR_EXTERNAL_VARIABLE), NULL); // Write Aho-Corasick automaton to arena. result = yr_ac_compile( compiler->automaton, compiler->automaton_arena, &tables); if (result == ERROR_SUCCESS) result = yr_arena_create(1024, 0, &arena); if (result == ERROR_SUCCESS) result = yr_arena_allocate_struct( arena, sizeof(YARA_RULES_FILE_HEADER), (void**) &rules_file_header, offsetof(YARA_RULES_FILE_HEADER, rules_list_head), offsetof(YARA_RULES_FILE_HEADER, externals_list_head), offsetof(YARA_RULES_FILE_HEADER, code_start), offsetof(YARA_RULES_FILE_HEADER, match_table), offsetof(YARA_RULES_FILE_HEADER, transition_table), EOL); if (result == ERROR_SUCCESS) { rules_file_header->rules_list_head = (YR_RULE*) yr_arena_base_address( compiler->rules_arena); rules_file_header->externals_list_head = (YR_EXTERNAL_VARIABLE*) yr_arena_base_address(compiler->externals_arena); rules_file_header->code_start = (uint8_t*) yr_arena_base_address( compiler->code_arena); rules_file_header->match_table = tables.matches; rules_file_header->transition_table = tables.transitions; } if (result == ERROR_SUCCESS) { result = yr_arena_append( arena, compiler->code_arena); } if (result == ERROR_SUCCESS) { compiler->code_arena = NULL; result = yr_arena_append( arena, compiler->re_code_arena); } if (result == ERROR_SUCCESS) { compiler->re_code_arena = NULL; result = yr_arena_append( arena, compiler->rules_arena); } if (result == ERROR_SUCCESS) { compiler->rules_arena = NULL; result = yr_arena_append( arena, compiler->strings_arena); } if (result == ERROR_SUCCESS) { compiler->strings_arena = NULL; result = yr_arena_append( arena, compiler->externals_arena); } if (result == ERROR_SUCCESS) { compiler->externals_arena = NULL; result = yr_arena_append( arena, compiler->namespaces_arena); } if (result == ERROR_SUCCESS) { compiler->namespaces_arena = NULL; result = yr_arena_append( arena, compiler->metas_arena); } if (result == ERROR_SUCCESS) { compiler->metas_arena = NULL; result = yr_arena_append( arena, compiler->sz_arena); } if (result == ERROR_SUCCESS) { compiler->sz_arena = NULL; result = yr_arena_append( arena, compiler->automaton_arena); } if (result == ERROR_SUCCESS) { compiler->automaton_arena = NULL; result = yr_arena_append( arena, compiler->matches_arena); } if (result == ERROR_SUCCESS) { compiler->matches_arena = NULL; compiler->compiled_rules_arena = arena; result = yr_arena_coalesce(arena); } else { yr_arena_destroy(arena); } return result; }
int yr_arena_load_stream( YR_STREAM* stream, YR_ARENA** arena) { YR_ARENA_PAGE* page; YR_ARENA* new_arena; ARENA_FILE_HEADER header; uint32_t reloc_offset; uint8_t** reloc_address; uint8_t* reloc_target; int result; if (yr_stream_read(&header, sizeof(header), 1, stream) != 1) return ERROR_INVALID_FILE; if (header.magic[0] != 'Y' || header.magic[1] != 'A' || header.magic[2] != 'R' || header.magic[3] != 'A') { return ERROR_INVALID_FILE; } if (header.size < 2048) // compiled rules are always larger than 2KB return ERROR_CORRUPT_FILE; if (header.version != ARENA_FILE_VERSION) return ERROR_UNSUPPORTED_FILE_VERSION; result = yr_arena_create(header.size, 0, &new_arena); if (result != ERROR_SUCCESS) return result; page = new_arena->current_page; if (yr_stream_read(page->address, header.size, 1, stream) != 1) { yr_arena_destroy(new_arena); return ERROR_CORRUPT_FILE; } page->used = header.size; if (yr_stream_read(&reloc_offset, sizeof(reloc_offset), 1, stream) != 1) { yr_arena_destroy(new_arena); return ERROR_CORRUPT_FILE; } while (reloc_offset != 0xFFFFFFFF) { if (reloc_offset > header.size - sizeof(uint8_t*)) { yr_arena_destroy(new_arena); return ERROR_CORRUPT_FILE; } yr_arena_make_relocatable(new_arena, page->address, reloc_offset, EOL); reloc_address = (uint8_t**) (page->address + reloc_offset); reloc_target = *reloc_address; if (reloc_target != (uint8_t*) (size_t) 0xFFFABADA) *reloc_address += (size_t) page->address; else *reloc_address = 0; if (yr_stream_read(&reloc_offset, sizeof(reloc_offset), 1, stream) != 1) { yr_arena_destroy(new_arena); return ERROR_CORRUPT_FILE; } } *arena = new_arena; return ERROR_SUCCESS; }
int yr_arena_duplicate( YR_ARENA* arena, YR_ARENA** duplicated) { YR_RELOC* reloc; YR_RELOC* new_reloc; YR_ARENA_PAGE* page; YR_ARENA_PAGE* new_page; YR_ARENA* new_arena; uint8_t** reloc_address; uint8_t* reloc_target; // Only coalesced arenas can be duplicated. assert(arena->flags & ARENA_FLAGS_COALESCED); page = arena->page_list_head; FAIL_ON_ERROR(yr_arena_create(page->size, arena->flags, &new_arena)); new_page = new_arena->current_page; new_page->used = page->used; memcpy(new_page->address, page->address, page->size); reloc = page->reloc_list_head; while (reloc != NULL) { new_reloc = (YR_RELOC*) yr_malloc(sizeof(YR_RELOC)); if (new_reloc == NULL) { yr_arena_destroy(new_arena); return ERROR_INSUFFICIENT_MEMORY; } new_reloc->offset = reloc->offset; new_reloc->next = NULL; if (new_page->reloc_list_head == NULL) new_page->reloc_list_head = new_reloc; if (new_page->reloc_list_tail != NULL) new_page->reloc_list_tail->next = new_reloc; new_page->reloc_list_tail = new_reloc; reloc_address = (uint8_t**) (new_page->address + new_reloc->offset); reloc_target = *reloc_address; if (reloc_target != NULL) { assert(reloc_target >= page->address); assert(reloc_target < page->address + page->used); *reloc_address = reloc_target - \ page->address + \ new_page->address; } reloc = reloc->next; } *duplicated = new_arena; return ERROR_SUCCESS; }
int yr_execute_code( YR_SCAN_CONTEXT* context) { int64_t mem[MEM_SIZE]; int32_t sp = 0; const uint8_t* ip = context->rules->code_start; YR_VALUE args[YR_MAX_FUNCTION_ARGS]; YR_VALUE *stack; YR_VALUE r1; YR_VALUE r2; YR_VALUE r3; uint64_t elapsed_time; #ifdef PROFILING_ENABLED uint64_t start_time; YR_RULE* current_rule = NULL; #endif YR_INIT_RULE_ARGS init_rule_args; YR_RULE* rule; YR_MATCH* match; YR_OBJECT_FUNCTION* function; YR_OBJECT** obj_ptr; YR_ARENA* obj_arena; char* identifier; char* args_fmt; int i; int found; int count; int result = ERROR_SUCCESS; int cycle = 0; int tidx = context->tidx; int stack_size; bool stop = false; uint8_t opcode; yr_get_configuration(YR_CONFIG_STACK_SIZE, (void*) &stack_size); stack = (YR_VALUE*) yr_malloc(stack_size * sizeof(YR_VALUE)); if (stack == NULL) return ERROR_INSUFFICIENT_MEMORY; FAIL_ON_ERROR_WITH_CLEANUP( yr_arena_create(1024, 0, &obj_arena), yr_free(stack)); #ifdef PROFILING_ENABLED start_time = yr_stopwatch_elapsed_us(&context->stopwatch); #endif #if PARANOID_EXEC memset(mem, 0, MEM_SIZE * sizeof(mem[0])); #endif while(!stop) { opcode = *ip; ip++; switch(opcode) { case OP_NOP: break; case OP_HALT: assert(sp == 0); // When HALT is reached the stack should be empty. stop = true; break; case OP_PUSH: r1.i = *(uint64_t*)(ip); ip += sizeof(uint64_t); push(r1); break; case OP_POP: pop(r1); break; case OP_CLEAR_M: r1.i = *(uint64_t*)(ip); ip += sizeof(uint64_t); #if PARANOID_EXEC ensure_within_mem(r1.i); #endif mem[r1.i] = 0; break; case OP_ADD_M: r1.i = *(uint64_t*)(ip); ip += sizeof(uint64_t); #if PARANOID_EXEC ensure_within_mem(r1.i); #endif pop(r2); if (!is_undef(r2)) mem[r1.i] += r2.i; break; case OP_INCR_M: r1.i = *(uint64_t*)(ip); ip += sizeof(uint64_t); #if PARANOID_EXEC ensure_within_mem(r1.i); #endif mem[r1.i]++; break; case OP_PUSH_M: r1.i = *(uint64_t*)(ip); ip += sizeof(uint64_t); #if PARANOID_EXEC ensure_within_mem(r1.i); #endif r1.i = mem[r1.i]; push(r1); break; case OP_POP_M: r1.i = *(uint64_t*)(ip); ip += sizeof(uint64_t); #if PARANOID_EXEC ensure_within_mem(r1.i); #endif pop(r2); mem[r1.i] = r2.i; break; case OP_SET_M: r1.i = *(uint64_t*)(ip); ip += sizeof(uint64_t); #if PARANOID_EXEC ensure_within_mem(r1.i); #endif pop(r2); push(r2); if (!is_undef(r2)) mem[r1.i] = r2.i; break; case OP_SWAPUNDEF: r1.i = *(uint64_t*)(ip); ip += sizeof(uint64_t); #if PARANOID_EXEC ensure_within_mem(r1.i); #endif pop(r2); if (is_undef(r2)) { r1.i = mem[r1.i]; push(r1); } else { push(r2); } break; case OP_JNUNDEF: pop(r1); push(r1); ip = jmp_if(!is_undef(r1), ip); break; case OP_JLE_P: pop(r2); pop(r1); ip = jmp_if(r1.i <= r2.i, ip); break; case OP_JTRUE: pop(r1); push(r1); ip = jmp_if(!is_undef(r1) && r1.i, ip); break; case OP_JFALSE: pop(r1); push(r1); ip = jmp_if(is_undef(r1) || !r1.i, ip); break; case OP_JFALSE_P: pop(r1); ip = jmp_if(is_undef(r1) || !r1.i, ip); break; case OP_AND: pop(r2); pop(r1); if (is_undef(r1) || is_undef(r2)) r1.i = 0; else r1.i = r1.i && r2.i; push(r1); break; case OP_OR: pop(r2); pop(r1); if (is_undef(r1)) { push(r2); } else if (is_undef(r2)) { push(r1); } else { r1.i = r1.i || r2.i; push(r1); } break; case OP_NOT: pop(r1); if (is_undef(r1)) r1.i = UNDEFINED; else r1.i = !r1.i; push(r1); break; case OP_MOD: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); if (r2.i != 0) r1.i = r1.i % r2.i; else r1.i = UNDEFINED; push(r1); break; case OP_SHR: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); if (r2.i < 0) r1.i = UNDEFINED; else if (r2.i < 64) r1.i = r1.i >> r2.i; else r1.i = 0; push(r1); break; case OP_SHL: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); if (r2.i < 0) r1.i = UNDEFINED; else if (r2.i < 64) r1.i = r1.i << r2.i; else r1.i = 0; push(r1); break; case OP_BITWISE_NOT: pop(r1); ensure_defined(r1); r1.i = ~r1.i; push(r1); break; case OP_BITWISE_AND: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i & r2.i; push(r1); break; case OP_BITWISE_OR: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i | r2.i; push(r1); break; case OP_BITWISE_XOR: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i ^ r2.i; push(r1); break; case OP_PUSH_RULE: rule = *(YR_RULE**)(ip); ip += sizeof(uint64_t); if (RULE_IS_DISABLED(rule)) r1.i = UNDEFINED; else r1.i = rule->t_flags[tidx] & RULE_TFLAGS_MATCH ? 1 : 0; push(r1); break; case OP_INIT_RULE: memcpy(&init_rule_args, ip, sizeof(init_rule_args)); #ifdef PROFILING_ENABLED current_rule = init_rule_args.rule; #endif if (RULE_IS_DISABLED(init_rule_args.rule)) ip = init_rule_args.jmp_addr; else ip += sizeof(init_rule_args); break; case OP_MATCH_RULE: pop(r1); rule = *(YR_RULE**)(ip); ip += sizeof(uint64_t); if (!is_undef(r1) && r1.i) rule->t_flags[tidx] |= RULE_TFLAGS_MATCH; else if (RULE_IS_GLOBAL(rule)) rule->ns->t_flags[tidx] |= NAMESPACE_TFLAGS_UNSATISFIED_GLOBAL; #ifdef PROFILING_ENABLED elapsed_time = yr_stopwatch_elapsed_us(&context->stopwatch); rule->time_cost_per_thread[tidx] += (elapsed_time - start_time); start_time = elapsed_time; #endif assert(sp == 0); // at this point the stack should be empty. break; case OP_OBJ_LOAD: identifier = *(char**)(ip); ip += sizeof(uint64_t); r1.o = (YR_OBJECT*) yr_hash_table_lookup( context->objects_table, identifier, NULL); assert(r1.o != NULL); push(r1); break; case OP_OBJ_FIELD: identifier = *(char**)(ip); ip += sizeof(uint64_t); pop(r1); ensure_defined(r1); r1.o = yr_object_lookup_field(r1.o, identifier); assert(r1.o != NULL); push(r1); break; case OP_OBJ_VALUE: pop(r1); ensure_defined(r1); #if PARANOID_EXEC check_object_canary(r1.o); #endif switch(r1.o->type) { case OBJECT_TYPE_INTEGER: r1.i = r1.o->value.i; break; case OBJECT_TYPE_FLOAT: if (isnan(r1.o->value.d)) r1.i = UNDEFINED; else r1.d = r1.o->value.d; break; case OBJECT_TYPE_STRING: if (r1.o->value.ss == NULL) r1.i = UNDEFINED; else r1.ss = r1.o->value.ss; break; default: assert(false); } push(r1); break; case OP_INDEX_ARRAY: pop(r1); // index pop(r2); // array ensure_defined(r1); ensure_defined(r2); assert(r2.o->type == OBJECT_TYPE_ARRAY); #if PARANOID_EXEC check_object_canary(r2.o); #endif r1.o = yr_object_array_get_item(r2.o, 0, (int) r1.i); if (r1.o == NULL) r1.i = UNDEFINED; push(r1); break; case OP_LOOKUP_DICT: pop(r1); // key pop(r2); // dictionary ensure_defined(r1); ensure_defined(r2); assert(r2.o->type == OBJECT_TYPE_DICTIONARY); #if PARANOID_EXEC check_object_canary(r2.o); #endif r1.o = yr_object_dict_get_item( r2.o, 0, r1.ss->c_string); if (r1.o == NULL) r1.i = UNDEFINED; push(r1); break; case OP_CALL: args_fmt = *(char**)(ip); ip += sizeof(uint64_t); i = (int) strlen(args_fmt); count = 0; #if PARANOID_EXEC if (i > YR_MAX_FUNCTION_ARGS) { stop = true; result = ERROR_INTERNAL_FATAL_ERROR; break; } #endif // pop arguments from stack and copy them to args array while (i > 0) { pop(r1); if (is_undef(r1)) // count the number of undefined args count++; args[i - 1] = r1; i--; } pop(r2); ensure_defined(r2); #if PARANOID_EXEC check_object_canary(r2.o); #endif if (count > 0) { // if there are undefined args, result for function call // is undefined as well. r1.i = UNDEFINED; push(r1); break; } function = object_as_function(r2.o); result = ERROR_INTERNAL_FATAL_ERROR; for (i = 0; i < YR_MAX_OVERLOADED_FUNCTIONS; i++) { if (function->prototypes[i].arguments_fmt == NULL) break; if (strcmp(function->prototypes[i].arguments_fmt, args_fmt) == 0) { result = function->prototypes[i].code(args, context, function); break; } } // if i == YR_MAX_OVERLOADED_FUNCTIONS at this point no matching // prototype was found, but this shouldn't happen. assert(i < YR_MAX_OVERLOADED_FUNCTIONS); // make a copy of the returned object and push the copy into the stack // function->return_obj can't be pushed because it can change in // subsequent calls to the same function. if (result == ERROR_SUCCESS) result = yr_object_copy(function->return_obj, &r1.o); // a pointer to the copied object is stored in a arena in order to // free the object before exiting yr_execute_code if (result == ERROR_SUCCESS) result = yr_arena_write_data(obj_arena, &r1.o, sizeof(r1.o), NULL); stop = (result != ERROR_SUCCESS); push(r1); break; case OP_FOUND: pop(r1); r1.i = r1.s->matches[tidx].tail != NULL ? 1 : 0; push(r1); break; case OP_FOUND_AT: pop(r2); pop(r1); if (is_undef(r1)) { r1.i = 0; push(r1); break; } match = r2.s->matches[tidx].head; r3.i = false; while (match != NULL) { if (r1.i == match->base + match->offset) { r3.i = true; break; } if (r1.i < match->base + match->offset) break; match = match->next; } push(r3); break; case OP_FOUND_IN: pop(r3); pop(r2); pop(r1); ensure_defined(r1); ensure_defined(r2); match = r3.s->matches[tidx].head; r3.i = false; while (match != NULL && !r3.i) { if (match->base + match->offset >= r1.i && match->base + match->offset <= r2.i) { r3.i = true; } if (match->base + match->offset > r2.i) break; match = match->next; } push(r3); break; case OP_COUNT: pop(r1); #if PARANOID_EXEC // Make sure that the string pointer is within the rules arena. if (yr_arena_page_for_address(context->rules->arena, r1.p) == NULL) return ERROR_INTERNAL_FATAL_ERROR; #endif r1.i = r1.s->matches[tidx].count; push(r1); break; case OP_OFFSET: pop(r2); pop(r1); ensure_defined(r1); match = r2.s->matches[tidx].head; i = 1; r3.i = UNDEFINED; while (match != NULL && r3.i == UNDEFINED) { if (r1.i == i) r3.i = match->base + match->offset; i++; match = match->next; } push(r3); break; case OP_LENGTH: pop(r2); pop(r1); ensure_defined(r1); match = r2.s->matches[tidx].head; i = 1; r3.i = UNDEFINED; while (match != NULL && r3.i == UNDEFINED) { if (r1.i == i) r3.i = match->match_length; i++; match = match->next; } push(r3); break; case OP_OF: found = 0; count = 0; pop(r1); while (!is_undef(r1)) { if (r1.s->matches[tidx].tail != NULL) found++; count++; pop(r1); } pop(r2); if (is_undef(r2)) r1.i = found >= count ? 1 : 0; else r1.i = found >= r2.i ? 1 : 0; push(r1); break; case OP_FILESIZE: r1.i = context->file_size; push(r1); break; case OP_ENTRYPOINT: r1.i = context->entry_point; push(r1); break; case OP_INT8: pop(r1); r1.i = read_int8_t_little_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_INT16: pop(r1); r1.i = read_int16_t_little_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_INT32: pop(r1); r1.i = read_int32_t_little_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_UINT8: pop(r1); r1.i = read_uint8_t_little_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_UINT16: pop(r1); r1.i = read_uint16_t_little_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_UINT32: pop(r1); r1.i = read_uint32_t_little_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_INT8BE: pop(r1); r1.i = read_int8_t_big_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_INT16BE: pop(r1); r1.i = read_int16_t_big_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_INT32BE: pop(r1); r1.i = read_int32_t_big_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_UINT8BE: pop(r1); r1.i = read_uint8_t_big_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_UINT16BE: pop(r1); r1.i = read_uint16_t_big_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_UINT32BE: pop(r1); r1.i = read_uint32_t_big_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_CONTAINS: pop(r2); pop(r1); ensure_defined(r1); ensure_defined(r2); r1.i = memmem(r1.ss->c_string, r1.ss->length, r2.ss->c_string, r2.ss->length) != NULL; push(r1); break; case OP_IMPORT: r1.i = *(uint64_t*)(ip); ip += sizeof(uint64_t); result = yr_modules_load((char*) r1.p, context); if (result != ERROR_SUCCESS) stop = true; break; case OP_MATCHES: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); if (r1.ss->length == 0) { r1.i = false; push(r1); break; } result = yr_re_exec( context, (uint8_t*) r2.re->code, (uint8_t*) r1.ss->c_string, r1.ss->length, 0, r2.re->flags | RE_FLAGS_SCAN, NULL, NULL, &found); if (result != ERROR_SUCCESS) stop = true; r1.i = found >= 0; push(r1); break; case OP_INT_TO_DBL: r1.i = *(uint64_t*)(ip); ip += sizeof(uint64_t); #if PARANOID_EXEC if (r1.i > sp || sp - r1.i >= stack_size) { stop = true; result = ERROR_INTERNAL_FATAL_ERROR; break; } #endif r2 = stack[sp - r1.i]; if (is_undef(r2)) stack[sp - r1.i].i = UNDEFINED; else stack[sp - r1.i].d = (double) r2.i; break; case OP_STR_TO_BOOL: pop(r1); ensure_defined(r1); r1.i = r1.ss->length > 0; push(r1); break; case OP_INT_EQ: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i == r2.i; push(r1); break; case OP_INT_NEQ: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i != r2.i; push(r1); break; case OP_INT_LT: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i < r2.i; push(r1); break; case OP_INT_GT: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i > r2.i; push(r1); break; case OP_INT_LE: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i <= r2.i; push(r1); break; case OP_INT_GE: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i >= r2.i; push(r1); break; case OP_INT_ADD: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i + r2.i; push(r1); break; case OP_INT_SUB: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i - r2.i; push(r1); break; case OP_INT_MUL: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i * r2.i; push(r1); break; case OP_INT_DIV: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); if (r2.i != 0) r1.i = r1.i / r2.i; else r1.i = UNDEFINED; push(r1); break; case OP_INT_MINUS: pop(r1); ensure_defined(r1); r1.i = -r1.i; push(r1); break; case OP_DBL_LT: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.d < r2.d; push(r1); break; case OP_DBL_GT: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.d > r2.d; push(r1); break; case OP_DBL_LE: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.d <= r2.d; push(r1); break; case OP_DBL_GE: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.d >= r2.d; push(r1); break; case OP_DBL_EQ: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = fabs(r1.d - r2.d) < DBL_EPSILON; push(r1); break; case OP_DBL_NEQ: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = fabs(r1.d - r2.d) >= DBL_EPSILON; push(r1); break; case OP_DBL_ADD: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.d = r1.d + r2.d; push(r1); break; case OP_DBL_SUB: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.d = r1.d - r2.d; push(r1); break; case OP_DBL_MUL: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.d = r1.d * r2.d; push(r1); break; case OP_DBL_DIV: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.d = r1.d / r2.d; push(r1); break; case OP_DBL_MINUS: pop(r1); ensure_defined(r1); r1.d = -r1.d; push(r1); break; case OP_STR_EQ: case OP_STR_NEQ: case OP_STR_LT: case OP_STR_LE: case OP_STR_GT: case OP_STR_GE: pop(r2); pop(r1); ensure_defined(r1); ensure_defined(r2); switch(opcode) { case OP_STR_EQ: r1.i = (sized_string_cmp(r1.ss, r2.ss) == 0); break; case OP_STR_NEQ: r1.i = (sized_string_cmp(r1.ss, r2.ss) != 0); break; case OP_STR_LT: r1.i = (sized_string_cmp(r1.ss, r2.ss) < 0); break; case OP_STR_LE: r1.i = (sized_string_cmp(r1.ss, r2.ss) <= 0); break; case OP_STR_GT: r1.i = (sized_string_cmp(r1.ss, r2.ss) > 0); break; case OP_STR_GE: r1.i = (sized_string_cmp(r1.ss, r2.ss) >= 0); break; } push(r1); break; default: // Unknown instruction, this shouldn't happen. assert(false); } // Check for timeout every 10 instruction cycles. If timeout == 0 it means // no timeout at all. if (context->timeout > 0L && ++cycle == 10) { elapsed_time = yr_stopwatch_elapsed_us(&context->stopwatch); if (elapsed_time > context->timeout) { #ifdef PROFILING_ENABLED assert(current_rule != NULL); current_rule->time_cost_per_thread[tidx] += elapsed_time - start_time; #endif result = ERROR_SCAN_TIMEOUT; stop = true; } cycle = 0; } }
int yr_arena_load( const char* filename, YR_ARENA** arena) { FILE* fh; YR_ARENA_PAGE* page; YR_ARENA* new_arena; ARENA_FILE_HEADER header; int32_t reloc_offset; uint8_t** reloc_address; uint8_t* reloc_target; long file_size; int result; fh = fopen(filename, "rb"); if (fh == NULL) return ERROR_COULD_NOT_OPEN_FILE; fseek(fh, 0, SEEK_END); file_size = ftell(fh); fseek(fh, 0, SEEK_SET); if (fread(&header, sizeof(header), 1, fh) != 1) { fclose(fh); return ERROR_INVALID_FILE; } if (header.magic[0] != 'Y' || header.magic[1] != 'A' || header.magic[2] != 'R' || header.magic[3] != 'A') { fclose(fh); return ERROR_INVALID_FILE; } if (header.size >= (uint32_t)file_size) { fclose(fh); return ERROR_CORRUPT_FILE; } if (header.version > ARENA_FILE_VERSION) { fclose(fh); return ERROR_UNSUPPORTED_FILE_VERSION; } result = yr_arena_create(header.size, 0, &new_arena); if (result != ERROR_SUCCESS) { fclose(fh); return result; } page = new_arena->current_page; if (fread(page->address, header.size, 1, fh) != 1) { fclose(fh); yr_arena_destroy(new_arena); return ERROR_CORRUPT_FILE; } page->used = header.size; if (fread(&reloc_offset, sizeof(reloc_offset), 1, fh) != 1) { fclose(fh); yr_arena_destroy(new_arena); return ERROR_CORRUPT_FILE; } while (reloc_offset != -1) { yr_arena_make_relocatable(new_arena, page->address, reloc_offset, EOL); reloc_address = (uint8_t**) (page->address + reloc_offset); reloc_target = *reloc_address; if (reloc_target != (uint8_t*) (size_t) 0xFFFABADA) *reloc_address += (size_t) page->address; else *reloc_address = 0; if (fread(&reloc_offset, sizeof(reloc_offset), 1, fh) != 1) { fclose(fh); yr_arena_destroy(new_arena); return ERROR_CORRUPT_FILE; } } fclose(fh); *arena = new_arena; return ERROR_SUCCESS; }
int yr_rules_scan_mem_blocks( YR_RULES* rules, YR_MEMORY_BLOCK* block, int scanning_process_memory, YR_CALLBACK_FUNC callback, void* user_data, int fast_scan_mode, int timeout) { YR_RULE* rule; EVALUATION_CONTEXT context; YR_ARENA* matches_arena = NULL; time_t start_time; tidx_mask_t bit; int message; int tidx = 0; int result = ERROR_SUCCESS; if (block == NULL) return ERROR_SUCCESS; context.file_size = block->size; context.mem_block = block; context.entry_point = UNDEFINED; _yr_rules_lock(rules); bit = 1; while (rules->tidx_mask & bit) { tidx++; bit <<= 1; } if (tidx < MAX_THREADS) rules->tidx_mask |= bit; else result = ERROR_TOO_MANY_SCAN_THREADS; _yr_rules_unlock(rules); if (result != ERROR_SUCCESS) return result; yr_set_tidx(tidx); result = yr_arena_create(1024, 0, &matches_arena); if (result != ERROR_SUCCESS) goto _exit; start_time = time(NULL); while (block != NULL) { if (context.entry_point == UNDEFINED) { if (scanning_process_memory) context.entry_point = yr_get_entry_point_address( block->data, block->size, block->base); else context.entry_point = yr_get_entry_point_offset( block->data, block->size); } result = yr_rules_scan_mem_block( rules, block->data, block->size, fast_scan_mode, timeout, start_time, matches_arena); if (result != ERROR_SUCCESS) goto _exit; block = block->next; } result = yr_execute_code( rules, &context, timeout, start_time); if (result != ERROR_SUCCESS) goto _exit; rule = rules->rules_list_head; while (!RULE_IS_NULL(rule)) { if (RULE_IS_GLOBAL(rule) && !(rule->t_flags[tidx] & RULE_TFLAGS_MATCH)) { rule->ns->t_flags[tidx] |= NAMESPACE_TFLAGS_UNSATISFIED_GLOBAL; } rule++; } rule = rules->rules_list_head; while (!RULE_IS_NULL(rule)) { if (rule->t_flags[tidx] & RULE_TFLAGS_MATCH && !(rule->ns->t_flags[tidx] & NAMESPACE_TFLAGS_UNSATISFIED_GLOBAL)) { message = CALLBACK_MSG_RULE_MATCHING; } else { message = CALLBACK_MSG_RULE_NOT_MATCHING; } if (!RULE_IS_PRIVATE(rule)) { switch (callback(message, rule, user_data)) { case CALLBACK_ABORT: result = ERROR_SUCCESS; goto _exit; case CALLBACK_ERROR: result = ERROR_CALLBACK_ERROR; goto _exit; } } rule++; } callback(CALLBACK_MSG_SCAN_FINISHED, NULL, user_data); _exit: _yr_rules_clean_matches(rules); if (matches_arena != NULL) yr_arena_destroy(matches_arena); _yr_rules_lock(rules); rules->tidx_mask &= ~(1 << tidx); _yr_rules_unlock(rules); yr_set_tidx(-1); return result; }
int yr_rules_scan_mem_blocks( YR_RULES* rules, YR_MEMORY_BLOCK* block, int flags, YR_CALLBACK_FUNC callback, void* user_data, int timeout) { YR_SCAN_CONTEXT context; YR_RULE* rule; YR_OBJECT* object; YR_EXTERNAL_VARIABLE* external; YR_ARENA* matches_arena = NULL; time_t start_time; tidx_mask_t bit; int message; int tidx = 0; int result = ERROR_SUCCESS; if (block == NULL) return ERROR_SUCCESS; context.flags = flags; context.callback = callback; context.user_data = user_data; context.file_size = block->size; context.mem_block = block; context.entry_point = UNDEFINED; context.objects_table = NULL; _yr_rules_lock(rules); bit = 1; while (rules->tidx_mask & bit) { tidx++; bit <<= 1; } if (tidx < MAX_THREADS) rules->tidx_mask |= bit; else result = ERROR_TOO_MANY_SCAN_THREADS; _yr_rules_unlock(rules); if (result != ERROR_SUCCESS) return result; yr_set_tidx(tidx); result = yr_arena_create(1024, 0, &matches_arena); if (result != ERROR_SUCCESS) goto _exit; result = yr_hash_table_create(64, &context.objects_table); if (result != ERROR_SUCCESS) goto _exit; external = rules->externals_list_head; while (!EXTERNAL_VARIABLE_IS_NULL(external)) { result = yr_object_from_external_variable( external, &object); if (result == ERROR_SUCCESS) result = yr_hash_table_add( context.objects_table, external->identifier, NULL, (void*) object); if (result != ERROR_SUCCESS) goto _exit; external++; } start_time = time(NULL); while (block != NULL) { if (context.entry_point == UNDEFINED) { if (flags & SCAN_FLAGS_PROCESS_MEMORY) context.entry_point = yr_get_entry_point_address( block->data, block->size, block->base); else context.entry_point = yr_get_entry_point_offset( block->data, block->size); } result = yr_rules_scan_mem_block( rules, block, flags, timeout, start_time, matches_arena); if (result != ERROR_SUCCESS) goto _exit; block = block->next; } result = yr_execute_code( rules, &context, timeout, start_time); if (result != ERROR_SUCCESS) goto _exit; rule = rules->rules_list_head; while (!RULE_IS_NULL(rule)) { if (RULE_IS_GLOBAL(rule) && !(rule->t_flags[tidx] & RULE_TFLAGS_MATCH)) { rule->ns->t_flags[tidx] |= NAMESPACE_TFLAGS_UNSATISFIED_GLOBAL; } rule++; } rule = rules->rules_list_head; while (!RULE_IS_NULL(rule)) { if (rule->t_flags[tidx] & RULE_TFLAGS_MATCH && !(rule->ns->t_flags[tidx] & NAMESPACE_TFLAGS_UNSATISFIED_GLOBAL)) { message = CALLBACK_MSG_RULE_MATCHING; } else { message = CALLBACK_MSG_RULE_NOT_MATCHING; } if (!RULE_IS_PRIVATE(rule)) { switch (callback(message, rule, user_data)) { case CALLBACK_ABORT: result = ERROR_SUCCESS; goto _exit; case CALLBACK_ERROR: result = ERROR_CALLBACK_ERROR; goto _exit; } } rule++; } callback(CALLBACK_MSG_SCAN_FINISHED, NULL, user_data); _exit: yr_modules_unload_all(&context); _yr_rules_clean_matches(rules); if (matches_arena != NULL) yr_arena_destroy(matches_arena); if (context.objects_table != NULL) yr_hash_table_destroy( context.objects_table, (YR_HASH_TABLE_FREE_VALUE_FUNC) yr_object_destroy); _yr_rules_lock(rules); rules->tidx_mask &= ~(1 << tidx); _yr_rules_unlock(rules); yr_set_tidx(-1); return result; }