Пример #1
0
int yr_parser_reduce_import(
    yyscan_t yyscanner,
    SIZED_STRING* module_name)
{
  YR_COMPILER* compiler = yyget_extra(yyscanner);
  YR_OBJECT* module_structure;

  char* name;

  module_structure = (YR_OBJECT*) yr_hash_table_lookup(
      compiler->objects_table,
      module_name->c_string,
      compiler->current_namespace->name);

  // if module already imported, do nothing

  if (module_structure != NULL)
    return ERROR_SUCCESS;

  compiler->last_result = yr_object_create(
      OBJECT_TYPE_STRUCTURE,
      module_name->c_string,
      NULL,
      &module_structure);

  if (compiler->last_result == ERROR_SUCCESS)
    compiler->last_result = yr_hash_table_add(
        compiler->objects_table,
        module_name->c_string,
        compiler->current_namespace->name,
        module_structure);

  if (compiler->last_result == ERROR_SUCCESS)
  {
    compiler->last_result = yr_modules_do_declarations(
        module_name->c_string,
        module_structure);

    if (compiler->last_result == ERROR_UNKNOWN_MODULE)
      yr_compiler_set_error_extra_info(compiler, module_name->c_string);
  }

  if (compiler->last_result == ERROR_SUCCESS)
    compiler->last_result = yr_arena_write_string(
        compiler->sz_arena,
        module_name->c_string,
        &name);

  if (compiler->last_result == ERROR_SUCCESS)
    compiler->last_result = yr_parser_emit_with_arg_reloc(
        yyscanner,
        OP_IMPORT,
        PTR_TO_INT64(name),
        NULL,
        NULL);

  return compiler->last_result;
}
Пример #2
0
int yr_compiler_define_string_variable(
    YR_COMPILER* compiler,
    const char* identifier,
    const char* value)
{
  YR_OBJECT* object;
  YR_EXTERNAL_VARIABLE* external;

  char* id = NULL;
  char* val = NULL;

  compiler->last_result = ERROR_SUCCESS;

  FAIL_ON_COMPILER_ERROR(yr_arena_write_string(
      compiler->sz_arena,
      identifier,
      &id));

  FAIL_ON_COMPILER_ERROR(yr_arena_write_string(
      compiler->sz_arena,
      value,
      &val));

  FAIL_ON_COMPILER_ERROR(yr_arena_allocate_struct(
      compiler->externals_arena,
      sizeof(YR_EXTERNAL_VARIABLE),
      (void**) &external,
      offsetof(YR_EXTERNAL_VARIABLE, identifier),
      offsetof(YR_EXTERNAL_VARIABLE, string),
      EOL));

  external->type = EXTERNAL_VARIABLE_TYPE_STRING;
  external->identifier = id;
  external->integer = 0;
  external->string = val;

  FAIL_ON_COMPILER_ERROR(yr_object_from_external_variable(
      external,
      &object));

  FAIL_ON_COMPILER_ERROR(yr_hash_table_add(
      compiler->objects_table,
      external->identifier,
      NULL,
      (void*) object));

  return compiler->last_result;
}
Пример #3
0
YR_API int yr_compiler_define_float_variable(
    YR_COMPILER* compiler,
    const char* identifier,
    double value)
{
  YR_EXTERNAL_VARIABLE* external;
  YR_OBJECT* object;

  char* id;

  compiler->last_result = ERROR_SUCCESS;

  FAIL_ON_COMPILER_ERROR(yr_arena_write_string(
      compiler->sz_arena,
      identifier,
      &id));

  FAIL_ON_COMPILER_ERROR(yr_arena_allocate_struct(
      compiler->externals_arena,
      sizeof(YR_EXTERNAL_VARIABLE),
      (void**) &external,
      offsetof(YR_EXTERNAL_VARIABLE, identifier),
      EOL));

  external->type = EXTERNAL_VARIABLE_TYPE_FLOAT;
  external->identifier = id;
  external->value.f = value;

  FAIL_ON_COMPILER_ERROR(yr_object_from_external_variable(
      external,
      &object));

  FAIL_ON_COMPILER_ERROR(yr_hash_table_add(
      compiler->objects_table,
      external->identifier,
      NULL,
      (void*) object));

  return ERROR_SUCCESS;
}
Пример #4
0
int yr_modules_load(
    const char* module_name,
    YR_SCAN_CONTEXT* context)
{
  int i, result;

  YR_MODULE_IMPORT mi;

  YR_OBJECT* module_structure = (YR_OBJECT*) yr_hash_table_lookup(
      context->objects_table,
      module_name,
      NULL);

  // if module_structure != NULL, the module was already
  // loaded, return successfully without doing nothing.

  if (module_structure != NULL)
    return ERROR_SUCCESS;

  // not loaded yet

  FAIL_ON_ERROR(yr_object_create(
      OBJECT_TYPE_STRUCTURE,
      module_name,
      NULL,
      &module_structure));

  mi.module_name = module_name;
  mi.module_data = NULL;
  mi.module_data_size = 0;

  result = context->callback(
      CALLBACK_MSG_IMPORT_MODULE,
      &mi,
      context->user_data);

  if (result == CALLBACK_ERROR)
  {
    yr_object_destroy(module_structure);
    return ERROR_CALLBACK_ERROR;
  }
    
  FAIL_ON_ERROR_WITH_CLEANUP(
      yr_modules_do_declarations(module_name, module_structure),
      yr_object_destroy(module_structure));

  FAIL_ON_ERROR_WITH_CLEANUP(
      yr_hash_table_add(
          context->objects_table,
          module_name,
          NULL,
          module_structure),
      yr_object_destroy(module_structure));

  for (i = 0; i < sizeof(yr_modules_table) / sizeof(YR_MODULE); i++)
  {
    if (strcmp(yr_modules_table[i].name, module_name) == 0)
    {
      result = yr_modules_table[i].load(
          context,
          module_structure,
          mi.module_data,
          mi.module_data_size);

      if (result != ERROR_SUCCESS)
        return result;
    }
  }

  result = context->callback(
      CALLBACK_MSG_MODULE_IMPORTED,
      module_structure,
      context->user_data);

  return ERROR_SUCCESS;
}
Пример #5
0
int yr_parser_reduce_rule_declaration(
    yyscan_t yyscanner,
    int32_t flags,
    const char* identifier,
    char* tags,
    YR_STRING* strings,
    YR_META* metas)
{
  YR_COMPILER* compiler = yyget_extra(yyscanner);
  YR_RULE* rule;
  YR_STRING* string;

  if (yr_hash_table_lookup(
        compiler->rules_table,
        identifier,
        compiler->current_namespace->name) != NULL)
  {
    // A rule with the same identifier already exists, return the
    // appropriate error.

    yr_compiler_set_error_extra_info(compiler, identifier);
    compiler->last_result = ERROR_DUPLICATE_RULE_IDENTIFIER;
    return compiler->last_result;
  }

  // Check for unreferenced (unused) strings.

  string = compiler->current_rule_strings;

  while(!STRING_IS_NULL(string))
  {
    // Only the heading fragment in a chain of strings (the one with
    // chained_to == NULL) must be referenced. All other fragments
    // are never marked as referenced.

    if (!STRING_IS_REFERENCED(string) &&
        string->chained_to == NULL)
    {
      yr_compiler_set_error_extra_info(compiler, string->identifier);
      compiler->last_result = ERROR_UNREFERENCED_STRING;
      break;
    }

    string = yr_arena_next_address(
        compiler->strings_arena,
        string,
        sizeof(YR_STRING));
  }

  if (compiler->last_result != ERROR_SUCCESS)
    return compiler->last_result;

  compiler->last_result = yr_arena_allocate_struct(
      compiler->rules_arena,
      sizeof(YR_RULE),
      (void**) &rule,
      offsetof(YR_RULE, identifier),
      offsetof(YR_RULE, tags),
      offsetof(YR_RULE, strings),
      offsetof(YR_RULE, metas),
      offsetof(YR_RULE, ns),
      EOL);

  if (compiler->last_result != ERROR_SUCCESS)
    return compiler->last_result;

  compiler->last_result = yr_arena_write_string(
      compiler->sz_arena,
      identifier,
      &rule->identifier);

  if (compiler->last_result != ERROR_SUCCESS)
    return compiler->last_result;

  compiler->last_result = yr_parser_emit_with_arg_reloc(
      yyscanner,
      RULE_POP,
      PTR_TO_UINT64(rule),
      NULL);

  if (compiler->last_result != ERROR_SUCCESS)
    return compiler->last_result;

  rule->g_flags = flags | compiler->current_rule_flags;
  rule->tags = tags;
  rule->strings = strings;
  rule->metas = metas;
  rule->ns = compiler->current_namespace;

  compiler->current_rule_flags = 0;
  compiler->current_rule_strings = NULL;

  yr_hash_table_add(
      compiler->rules_table,
      identifier,
      compiler->current_namespace->name,
      (void*) rule);

  return compiler->last_result;
}
Пример #6
0
YR_RULE* yr_parser_reduce_rule_declaration_phase_1(
    yyscan_t yyscanner,
    int32_t flags,
    const char* identifier)
{
  YR_COMPILER* compiler = yyget_extra(yyscanner);
  YR_RULE* rule = NULL;

  if (yr_hash_table_lookup(
        compiler->rules_table,
        identifier,
        compiler->current_namespace->name) != NULL ||
      yr_hash_table_lookup(
        compiler->objects_table,
        identifier,
        compiler->current_namespace->name) != NULL)
  {
    // A rule or variable with the same identifier already exists, return the
    // appropriate error.

    yr_compiler_set_error_extra_info(compiler, identifier);
    compiler->last_result = ERROR_DUPLICATED_IDENTIFIER;
    return NULL;
  }

  compiler->last_result = yr_arena_allocate_struct(
      compiler->rules_arena,
      sizeof(YR_RULE),
      (void**) &rule,
      offsetof(YR_RULE, identifier),
      offsetof(YR_RULE, tags),
      offsetof(YR_RULE, strings),
      offsetof(YR_RULE, metas),
      offsetof(YR_RULE, ns),
      EOL);

  if (compiler->last_result != ERROR_SUCCESS)
    return NULL;

  rule->g_flags = flags;
  rule->ns = compiler->current_namespace;

  #ifdef PROFILING_ENABLED
  rule->clock_ticks = 0;
  #endif

  compiler->last_result = yr_arena_write_string(
      compiler->sz_arena,
      identifier,
      (char**) &rule->identifier);

  if (compiler->last_result != ERROR_SUCCESS)
    return NULL;

  compiler->last_result = yr_parser_emit_with_arg_reloc(
      yyscanner,
      OP_INIT_RULE,
      PTR_TO_INT64(rule),
      NULL,
      NULL);

  if (compiler->last_result == ERROR_SUCCESS)
    compiler->last_result = yr_hash_table_add(
        compiler->rules_table,
        identifier,
        compiler->current_namespace->name,
        (void*) rule);

  // Clean strings_table as we are starting to parse a new rule.
  yr_hash_table_clean(compiler->strings_table, NULL);

  compiler->current_rule = rule;
  return rule;
}
Пример #7
0
YR_STRING* yr_parser_reduce_string_declaration(
    yyscan_t yyscanner,
    int32_t string_flags,
    const char* identifier,
    SIZED_STRING* str)
{
  int min_atom_quality;
  int min_atom_quality_aux;
  int re_flags = 0;

  int32_t min_gap;
  int32_t max_gap;

  char message[512];

  YR_COMPILER* compiler = yyget_extra(yyscanner);
  YR_STRING* string = NULL;
  YR_STRING* aux_string;
  YR_STRING* prev_string;

  RE* re = NULL;
  RE* remainder_re;

  RE_ERROR re_error;

  // Determine if a string with the same identifier was already defined
  // by searching for the identifier in string_table.

  string = yr_hash_table_lookup(
      compiler->strings_table,
      identifier,
      NULL);

  if (string != NULL)
  {
    compiler->last_result = ERROR_DUPLICATED_STRING_IDENTIFIER;
    yr_compiler_set_error_extra_info(compiler, identifier);
    goto _exit;
  }

  // Empty strings are now allowed

  if (str->length == 0)
  {
    compiler->last_result = ERROR_EMPTY_STRING;
    yr_compiler_set_error_extra_info(compiler, identifier);
    goto _exit;
  }

  if (str->flags & SIZED_STRING_FLAGS_NO_CASE)
    string_flags |= STRING_GFLAGS_NO_CASE;

  if (str->flags & SIZED_STRING_FLAGS_DOT_ALL)
    re_flags |= RE_FLAGS_DOT_ALL;

  if (strcmp(identifier,"$") == 0)
    string_flags |= STRING_GFLAGS_ANONYMOUS;

  if (!(string_flags & STRING_GFLAGS_WIDE))
    string_flags |= STRING_GFLAGS_ASCII;

  if (string_flags & STRING_GFLAGS_NO_CASE)
    re_flags |= RE_FLAGS_NO_CASE;

  // The STRING_GFLAGS_SINGLE_MATCH flag indicates that finding
  // a single match for the string is enough. This is true in
  // most cases, except when the string count (#) and string offset (@)
  // operators are used. All strings are marked STRING_FLAGS_SINGLE_MATCH
  // initially, and unmarked later if required.

  string_flags |= STRING_GFLAGS_SINGLE_MATCH;

  // The STRING_GFLAGS_FIXED_OFFSET indicates that the string doesn't
  // need to be searched all over the file because the user is using the
  // "at" operator. The string must be searched at a fixed offset in the
  // file. All strings are marked STRING_GFLAGS_FIXED_OFFSET initially,
  // and unmarked later if required.

  string_flags |= STRING_GFLAGS_FIXED_OFFSET;

  if (string_flags & STRING_GFLAGS_HEXADECIMAL ||
      string_flags & STRING_GFLAGS_REGEXP)
  {
    if (string_flags & STRING_GFLAGS_HEXADECIMAL)
      compiler->last_result = yr_re_parse_hex(
          str->c_string, re_flags, &re, &re_error);
    else
      compiler->last_result = yr_re_parse(
          str->c_string, re_flags, &re, &re_error);

    if (compiler->last_result != ERROR_SUCCESS)
    {
      snprintf(
          message,
          sizeof(message),
          "invalid %s \"%s\": %s",
          (string_flags & STRING_GFLAGS_HEXADECIMAL) ?
              "hex string" : "regular expression",
          identifier,
          re_error.message);

      yr_compiler_set_error_extra_info(
          compiler, message);

      goto _exit;
    }

    if (re->flags & RE_FLAGS_FAST_HEX_REGEXP)
      string_flags |= STRING_GFLAGS_FAST_HEX_REGEXP;

    // Regular expressions in the strings section can't mix greedy and ungreedy
    // quantifiers like .* and .*?. That's because these regular expressions can
    // be matched forwards and/or backwards depending on the atom found, and we
    // need the regexp to be all-greedy or all-ungreedy to be able to properly
    // calculate the length of the match.

    if ((re->flags & RE_FLAGS_GREEDY) &&
        (re->flags & RE_FLAGS_UNGREEDY))
    {
      compiler->last_result = ERROR_INVALID_REGULAR_EXPRESSION;

      yr_compiler_set_error_extra_info(compiler,
          "greedy and ungreedy quantifiers can't be mixed in a regular "
          "expression");

      goto _exit;
    }

    if (re->flags & RE_FLAGS_GREEDY)
      string_flags |= STRING_GFLAGS_GREEDY_REGEXP;

    if (yr_re_contains_dot_star(re))
    {
      snprintf(
        message,
        sizeof(message),
        "%s contains .*, consider using .{N} with a reasonable value for N",
        identifier);

        yywarning(yyscanner, message);
    }

    compiler->last_result = yr_re_split_at_chaining_point(
        re, &re, &remainder_re, &min_gap, &max_gap);

    if (compiler->last_result != ERROR_SUCCESS)
      goto _exit;

    compiler->last_result = _yr_parser_write_string(
        identifier,
        string_flags,
        compiler,
        NULL,
        re,
        &string,
        &min_atom_quality);

    if (compiler->last_result != ERROR_SUCCESS)
      goto _exit;

    if (remainder_re != NULL)
    {
      string->g_flags |= STRING_GFLAGS_CHAIN_TAIL | STRING_GFLAGS_CHAIN_PART;
      string->chain_gap_min = min_gap;
      string->chain_gap_max = max_gap;
    }

    // Use "aux_string" from now on, we want to keep the value of "string"
    // because it will returned.

    aux_string = string;

    while (remainder_re != NULL)
    {
      // Destroy regexp pointed by 're' before yr_re_split_at_jmp
      // overwrites 're' with another value.

      yr_re_destroy(re);

      compiler->last_result = yr_re_split_at_chaining_point(
          remainder_re, &re, &remainder_re, &min_gap, &max_gap);

      if (compiler->last_result != ERROR_SUCCESS)
        goto _exit;

      prev_string = aux_string;

      compiler->last_result = _yr_parser_write_string(
          identifier,
          string_flags,
          compiler,
          NULL,
          re,
          &aux_string,
          &min_atom_quality_aux);

      if (compiler->last_result != ERROR_SUCCESS)
        goto _exit;

      if (min_atom_quality_aux < min_atom_quality)
        min_atom_quality = min_atom_quality_aux;

      aux_string->g_flags |= STRING_GFLAGS_CHAIN_PART;
      aux_string->chain_gap_min = min_gap;
      aux_string->chain_gap_max = max_gap;

      prev_string->chained_to = aux_string;

      // prev_string is now chained to aux_string, an string chained
      // to another one can't have a fixed offset, only the head of the
      // string chain can have a fixed offset.

      prev_string->g_flags &= ~STRING_GFLAGS_FIXED_OFFSET;
    }
  }
  else
  {
    compiler->last_result = _yr_parser_write_string(
        identifier,
        string_flags,
        compiler,
        str,
        NULL,
        &string,
        &min_atom_quality);

    if (compiler->last_result != ERROR_SUCCESS)
      goto _exit;
  }

  compiler->last_result = yr_hash_table_add(
      compiler->strings_table,
      identifier,
      NULL,
      string);

  if (compiler->last_result != ERROR_SUCCESS)
    goto _exit;

  if (min_atom_quality < 3 && compiler->callback != NULL)
  {
    snprintf(
        message,
        sizeof(message),
        "%s is slowing down scanning%s",
        string->identifier,
        min_atom_quality < 2 ? " (critical!)" : "");

    yywarning(yyscanner, message);
  }

_exit:

  if (re != NULL)
    yr_re_destroy(re);

  if (compiler->last_result != ERROR_SUCCESS)
    return NULL;

  return string;
}
Пример #8
0
int yr_parser_reduce_rule_declaration_phase_1(
    yyscan_t yyscanner,
    int32_t flags,
    const char* identifier,
    YR_RULE** rule)
{
  YR_FIXUP *fixup;
  YR_INIT_RULE_ARGS *init_rule_args;
  YR_COMPILER* compiler = yyget_extra(yyscanner);

  *rule = NULL;

  if (yr_hash_table_lookup(
        compiler->rules_table,
        identifier,
        compiler->current_namespace->name) != NULL ||
      yr_hash_table_lookup(
        compiler->objects_table,
        identifier,
        NULL) != NULL)
  {
    // A rule or variable with the same identifier already exists, return the
    // appropriate error.

    yr_compiler_set_error_extra_info(compiler, identifier);
    return ERROR_DUPLICATED_IDENTIFIER;
  }

  FAIL_ON_ERROR(yr_arena_allocate_struct(
      compiler->rules_arena,
      sizeof(YR_RULE),
      (void**) rule,
      offsetof(YR_RULE, identifier),
      offsetof(YR_RULE, tags),
      offsetof(YR_RULE, strings),
      offsetof(YR_RULE, metas),
      offsetof(YR_RULE, ns),
      EOL))

  (*rule)->g_flags = flags;
  (*rule)->ns = compiler->current_namespace;
  (*rule)->num_atoms = 0;

  #ifdef PROFILING_ENABLED
  (*rule)->time_cost = 0;

  memset(
      (*rule)->time_cost_per_thread, 0, sizeof((*rule)->time_cost_per_thread));
  #endif

  FAIL_ON_ERROR(yr_arena_write_string(
      compiler->sz_arena,
      identifier,
      (char**) &(*rule)->identifier));

  FAIL_ON_ERROR(yr_parser_emit(
      yyscanner,
      OP_INIT_RULE,
      NULL));

  FAIL_ON_ERROR(yr_arena_allocate_struct(
      compiler->code_arena,
      sizeof(YR_INIT_RULE_ARGS),
      (void**) &init_rule_args,
      offsetof(YR_INIT_RULE_ARGS, rule),
      offsetof(YR_INIT_RULE_ARGS, jmp_addr),
      EOL));

  init_rule_args->rule = *rule;

  // jmp_addr holds the address to jump to when we want to skip the code for
  // the rule. It is iniatialized as NULL at this point because we don't know
  // the address until emmiting the code for the rule's condition. The address
  // is set in yr_parser_reduce_rule_declaration_phase_2.
  init_rule_args->jmp_addr = NULL;

  // Create a fixup entry for the jump and push it in the stack
  fixup = (YR_FIXUP*) yr_malloc(sizeof(YR_FIXUP));

  if (fixup == NULL)
    return ERROR_INSUFFICIENT_MEMORY;

  fixup->address = (void*) &(init_rule_args->jmp_addr);
  fixup->next = compiler->fixup_stack_head;
  compiler->fixup_stack_head = fixup;

  // Clean strings_table as we are starting to parse a new rule.
  yr_hash_table_clean(compiler->strings_table, NULL);

  FAIL_ON_ERROR(yr_hash_table_add(
      compiler->rules_table,
      identifier,
      compiler->current_namespace->name,
      (void*) *rule));

  compiler->current_rule = *rule;

  return ERROR_SUCCESS;
}
Пример #9
0
int yr_parser_reduce_import(
    yyscan_t yyscanner,
    SIZED_STRING* module_name)
{
  int result;

  YR_COMPILER* compiler = yyget_extra(yyscanner);
  YR_OBJECT* module_structure;

  char* name;

  if (!_yr_parser_valid_module_name(module_name))
  {
    yr_compiler_set_error_extra_info(compiler, module_name->c_string);
    return ERROR_INVALID_MODULE_NAME;
  }

  module_structure = (YR_OBJECT*) yr_hash_table_lookup(
      compiler->objects_table,
      module_name->c_string,
      compiler->current_namespace->name);

  // if module already imported, do nothing

  if (module_structure != NULL)
    return ERROR_SUCCESS;

  FAIL_ON_ERROR(yr_object_create(
      OBJECT_TYPE_STRUCTURE,
      module_name->c_string,
      NULL,
      &module_structure));

  FAIL_ON_ERROR(yr_hash_table_add(
      compiler->objects_table,
      module_name->c_string,
      compiler->current_namespace->name,
      module_structure));

  result = yr_modules_do_declarations(
      module_name->c_string,
      module_structure);

  if (result == ERROR_UNKNOWN_MODULE)
    yr_compiler_set_error_extra_info(compiler, module_name->c_string);

  if (result != ERROR_SUCCESS)
    return result;

  FAIL_ON_ERROR(yr_arena_write_string(
      compiler->sz_arena,
      module_name->c_string,
      &name));

  FAIL_ON_ERROR(yr_parser_emit_with_arg_reloc(
        yyscanner,
        OP_IMPORT,
        name,
        NULL,
        NULL));

  return ERROR_SUCCESS;
}
Пример #10
0
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;
}