mirror of
https://github.com/VirusTotal/yara.git
synced 2026-10-12 02:10:00 +00:00
conditional-jump opcodes add an unchecked 4-byte offset from the compiled bytecode to the instruction pointer, so a crafted .yrc whose taken jump lands past the code section makes yr_execute_code fetch the next opcode out of bounds; reject any ip that leaves the code buffer.
2461 lines
65 KiB
C
2461 lines
65 KiB
C
/*
|
|
Copyright (c) 2013-2014. The YARA Authors. All Rights Reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
are permitted provided that the following conditions are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright notice, this
|
|
list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
this list of conditions and the following disclaimer in the documentation and/or
|
|
other materials provided with the distribution.
|
|
|
|
3. Neither the name of the copyright holder nor the names of its contributors
|
|
may be used to endorse or promote products derived from this software without
|
|
specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <float.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <yara.h>
|
|
#include <yara/arena.h>
|
|
#include <yara/compiler.h>
|
|
#include <yara/endian.h>
|
|
#include <yara/error.h>
|
|
#include <yara/exec.h>
|
|
#include <yara/globals.h>
|
|
#include <yara/limits.h>
|
|
#include <yara/mem.h>
|
|
#include <yara/modules.h>
|
|
#include <yara/object.h>
|
|
#include <yara/re.h>
|
|
#include <yara/sizedstr.h>
|
|
#include <yara/stopwatch.h>
|
|
#include <yara/strutils.h>
|
|
#include <yara/unaligned.h>
|
|
#include <yara/utils.h>
|
|
|
|
#define MEM_SIZE YR_MAX_LOOP_NESTING*(YR_MAX_LOOP_VARS + YR_INTERNAL_LOOP_VARS)
|
|
|
|
#define push(x) \
|
|
if (stack.sp < stack.capacity) \
|
|
{ \
|
|
stack.items[stack.sp++] = (x); \
|
|
} \
|
|
else \
|
|
{ \
|
|
result = ERROR_EXEC_STACK_OVERFLOW; \
|
|
stop = true; \
|
|
break; \
|
|
}
|
|
|
|
#define pop(x) \
|
|
{ \
|
|
assert(stack.sp > 0); \
|
|
x = stack.items[--stack.sp]; \
|
|
}
|
|
|
|
#define is_undef(x) IS_UNDEFINED((x).i)
|
|
|
|
#define ensure_defined(x) \
|
|
if (is_undef(x)) \
|
|
{ \
|
|
r1.i = YR_UNDEFINED; \
|
|
push(r1); \
|
|
break; \
|
|
}
|
|
|
|
#define ensure_within_mem(x) \
|
|
if (x < 0 || x >= MEM_SIZE) \
|
|
{ \
|
|
stop = true; \
|
|
result = ERROR_INTERNAL_FATAL_ERROR; \
|
|
break; \
|
|
}
|
|
|
|
// Make sure that the string pointer is within the rules arena.
|
|
#define ensure_within_rules_arena(x) \
|
|
{ \
|
|
YR_ARENA_REF ref; \
|
|
if (yr_arena_ptr_to_ref(context->rules->arena, x, &ref) == 0) \
|
|
{ \
|
|
stop = true; \
|
|
result = ERROR_INTERNAL_FATAL_ERROR; \
|
|
break; \
|
|
} \
|
|
}
|
|
|
|
// Make sure that the instruction pointer stays within the code section. A
|
|
// crafted compiled-rules file can carry a jump whose offset moves ip outside
|
|
// the code buffer, and the dispatch loop would then read the opcode out of
|
|
// bounds.
|
|
#define ensure_within_code(x) \
|
|
if ((x) < code_start || (x) >= code_end) \
|
|
{ \
|
|
stop = true; \
|
|
result = ERROR_INTERNAL_FATAL_ERROR; \
|
|
break; \
|
|
}
|
|
|
|
#define check_object_canary(o) \
|
|
if (o->canary != context->canary) \
|
|
{ \
|
|
stop = true; \
|
|
result = ERROR_INTERNAL_FATAL_ERROR; \
|
|
break; \
|
|
}
|
|
|
|
#define little_endian_uint8_t(x) (x)
|
|
#define little_endian_int8_t(x) (x)
|
|
#define little_endian_uint16_t(x) yr_le16toh(x)
|
|
#define little_endian_int16_t(x) yr_le16toh(x)
|
|
#define little_endian_uint32_t(x) yr_le32toh(x)
|
|
#define little_endian_int32_t(x) yr_le32toh(x)
|
|
|
|
#define big_endian_uint8_t(x) (x)
|
|
#define big_endian_int8_t(x) (x)
|
|
#define big_endian_uint16_t(x) yr_be16toh(x)
|
|
#define big_endian_int16_t(x) yr_be16toh(x)
|
|
#define big_endian_uint32_t(x) yr_be32toh(x)
|
|
#define big_endian_int32_t(x) yr_be32toh(x)
|
|
|
|
#define function_read(type, endianess) \
|
|
int64_t read_##type##_##endianess( \
|
|
YR_MEMORY_BLOCK_ITERATOR* iterator, size_t offset) \
|
|
{ \
|
|
YR_MEMORY_BLOCK* block = iterator->first(iterator); \
|
|
while (block != NULL) \
|
|
{ \
|
|
if (offset >= block->base && block->size >= sizeof(type) && \
|
|
offset <= block->base + block->size - sizeof(type)) \
|
|
{ \
|
|
type result; \
|
|
const uint8_t* data = yr_fetch_block_data(block); \
|
|
if (data == NULL) \
|
|
return YR_UNDEFINED; \
|
|
result = *(type*) (data + offset - block->base); \
|
|
result = endianess##_##type(result); \
|
|
return result; \
|
|
} \
|
|
block = iterator->next(iterator); \
|
|
} \
|
|
return YR_UNDEFINED; \
|
|
};
|
|
|
|
function_read(uint8_t, little_endian);
|
|
function_read(uint16_t, little_endian);
|
|
function_read(uint32_t, little_endian);
|
|
function_read(int8_t, little_endian);
|
|
function_read(int16_t, little_endian);
|
|
function_read(int32_t, little_endian);
|
|
function_read(uint8_t, big_endian);
|
|
function_read(uint16_t, big_endian);
|
|
function_read(uint32_t, big_endian);
|
|
function_read(int8_t, big_endian);
|
|
function_read(int16_t, big_endian);
|
|
function_read(int32_t, big_endian);
|
|
|
|
static const uint8_t* jmp_if(int condition, const uint8_t* ip)
|
|
{
|
|
int32_t off = 0;
|
|
|
|
if (condition)
|
|
{
|
|
// The condition is true, the instruction pointer (ip) is incremented in
|
|
// the amount specified by the jump's offset, which is a int32_t following
|
|
// the jump opcode. The ip is currently past the opcode and pointing to
|
|
// the offset.
|
|
|
|
// Copy the offset from the instruction stream to a local variable.
|
|
off = yr_unaligned_u32(ip);
|
|
|
|
// The offset is relative to the jump opcode, but now the ip is one byte
|
|
// past the opcode, so we need to decrement it by one.
|
|
off -= 1;
|
|
}
|
|
else
|
|
{
|
|
// The condition is false, the execution flow proceeds with the instruction
|
|
// right after the jump.
|
|
off = sizeof(int32_t);
|
|
}
|
|
|
|
return ip + off;
|
|
}
|
|
|
|
static int iter_array_next(YR_ITERATOR* self, YR_VALUE_STACK* stack)
|
|
{
|
|
// Check that there's two available slots in the stack, one for the next
|
|
// item returned by the iterator and another one for the boolean that
|
|
// indicates if there are more items.
|
|
if (stack->sp + 1 >= stack->capacity)
|
|
return ERROR_EXEC_STACK_OVERFLOW;
|
|
|
|
// If the array that must be iterated is undefined stop the iteration right
|
|
// aways, as if the array would be empty.
|
|
if (IS_UNDEFINED(self->array_it.array))
|
|
goto _stop_iter;
|
|
|
|
// If the current index is equal or larger than array's length the iterator
|
|
// has reached the end of the array.
|
|
if (self->array_it.index >= yr_object_array_length(self->array_it.array))
|
|
goto _stop_iter;
|
|
|
|
// Push the false value that indicates that the iterator is not exhausted.
|
|
stack->items[stack->sp++].i = 0;
|
|
|
|
YR_OBJECT* obj = yr_object_array_get_item(
|
|
self->array_it.array, 0, self->array_it.index);
|
|
|
|
if (obj != NULL)
|
|
stack->items[stack->sp++].o = obj;
|
|
else
|
|
stack->items[stack->sp++].i = YR_UNDEFINED;
|
|
|
|
self->array_it.index++;
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
_stop_iter:
|
|
|
|
// Push true for indicating the iterator has been exhausted.
|
|
stack->items[stack->sp++].i = 1;
|
|
// Push YR_UNDEFINED as a placeholder for the next item.
|
|
stack->items[stack->sp++].i = YR_UNDEFINED;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static int iter_dict_next(YR_ITERATOR* self, YR_VALUE_STACK* stack)
|
|
{
|
|
// Check that there's three available slots in the stack, two for the next
|
|
// item returned by the iterator and its key, and another one for the boolean
|
|
// that indicates if there are more items.
|
|
if (stack->sp + 2 >= stack->capacity)
|
|
return ERROR_EXEC_STACK_OVERFLOW;
|
|
|
|
// If the dictionary that must be iterated is undefined, stop the iteration
|
|
// right away, as if the dictionary would be empty.
|
|
if (IS_UNDEFINED(self->dict_it.dict))
|
|
goto _stop_iter;
|
|
|
|
YR_DICTIONARY_ITEMS* items = object_as_dictionary(self->dict_it.dict)->items;
|
|
|
|
// If the dictionary has no items or the iterator reached the last item, abort
|
|
// the iteration, if not push the next key and value.
|
|
if (items == NULL || self->dict_it.index == items->used)
|
|
goto _stop_iter;
|
|
|
|
// Push the false value that indicates that the iterator is not exhausted.
|
|
stack->items[stack->sp++].i = 0;
|
|
|
|
if (items->objects[self->dict_it.index].obj != NULL)
|
|
{
|
|
stack->items[stack->sp++].o = items->objects[self->dict_it.index].obj;
|
|
stack->items[stack->sp++].p = items->objects[self->dict_it.index].key;
|
|
}
|
|
else
|
|
{
|
|
stack->items[stack->sp++].i = YR_UNDEFINED;
|
|
stack->items[stack->sp++].i = YR_UNDEFINED;
|
|
}
|
|
|
|
self->dict_it.index++;
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
_stop_iter:
|
|
|
|
// Push true for indicating the iterator has been exhausted.
|
|
stack->items[stack->sp++].i = 1;
|
|
// Push YR_UNDEFINED as a placeholder for the next key and value.
|
|
stack->items[stack->sp++].i = YR_UNDEFINED;
|
|
stack->items[stack->sp++].i = YR_UNDEFINED;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static int iter_int_range_next(YR_ITERATOR* self, YR_VALUE_STACK* stack)
|
|
{
|
|
// Check that there's two available slots in the stack, one for the next
|
|
// item returned by the iterator and another one for the boolean that
|
|
// indicates if there are more items.
|
|
if (stack->sp + 1 >= stack->capacity)
|
|
return ERROR_EXEC_STACK_OVERFLOW;
|
|
|
|
if (!IS_UNDEFINED(self->int_range_it.next) &&
|
|
!IS_UNDEFINED(self->int_range_it.last) &&
|
|
self->int_range_it.next <= self->int_range_it.last)
|
|
{
|
|
// Push the false value that indicates that the iterator is not exhausted.
|
|
stack->items[stack->sp++].i = 0;
|
|
stack->items[stack->sp++].i = self->int_range_it.next;
|
|
self->int_range_it.next++;
|
|
}
|
|
else
|
|
{
|
|
// Push true for indicating the iterator has been exhausted.
|
|
stack->items[stack->sp++].i = 1;
|
|
// Push YR_UNDEFINED as a placeholder for the next item.
|
|
stack->items[stack->sp++].i = YR_UNDEFINED;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static int iter_int_enum_next(YR_ITERATOR* self, YR_VALUE_STACK* stack)
|
|
{
|
|
// Check that there's two available slots in the stack, one for the next
|
|
// item returned by the iterator and another one for the boolean that
|
|
// indicates if there are more items.
|
|
if (stack->sp + 1 >= stack->capacity)
|
|
return ERROR_EXEC_STACK_OVERFLOW;
|
|
|
|
if (!IS_UNDEFINED(self->int_enum_it.next) &&
|
|
!IS_UNDEFINED(self->int_enum_it.count) &&
|
|
self->int_enum_it.next < self->int_enum_it.count)
|
|
{
|
|
// Push the false value that indicates that the iterator is not exhausted.
|
|
stack->items[stack->sp++].i = 0;
|
|
stack->items[stack->sp++].i =
|
|
self->int_enum_it.items[self->int_enum_it.next];
|
|
self->int_enum_it.next++;
|
|
}
|
|
else
|
|
{
|
|
// Push true for indicating the iterator has been exhausted.
|
|
stack->items[stack->sp++].i = 1;
|
|
// Push YR_UNDEFINED as a placeholder for the next item.
|
|
stack->items[stack->sp++].i = YR_UNDEFINED;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static int iter_string_set_next(YR_ITERATOR* self, YR_VALUE_STACK* stack)
|
|
{
|
|
// Check that there's two available slots in the stack, one for the next
|
|
// item returned by the iterator and another one for the boolean that
|
|
// indicates if there are more items.
|
|
if (stack->sp + 1 >= stack->capacity)
|
|
return ERROR_EXEC_STACK_OVERFLOW;
|
|
|
|
// If the current index is equal or larger than array's length the iterator
|
|
// has reached the end of the array.
|
|
if (self->string_set_it.index >= self->string_set_it.count)
|
|
goto _stop_iter;
|
|
|
|
// Push the false value that indicates that the iterator is not exhausted.
|
|
stack->items[stack->sp++].i = 0;
|
|
stack->items[stack->sp++].s =
|
|
self->string_set_it.strings[self->string_set_it.index];
|
|
self->string_set_it.index++;
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
_stop_iter:
|
|
|
|
// Push true for indicating the iterator has been exhausted.
|
|
stack->items[stack->sp++].i = 1;
|
|
// Push YR_UNDEFINED as a placeholder for the next item.
|
|
stack->items[stack->sp++].i = YR_UNDEFINED;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static int iter_text_string_set_next(YR_ITERATOR* self, YR_VALUE_STACK* stack)
|
|
{
|
|
// Check that there's two available slots in the stack, one for the next
|
|
// item returned by the iterator and another one for the boolean that
|
|
// indicates if there are more items.
|
|
if (stack->sp + 1 >= stack->capacity)
|
|
return ERROR_EXEC_STACK_OVERFLOW;
|
|
|
|
// If the current index is equal or larger than array's length the iterator
|
|
// has reached the end of the array.
|
|
if (self->text_string_set_it.index >= self->text_string_set_it.count)
|
|
goto _stop_iter;
|
|
|
|
// Push the false value that indicates that the iterator is not exhausted.
|
|
stack->items[stack->sp++].i = 0;
|
|
stack->items[stack->sp++].ss =
|
|
self->text_string_set_it.strings[self->text_string_set_it.index];
|
|
self->text_string_set_it.index++;
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
_stop_iter:
|
|
|
|
// Push true for indicating the iterator has been exhausted.
|
|
stack->items[stack->sp++].i = 1;
|
|
// Push YR_UNDEFINED as a placeholder for the next item.
|
|
stack->items[stack->sp++].i = YR_UNDEFINED;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
// Global table that contains the "next" function for different types of
|
|
// iterators. The reason for using this table is to avoid storing pointers
|
|
// in the YARA's VM stack. Instead of the pointers we store an index within
|
|
// this table.
|
|
static YR_ITERATOR_NEXT_FUNC iter_next_func_table[] = {
|
|
iter_array_next,
|
|
iter_dict_next,
|
|
iter_int_range_next,
|
|
iter_int_enum_next,
|
|
iter_string_set_next,
|
|
iter_text_string_set_next,
|
|
};
|
|
|
|
#define ITER_NEXT_ARRAY 0
|
|
#define ITER_NEXT_DICT 1
|
|
#define ITER_NEXT_INT_RANGE 2
|
|
#define ITER_NEXT_INT_ENUM 3
|
|
#define ITER_NEXT_STRING_SET 4
|
|
#define ITER_NEXT_TEXT_STRING_SET 5
|
|
|
|
int yr_execute_code(YR_SCAN_CONTEXT* context)
|
|
{
|
|
YR_DEBUG_FPRINTF(2, stderr, "+ %s() {\n", __FUNCTION__);
|
|
|
|
const uint8_t* code_start = context->rules->code_start;
|
|
const uint8_t* ip = code_start;
|
|
|
|
#if YR_PARANOID_EXEC
|
|
// One byte past the last instruction of the code section, used by
|
|
// ensure_within_code to reject a jump that leaves the code buffer.
|
|
const uint8_t* code_end = code_start +
|
|
yr_arena_get_current_offset(
|
|
context->rules->arena, YR_CODE_SECTION);
|
|
#endif
|
|
|
|
YR_VALUE mem[MEM_SIZE];
|
|
YR_VALUE args[YR_MAX_FUNCTION_ARGS];
|
|
YR_VALUE r1;
|
|
YR_VALUE r2;
|
|
YR_VALUE r3;
|
|
YR_VALUE r4;
|
|
|
|
YR_VALUE_STACK stack;
|
|
|
|
uint64_t elapsed_time;
|
|
|
|
#ifdef YR_PROFILING_ENABLED
|
|
uint64_t start_time;
|
|
#endif
|
|
|
|
uint32_t current_rule_idx = 0;
|
|
YR_RULE* current_rule = NULL;
|
|
YR_RULE* rule;
|
|
YR_MATCH* match;
|
|
YR_OBJECT_FUNCTION* function;
|
|
YR_OBJECT** obj_ptr;
|
|
YR_ARENA* obj_arena;
|
|
YR_NOTEBOOK* it_notebook;
|
|
|
|
char* identifier;
|
|
char* args_fmt;
|
|
|
|
int found;
|
|
int count;
|
|
int result = ERROR_SUCCESS;
|
|
int cycle = 0;
|
|
int obj_count = 0;
|
|
|
|
bool stop = false;
|
|
|
|
uint8_t opcode;
|
|
|
|
yr_get_configuration_uint32(YR_CONFIG_STACK_SIZE, &stack.capacity);
|
|
|
|
stack.sp = 0;
|
|
stack.items = (YR_VALUE*) yr_malloc(stack.capacity * sizeof(YR_VALUE));
|
|
|
|
if (stack.items == NULL)
|
|
return ERROR_INSUFFICIENT_MEMORY;
|
|
|
|
FAIL_ON_ERROR_WITH_CLEANUP(
|
|
yr_arena_create(1, 512 * sizeof(YR_OBJECT*), &obj_arena),
|
|
yr_free(stack.items));
|
|
|
|
FAIL_ON_ERROR_WITH_CLEANUP(
|
|
yr_notebook_create(512 * sizeof(YR_ITERATOR), &it_notebook),
|
|
yr_arena_release(obj_arena);
|
|
yr_free(stack.items));
|
|
|
|
#ifdef YR_PROFILING_ENABLED
|
|
start_time = yr_stopwatch_elapsed_ns(&context->stopwatch);
|
|
#endif
|
|
|
|
#if YR_PARANOID_EXEC
|
|
memset(mem, 0, MEM_SIZE * sizeof(mem[0]));
|
|
#endif
|
|
|
|
while (!stop)
|
|
{
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_code(ip);
|
|
#endif
|
|
|
|
// Read the opcode from the address indicated by the instruction pointer.
|
|
opcode = *ip;
|
|
|
|
// Advance the instruction pointer, which now points past the opcode.
|
|
ip++;
|
|
|
|
switch (opcode)
|
|
{
|
|
case OP_NOP:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_NOP: // %s()\n", __FUNCTION__);
|
|
break;
|
|
|
|
case OP_HALT:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_HALT: // %s()\n", __FUNCTION__);
|
|
assert(stack.sp == 0); // When HALT is reached the stack should be empty.
|
|
stop = true;
|
|
break;
|
|
|
|
case OP_ITER_START_ARRAY:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_ITER_START_ARRAY: // %s()\n", __FUNCTION__);
|
|
r2.p = yr_notebook_alloc(it_notebook, sizeof(YR_ITERATOR));
|
|
|
|
if (r2.p == NULL)
|
|
{
|
|
result = ERROR_INSUFFICIENT_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
pop(r1);
|
|
r2.it->array_it.array = r1.o;
|
|
r2.it->array_it.index = 0;
|
|
r2.it->next_func_idx = ITER_NEXT_ARRAY;
|
|
push(r2);
|
|
}
|
|
|
|
stop = (result != ERROR_SUCCESS);
|
|
break;
|
|
|
|
case OP_ITER_START_DICT:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_ITER_START_DICT: // %s()\n", __FUNCTION__);
|
|
r2.p = yr_notebook_alloc(it_notebook, sizeof(YR_ITERATOR));
|
|
|
|
if (r2.p == NULL)
|
|
{
|
|
result = ERROR_INSUFFICIENT_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
pop(r1);
|
|
r2.it->dict_it.dict = r1.o;
|
|
r2.it->dict_it.index = 0;
|
|
r2.it->next_func_idx = ITER_NEXT_DICT;
|
|
push(r2);
|
|
}
|
|
|
|
stop = (result != ERROR_SUCCESS);
|
|
break;
|
|
|
|
case OP_ITER_START_INT_RANGE:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_ITER_START_INT_RANGE: // %s()\n", __FUNCTION__);
|
|
// Creates an iterator for an integer range. The higher bound of the
|
|
// range is at the top of the stack followed by the lower bound.
|
|
r3.p = yr_notebook_alloc(it_notebook, sizeof(YR_ITERATOR));
|
|
|
|
if (r3.p == NULL)
|
|
{
|
|
result = ERROR_INSUFFICIENT_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
pop(r2);
|
|
pop(r1);
|
|
r3.it->int_range_it.next = r1.i;
|
|
r3.it->int_range_it.last = r2.i;
|
|
r3.it->next_func_idx = ITER_NEXT_INT_RANGE;
|
|
push(r3);
|
|
}
|
|
|
|
stop = (result != ERROR_SUCCESS);
|
|
break;
|
|
|
|
case OP_ITER_START_INT_ENUM:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_ITER_START_INT_ENUM: // %s()\n", __FUNCTION__);
|
|
// Creates an iterator for an integer enumeration. The number of items
|
|
// in the enumeration is at the top of the stack, followed by the
|
|
// items in reverse order.
|
|
pop(r1);
|
|
|
|
// The count comes from the compiled rules and a hand-crafted file can
|
|
// set it to a value that overflows the allocation size below or makes
|
|
// the loop pop past the stack. It can't be larger than the number of
|
|
// items currently on the stack.
|
|
if (r1.i < 0 || (uint64_t) r1.i > stack.sp)
|
|
{
|
|
result = ERROR_INTERNAL_FATAL_ERROR;
|
|
stop = true;
|
|
break;
|
|
}
|
|
|
|
r3.p = yr_notebook_alloc(
|
|
it_notebook, sizeof(YR_ITERATOR) + sizeof(uint64_t) * (size_t) r1.i);
|
|
|
|
if (r3.p == NULL)
|
|
{
|
|
result = ERROR_INSUFFICIENT_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
r3.it->int_enum_it.count = r1.i;
|
|
r3.it->int_enum_it.next = 0;
|
|
r3.it->next_func_idx = ITER_NEXT_INT_ENUM;
|
|
|
|
for (int64_t i = r1.i; i > 0; i--)
|
|
{
|
|
pop(r2);
|
|
r3.it->int_enum_it.items[i - 1] = r2.i;
|
|
}
|
|
|
|
push(r3);
|
|
}
|
|
|
|
stop = (result != ERROR_SUCCESS);
|
|
break;
|
|
|
|
case OP_ITER_START_STRING_SET:
|
|
YR_DEBUG_FPRINTF(
|
|
2,
|
|
stderr,
|
|
"- case OP_ITER_START_STRING_SET: // %s()\n",
|
|
__FUNCTION__);
|
|
|
|
pop(r1);
|
|
|
|
// One extra value (the undefined string) is popped below, so the count
|
|
// must be strictly smaller than the number of items on the stack. A
|
|
// hand-crafted count would otherwise overflow the allocation or pop past
|
|
// the stack.
|
|
if (r1.i < 0 || (uint64_t) r1.i >= stack.sp)
|
|
{
|
|
result = ERROR_INTERNAL_FATAL_ERROR;
|
|
stop = true;
|
|
break;
|
|
}
|
|
|
|
r3.p = yr_notebook_alloc(
|
|
it_notebook,
|
|
sizeof(YR_ITERATOR) + sizeof(YR_STRING*) * (size_t) r1.i);
|
|
|
|
if (r3.p == NULL)
|
|
{
|
|
result = ERROR_INSUFFICIENT_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
r3.it->string_set_it.count = r1.i;
|
|
r3.it->string_set_it.index = 0;
|
|
r3.it->next_func_idx = ITER_NEXT_STRING_SET;
|
|
|
|
for (int64_t i = r1.i; i > 0; i--)
|
|
{
|
|
pop(r2);
|
|
r3.it->string_set_it.strings[i - 1] = r2.s;
|
|
}
|
|
|
|
// One last pop of the UNDEFINED string
|
|
pop(r2);
|
|
push(r3);
|
|
}
|
|
|
|
stop = (result != ERROR_SUCCESS);
|
|
break;
|
|
|
|
case OP_ITER_START_TEXT_STRING_SET:
|
|
YR_DEBUG_FPRINTF(
|
|
2,
|
|
stderr,
|
|
"- case OP_ITER_START_TEXT_STRING_SET: // %s()\n",
|
|
__FUNCTION__);
|
|
|
|
pop(r1);
|
|
|
|
// The count comes from the compiled rules and can't be larger than the
|
|
// number of items on the stack, otherwise the allocation below overflows
|
|
// or the loop pops past the stack.
|
|
if (r1.i < 0 || (uint64_t) r1.i > stack.sp)
|
|
{
|
|
result = ERROR_INTERNAL_FATAL_ERROR;
|
|
stop = true;
|
|
break;
|
|
}
|
|
|
|
r3.p = yr_notebook_alloc(
|
|
it_notebook,
|
|
sizeof(YR_ITERATOR) + sizeof(SIZED_STRING*) * (size_t) r1.i);
|
|
|
|
if (r3.p == NULL)
|
|
{
|
|
result = ERROR_INSUFFICIENT_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
r3.it->text_string_set_it.count = r1.i;
|
|
r3.it->text_string_set_it.index = 0;
|
|
r3.it->next_func_idx = ITER_NEXT_TEXT_STRING_SET;
|
|
|
|
for (int64_t i = r1.i; i > 0; i--)
|
|
{
|
|
pop(r2);
|
|
r3.it->text_string_set_it.strings[i - 1] = r2.ss;
|
|
}
|
|
|
|
push(r3);
|
|
}
|
|
|
|
stop = (result != ERROR_SUCCESS);
|
|
break;
|
|
|
|
case OP_ITER_NEXT:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_ITER_NEXT: // %s()\n", __FUNCTION__);
|
|
// Loads the iterator in r1, but leaves the iterator in the stack.
|
|
pop(r1);
|
|
push(r1);
|
|
|
|
if (r1.it->next_func_idx <
|
|
sizeof(iter_next_func_table) / sizeof(YR_ITERATOR_NEXT_FUNC))
|
|
{
|
|
// The iterator's next function is responsible for pushing the next
|
|
// item in the stack, and a boolean indicating if there are more items
|
|
// to retrieve. The boolean will be at the top of the stack after
|
|
// calling "next".
|
|
result = iter_next_func_table[r1.it->next_func_idx](r1.it, &stack);
|
|
}
|
|
else
|
|
{
|
|
// next_func_idx is outside the valid range, this should not happend.
|
|
result = ERROR_INTERNAL_FATAL_ERROR;
|
|
}
|
|
|
|
stop = (result != ERROR_SUCCESS);
|
|
break;
|
|
|
|
case OP_ITER_CONDITION:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_ITER_CONDITION: // %s()\n", __FUNCTION__);
|
|
|
|
// Evaluate the iteration condition of the loop. This instruction
|
|
// evaluates to 1 if the loop should continue and 0 if it shouldn't
|
|
// (due to short-circuit evaluation).
|
|
|
|
pop(r2); // min. expression - all, any, none, integer
|
|
pop(r3); // number of true expressions
|
|
pop(r4); // last expression result
|
|
|
|
// In case of 'all' loop, end once we the body failed
|
|
if (is_undef(r2))
|
|
{
|
|
r1.i = r4.i != 0 ? 1 : 0;
|
|
}
|
|
// In case of 'none' loop, end once the body succeed
|
|
else if (r2.i == 0)
|
|
{
|
|
r1.i = r4.i != 1 ? 1 : 0;
|
|
}
|
|
// In case of other loops, end once we satified min. expr.
|
|
else
|
|
{
|
|
r1.i = r3.i + r4.i < r2.i ? 1 : 0;
|
|
}
|
|
|
|
// Push whether loop should continue and repush
|
|
// the last expression result
|
|
push(r1);
|
|
push(r4);
|
|
break;
|
|
|
|
case OP_ITER_END:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_ITER_END: // %s()\n", __FUNCTION__);
|
|
|
|
// Evaluate the whole loop. Whether it was successful or not
|
|
// and whether it satisfied it's quantifier.
|
|
|
|
pop(r2); // min. expression - all, any, none, integer
|
|
pop(r3); // number of true expressions
|
|
pop(r4); // number of total iterations
|
|
|
|
// If there was 0 iterations in total, it doesn't
|
|
// matter what other numbers show. We can't evaluate
|
|
// the loop as true.
|
|
if (r4.i == 0)
|
|
{
|
|
r1.i = 0;
|
|
}
|
|
else if (is_undef(r2))
|
|
{
|
|
r1.i = r3.i == r4.i ? 1 : 0;
|
|
}
|
|
else if (r2.i == 0)
|
|
{
|
|
r1.i = r3.i == 0 ? 1 : 0;
|
|
}
|
|
else
|
|
{
|
|
r1.i = r3.i >= r2.i ? 1 : 0;
|
|
}
|
|
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_PUSH:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_PUSH: // %s()\n", __FUNCTION__);
|
|
r1.i = yr_unaligned_u64(ip);
|
|
ip += sizeof(uint64_t);
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_PUSH_8:
|
|
r1.i = *ip;
|
|
YR_DEBUG_FPRINTF(
|
|
2,
|
|
stderr,
|
|
"- case OP_PUSH_8: r1.i=%" PRId64 " // %s()\n",
|
|
r1.i,
|
|
__FUNCTION__);
|
|
ip += sizeof(uint8_t);
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_PUSH_16:
|
|
r1.i = yr_unaligned_u16(ip);
|
|
YR_DEBUG_FPRINTF(
|
|
2,
|
|
stderr,
|
|
"- case OP_PUSH_16: r1.i=%" PRId64 " // %s()\n",
|
|
r1.i,
|
|
__FUNCTION__);
|
|
ip += sizeof(uint16_t);
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_PUSH_32:
|
|
r1.i = yr_unaligned_u32(ip);
|
|
YR_DEBUG_FPRINTF(
|
|
2,
|
|
stderr,
|
|
"- case OP_PUSH_32: r1.i=%" PRId64 " // %s()\n",
|
|
r1.i,
|
|
__FUNCTION__);
|
|
ip += sizeof(uint32_t);
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_PUSH_U:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_PUSH_U: // %s()\n", __FUNCTION__);
|
|
r1.i = YR_UNDEFINED;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_POP:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_POP: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
break;
|
|
|
|
case OP_CLEAR_M:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_CLEAR_M: // %s()\n", __FUNCTION__);
|
|
r1.i = yr_unaligned_u64(ip);
|
|
ip += sizeof(uint64_t);
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_mem(r1.i);
|
|
#endif
|
|
mem[r1.i].i = 0;
|
|
break;
|
|
|
|
case OP_ADD_M:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_ADD_M: // %s()\n", __FUNCTION__);
|
|
r1.i = yr_unaligned_u64(ip);
|
|
ip += sizeof(uint64_t);
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_mem(r1.i);
|
|
#endif
|
|
pop(r2);
|
|
if (!is_undef(r2))
|
|
mem[r1.i].i += r2.i;
|
|
break;
|
|
|
|
case OP_INCR_M:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_INCR_M: // %s()\n", __FUNCTION__);
|
|
r1.i = yr_unaligned_u64(ip);
|
|
ip += sizeof(uint64_t);
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_mem(r1.i);
|
|
#endif
|
|
mem[r1.i].i++;
|
|
break;
|
|
|
|
case OP_PUSH_M:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_PUSH_M: // %s()\n", __FUNCTION__);
|
|
r1.i = yr_unaligned_u64(ip);
|
|
ip += sizeof(uint64_t);
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_mem(r1.i);
|
|
#endif
|
|
r1 = mem[r1.i];
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_POP_M:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_POP_M: // %s()\n", __FUNCTION__);
|
|
r1.i = yr_unaligned_u64(ip);
|
|
ip += sizeof(uint64_t);
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_mem(r1.i);
|
|
#endif
|
|
pop(r2);
|
|
mem[r1.i] = r2;
|
|
break;
|
|
|
|
case OP_SET_M:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_SET_M: // %s()\n", __FUNCTION__);
|
|
r1.i = yr_unaligned_u64(ip);
|
|
ip += sizeof(uint64_t);
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_mem(r1.i);
|
|
#endif
|
|
pop(r2);
|
|
push(r2);
|
|
if (!is_undef(r2))
|
|
mem[r1.i] = r2;
|
|
break;
|
|
|
|
case OP_SWAPUNDEF:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_SWAPUNDEF: // %s()\n", __FUNCTION__);
|
|
r1.i = yr_unaligned_u64(ip);
|
|
ip += sizeof(uint64_t);
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_mem(r1.i);
|
|
#endif
|
|
pop(r2);
|
|
|
|
if (is_undef(r2))
|
|
{
|
|
r1 = mem[r1.i];
|
|
push(r1);
|
|
}
|
|
else
|
|
{
|
|
push(r2);
|
|
}
|
|
break;
|
|
|
|
case OP_JNUNDEF:
|
|
// Jump if the top the stack is not undefined without modifying the stack.
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_JNUNDEF: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
push(r1);
|
|
ip = jmp_if(!is_undef(r1), ip);
|
|
break;
|
|
|
|
case OP_JUNDEF_P:
|
|
// Removes a value from the top of the stack and jump if the value is not
|
|
// undefined.
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_JUNDEF_P: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
ip = jmp_if(is_undef(r1), ip);
|
|
break;
|
|
|
|
case OP_JL_P:
|
|
// Pops two values A and B from the stack and jump if A < B. B is popped
|
|
// first, and then A.
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_JL_P: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ip = jmp_if(r1.i < r2.i, ip);
|
|
break;
|
|
|
|
case OP_JLE_P:
|
|
// Pops two values A and B from the stack and jump if A <= B. B is popped
|
|
// first, and then A.
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_JLE_P: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ip = jmp_if(r1.i <= r2.i, ip);
|
|
break;
|
|
|
|
case OP_JTRUE:
|
|
// Jump if the top of the stack is true without modifying the stack. If
|
|
// the top of the stack is undefined the jump is not taken.
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_JTRUE: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
push(r1);
|
|
ip = jmp_if(!is_undef(r1) && r1.i, ip);
|
|
break;
|
|
|
|
case OP_JTRUE_P:
|
|
// Removes a value from the stack and jump if it is true. If the value
|
|
// is undefined the jump is not taken.
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_JTRUE_P: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
ip = jmp_if(!is_undef(r1) && r1.i, ip);
|
|
break;
|
|
|
|
case OP_JFALSE:
|
|
// Jump if the top of the stack is false without modifying the stack. If
|
|
// the top of the stack is undefined the jump is not taken.
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_JFALSE: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
push(r1);
|
|
ip = jmp_if(!is_undef(r1) && !r1.i, ip);
|
|
break;
|
|
|
|
case OP_JFALSE_P:
|
|
// Removes a value from the stack and jump if it is false. If the value
|
|
// is undefined the jump is not taken.
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_JFALSE_P: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
ip = jmp_if(!is_undef(r1) && !r1.i, ip);
|
|
break;
|
|
|
|
case OP_JZ:
|
|
// Jump if the value at the top of the stack is 0 without modifying the
|
|
// stack.
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_JZ: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
push(r1);
|
|
ip = jmp_if(r1.i == 0, ip);
|
|
break;
|
|
|
|
case OP_JZ_P:
|
|
// Removes a value from the stack and jump if the value is 0.
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_JZ_P: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
ip = jmp_if(r1.i == 0, ip);
|
|
break;
|
|
|
|
case OP_AND:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_AND: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
|
|
if (is_undef(r1))
|
|
r1.i = 0;
|
|
|
|
if (is_undef(r2))
|
|
r2.i = 0;
|
|
|
|
r1.i = r1.i && r2.i;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_OR:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_OR: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
|
|
if (is_undef(r1))
|
|
r1.i = 0;
|
|
|
|
if (is_undef(r2))
|
|
r2.i = 0;
|
|
|
|
r1.i = r1.i || r2.i;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_NOT:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_NOT: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
|
|
if (is_undef(r1))
|
|
r1.i = YR_UNDEFINED;
|
|
else
|
|
r1.i = !r1.i;
|
|
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_DEFINED:
|
|
pop(r1);
|
|
r1.i = !is_undef(r1);
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_MOD:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_MOD: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
// If divisor is zero the result is undefined. It's also undefined
|
|
// when dividing INT64_MIN by -1.
|
|
if (r2.i == 0 || (r1.i == INT64_MIN && r2.i == -1))
|
|
r1.i = YR_UNDEFINED;
|
|
else
|
|
r1.i = r1.i % r2.i;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_SHR:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_SHR: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
if (r2.i < 0)
|
|
r1.i = YR_UNDEFINED;
|
|
else if (r2.i < 64)
|
|
r1.i = r1.i >> r2.i;
|
|
else
|
|
r1.i = 0;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_SHL:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_SHL: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
if (r2.i < 0)
|
|
r1.i = YR_UNDEFINED;
|
|
else if (r2.i < 64)
|
|
r1.i = r1.i << r2.i;
|
|
else
|
|
r1.i = 0;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_BITWISE_NOT:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_BITWISE_NOT: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
ensure_defined(r1);
|
|
r1.i = ~r1.i;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_BITWISE_AND:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_BITWISE_AND: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
r1.i = r1.i & r2.i;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_BITWISE_OR:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_BITWISE_OR: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
r1.i = r1.i | r2.i;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_BITWISE_XOR:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_BITWISE_XOR: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
r1.i = r1.i ^ r2.i;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_PUSH_RULE:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_PUSH_RULE: // %s()\n", __FUNCTION__);
|
|
r1.i = yr_unaligned_u64(ip);
|
|
ip += sizeof(uint64_t);
|
|
|
|
rule = &context->rules->rules_table[r1.i];
|
|
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_rules_arena(rule);
|
|
#endif
|
|
|
|
if (RULE_IS_DISABLED(rule))
|
|
{
|
|
r2.i = YR_UNDEFINED;
|
|
}
|
|
else
|
|
{
|
|
if (yr_bitmask_is_set(context->rule_matches_flags, r1.i))
|
|
r2.i = 1;
|
|
else
|
|
r2.i = 0;
|
|
}
|
|
|
|
push(r2);
|
|
break;
|
|
|
|
case OP_INIT_RULE:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_INIT_RULE: // %s()\n", __FUNCTION__);
|
|
|
|
// After the opcode there's an int32_t corresponding to the jump's
|
|
// offset and an uint32_t corresponding to the rule's index.
|
|
current_rule_idx = yr_unaligned_u32(ip + sizeof(int32_t));
|
|
|
|
// The curent rule index can't be larger than the number of rules.
|
|
assert(current_rule_idx < context->rules->num_rules);
|
|
|
|
current_rule = &context->rules->rules_table[current_rule_idx];
|
|
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_rules_arena(current_rule);
|
|
#endif
|
|
|
|
// If the rule is disabled, let's skip its code.
|
|
bool skip_rule = RULE_IS_DISABLED(current_rule);
|
|
|
|
// The rule is also skipped if it is not required to be evaluated.
|
|
skip_rule |= yr_bitmask_is_not_set(
|
|
context->required_eval, current_rule_idx);
|
|
|
|
ip = jmp_if(skip_rule, ip);
|
|
|
|
if (skip_rule)
|
|
{
|
|
// If the rule is skipped it is false, and if a global rule is false
|
|
// we must mark its namespace as unsatisfied.
|
|
if (RULE_IS_GLOBAL(current_rule))
|
|
yr_bitmask_set(context->ns_unsatisfied_flags, current_rule->ns->idx);
|
|
}
|
|
else
|
|
{
|
|
// If not taking the jump, skip the bytes corresponding to the
|
|
// rule's index.
|
|
ip += sizeof(uint32_t);
|
|
}
|
|
|
|
break;
|
|
|
|
case OP_MATCH_RULE:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_MATCH_RULE: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
|
|
r2.i = yr_unaligned_u64(ip);
|
|
ip += sizeof(uint64_t);
|
|
|
|
rule = &context->rules->rules_table[r2.i];
|
|
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_rules_arena(rule);
|
|
#endif
|
|
|
|
if (!is_undef(r1) && r1.i)
|
|
yr_bitmask_set(context->rule_matches_flags, r2.i);
|
|
else if (RULE_IS_GLOBAL(rule))
|
|
yr_bitmask_set(context->ns_unsatisfied_flags, rule->ns->idx);
|
|
|
|
#ifdef YR_PROFILING_ENABLED
|
|
elapsed_time = yr_stopwatch_elapsed_ns(&context->stopwatch);
|
|
context->profiling_info[r2.i].exec_time += (elapsed_time - start_time);
|
|
start_time = elapsed_time;
|
|
#endif
|
|
|
|
assert(stack.sp == 0); // at this point the stack should be empty.
|
|
break;
|
|
|
|
case OP_OBJ_LOAD:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_OBJ_LOAD: // %s()\n", __FUNCTION__);
|
|
|
|
identifier = yr_unaligned_char_ptr(ip);
|
|
ip += sizeof(uint64_t);
|
|
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_rules_arena(identifier);
|
|
#endif
|
|
|
|
r1.o = (YR_OBJECT*) yr_hash_table_lookup(
|
|
context->objects_table, identifier, NULL);
|
|
|
|
assert(r1.o != NULL);
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_OBJ_FIELD:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_OBJ_FIELD: // %s()\n", __FUNCTION__);
|
|
|
|
identifier = yr_unaligned_char_ptr(ip);
|
|
ip += sizeof(uint64_t);
|
|
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_rules_arena(identifier);
|
|
#endif
|
|
|
|
pop(r1);
|
|
ensure_defined(r1);
|
|
|
|
r1.o = yr_object_lookup_field(r1.o, identifier);
|
|
|
|
if (r1.o == NULL)
|
|
{
|
|
result = ERROR_INVALID_FIELD_NAME;
|
|
stop = true;
|
|
break;
|
|
}
|
|
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_OBJ_VALUE:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_OBJ_VALUE: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
ensure_defined(r1);
|
|
|
|
#if YR_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 (yr_isnan(r1.o->value.d))
|
|
r1.i = YR_UNDEFINED;
|
|
else
|
|
r1.d = r1.o->value.d;
|
|
break;
|
|
|
|
case OBJECT_TYPE_STRING:
|
|
if (r1.o->value.ss == NULL)
|
|
r1.i = YR_UNDEFINED;
|
|
else
|
|
r1.ss = r1.o->value.ss;
|
|
break;
|
|
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_INDEX_ARRAY:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_INDEX_ARRAY: // %s()\n", __FUNCTION__);
|
|
pop(r1); // index
|
|
pop(r2); // array
|
|
|
|
ensure_defined(r1);
|
|
ensure_defined(r2);
|
|
|
|
assert(r2.o->type == OBJECT_TYPE_ARRAY);
|
|
|
|
#if YR_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 = YR_UNDEFINED;
|
|
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_LOOKUP_DICT:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_LOOKUP_DICT: // %s()\n", __FUNCTION__);
|
|
pop(r1); // key
|
|
pop(r2); // dictionary
|
|
|
|
ensure_defined(r1);
|
|
ensure_defined(r2);
|
|
|
|
assert(r2.o->type == OBJECT_TYPE_DICTIONARY);
|
|
|
|
#if YR_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 = YR_UNDEFINED;
|
|
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_CALL:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_CALL: // %s()\n", __FUNCTION__);
|
|
|
|
args_fmt = yr_unaligned_char_ptr(ip);
|
|
ip += sizeof(uint64_t);
|
|
|
|
int i = (int) strlen(args_fmt);
|
|
count = 0;
|
|
|
|
#if YR_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 YR_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 = YR_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, obj_count tracks
|
|
// the number of objects written.
|
|
if (result == ERROR_SUCCESS)
|
|
{
|
|
result = yr_arena_write_data(obj_arena, 0, &r1.o, sizeof(r1.o), NULL);
|
|
obj_count++;
|
|
}
|
|
else
|
|
{
|
|
r1.i = YR_UNDEFINED;
|
|
}
|
|
|
|
stop = (result != ERROR_SUCCESS);
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_FOUND:
|
|
pop(r1);
|
|
r2.i = context->matches[r1.s->idx].tail != NULL ? 1 : 0;
|
|
YR_DEBUG_FPRINTF(
|
|
2,
|
|
stderr,
|
|
"- case OP_FOUND: r2.i=%" PRId64 " // %s()\n",
|
|
r2.i,
|
|
__FUNCTION__);
|
|
push(r2);
|
|
break;
|
|
|
|
case OP_FOUND_AT:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_FOUND_AT: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
|
|
ensure_defined(r1);
|
|
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_rules_arena(r2.p);
|
|
#endif
|
|
|
|
match = context->matches[r2.s->idx].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:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_FOUND_IN: // %s()\n", __FUNCTION__);
|
|
pop(r3);
|
|
pop(r2);
|
|
pop(r1);
|
|
|
|
ensure_defined(r1);
|
|
ensure_defined(r2);
|
|
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_rules_arena(r3.p);
|
|
#endif
|
|
|
|
match = context->matches[r3.s->idx].head;
|
|
r4.i = false;
|
|
|
|
while (match != NULL && !r4.i)
|
|
{
|
|
if (match->base + match->offset >= r1.i &&
|
|
match->base + match->offset <= r2.i)
|
|
{
|
|
r4.i = true;
|
|
}
|
|
|
|
if (match->base + match->offset > r2.i)
|
|
break;
|
|
|
|
match = match->next;
|
|
}
|
|
|
|
push(r4);
|
|
break;
|
|
|
|
case OP_COUNT:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_COUNT: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_rules_arena(r1.p);
|
|
#endif
|
|
|
|
r2.i = context->matches[r1.s->idx].count;
|
|
push(r2);
|
|
break;
|
|
|
|
case OP_COUNT_IN:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_COUNT_IN: // %s()\n", __FUNCTION__);
|
|
pop(r3);
|
|
pop(r2);
|
|
pop(r1);
|
|
|
|
ensure_defined(r1);
|
|
ensure_defined(r2);
|
|
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_rules_arena(r3.p);
|
|
#endif
|
|
|
|
match = context->matches[r3.s->idx].head;
|
|
r4.i = 0;
|
|
|
|
while (match != NULL)
|
|
{
|
|
if (match->base + match->offset >= r1.i &&
|
|
match->base + match->offset <= r2.i)
|
|
{
|
|
r4.i++;
|
|
}
|
|
|
|
if (match->base + match->offset > r2.i)
|
|
break;
|
|
|
|
match = match->next;
|
|
}
|
|
|
|
push(r4);
|
|
break;
|
|
|
|
case OP_OFFSET:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_OFFSET: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
|
|
ensure_defined(r1);
|
|
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_rules_arena(r2.p);
|
|
#endif
|
|
|
|
match = context->matches[r2.s->idx].head;
|
|
|
|
i = 1;
|
|
r3.i = YR_UNDEFINED;
|
|
|
|
while (match != NULL && r3.i == YR_UNDEFINED)
|
|
{
|
|
if (r1.i == i)
|
|
r3.i = match->base + match->offset;
|
|
|
|
i++;
|
|
match = match->next;
|
|
}
|
|
|
|
push(r3);
|
|
break;
|
|
|
|
case OP_LENGTH:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_LENGTH: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
|
|
ensure_defined(r1);
|
|
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_rules_arena(r2.p);
|
|
#endif
|
|
|
|
match = context->matches[r2.s->idx].head;
|
|
|
|
i = 1;
|
|
r3.i = YR_UNDEFINED;
|
|
|
|
while (match != NULL && r3.i == YR_UNDEFINED)
|
|
{
|
|
if (r1.i == i)
|
|
r3.i = match->match_length;
|
|
|
|
i++;
|
|
match = match->next;
|
|
}
|
|
|
|
push(r3);
|
|
break;
|
|
|
|
case OP_OF:
|
|
case OP_OF_PERCENT:
|
|
r2.i = yr_unaligned_u64(ip);
|
|
ip += sizeof(uint64_t);
|
|
assert(r2.i == OF_STRING_SET || r2.i == OF_RULE_SET);
|
|
found = 0;
|
|
count = 0;
|
|
pop(r1);
|
|
|
|
while (!is_undef(r1))
|
|
{
|
|
if (r2.i == OF_STRING_SET)
|
|
{
|
|
if (context->matches[r1.s->idx].tail != NULL)
|
|
{
|
|
found++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// r1.i is 1 if the rule has already matched and zero otherwise.
|
|
found += r1.i;
|
|
}
|
|
count++;
|
|
pop(r1);
|
|
}
|
|
|
|
pop(r2);
|
|
|
|
if (opcode == OP_OF)
|
|
{
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_OF: // %s()\n", __FUNCTION__);
|
|
|
|
// Quantifier is "all"
|
|
if (is_undef(r2))
|
|
{
|
|
r1.i = found >= count ? 1 : 0;
|
|
}
|
|
// Quantifier is 0 or none. This is a special case in which we want
|
|
// exactly 0 strings matching. More information at:
|
|
// https://github.com/VirusTotal/yara/issues/1695
|
|
else if (r2.i == 0)
|
|
{
|
|
r1.i = found == 0 ? 1 : 0;
|
|
}
|
|
// In all other cases the number of strings matching should be at
|
|
// least the amount specified by the quantifier.
|
|
else
|
|
{
|
|
r1.i = found >= r2.i ? 1 : 0;
|
|
}
|
|
}
|
|
else // OP_OF_PERCENT
|
|
{
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_OF_PERCENT: // %s()\n", __FUNCTION__);
|
|
|
|
// If, by some weird reason, we manage to get an undefined string
|
|
// reference as the first thing on the stack then count would be zero.
|
|
// I don't know how this could ever happen but better to check for it.
|
|
if (is_undef(r2) || count == 0)
|
|
r1.i = YR_UNDEFINED;
|
|
else
|
|
r1.i = (((double) found / count) * 100) >= r2.i ? 1 : 0;
|
|
}
|
|
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_OF_FOUND_IN:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_OF_FOUND_IN: // %s()\n", __FUNCTION__);
|
|
|
|
found = 0;
|
|
count = 0;
|
|
|
|
pop(r2); // Offset range end
|
|
pop(r1); // Offset range start
|
|
pop(r3); // First string
|
|
|
|
// If any of the range boundaries are undefined the result is also
|
|
// undefined, be we need to unwind the stack first.
|
|
if (is_undef(r1) || is_undef(r2))
|
|
{
|
|
// Remove all the strings.
|
|
while (!is_undef(r3)) pop(r3);
|
|
// Remove the quantifier at the bottom of the stack.
|
|
pop(r3);
|
|
r1.i = YR_UNDEFINED;
|
|
push(r1);
|
|
break;
|
|
}
|
|
|
|
while (!is_undef(r3))
|
|
{
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_rules_arena(r3.p);
|
|
#endif
|
|
match = context->matches[r3.s->idx].head;
|
|
|
|
while (match != NULL)
|
|
{
|
|
// String match within range start and range end?
|
|
if (match->base + match->offset >= r1.i &&
|
|
match->base + match->offset <= r2.i)
|
|
{
|
|
found++;
|
|
break;
|
|
}
|
|
|
|
// If current match is past range end, we can stop as matches
|
|
// are sorted by offset in increasing order, so all remaining
|
|
// matches are part the range end too.
|
|
if (match->base + match->offset > r1.i)
|
|
break;
|
|
|
|
match = match->next;
|
|
}
|
|
|
|
count++;
|
|
pop(r3);
|
|
}
|
|
|
|
pop(r2); // Quantifier X in expressions like "X of string_set in range"
|
|
|
|
// Quantifier is "all".
|
|
if (is_undef(r2))
|
|
{
|
|
r1.i = found >= count ? 1 : 0;
|
|
}
|
|
// Quantifier is 0 or none. This is a special case in which we want
|
|
// exactly 0 strings matching. More information at:
|
|
// https://github.com/VirusTotal/yara/issues/1695
|
|
else if (r2.i == 0)
|
|
{
|
|
r1.i = found == 0 ? 1 : 0;
|
|
}
|
|
// In all other cases the number of strings matching should be at least
|
|
// the amount specified by the quantifier.
|
|
else
|
|
{
|
|
r1.i = found >= r2.i ? 1 : 0;
|
|
}
|
|
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_OF_FOUND_AT:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_OF_FOUND_AT: // %s()\n", __FUNCTION__);
|
|
|
|
found = 0;
|
|
count = 0;
|
|
|
|
pop(r2); // Match location
|
|
pop(r1); // First string
|
|
|
|
// Match location must be defined.
|
|
if (is_undef(r2))
|
|
{
|
|
// Remove all the strings.
|
|
while (!is_undef(r1)) pop(r1);
|
|
// Remove the quantifier at the bottom of the stack.
|
|
pop(r1);
|
|
r1.i = YR_UNDEFINED;
|
|
push(r1);
|
|
break;
|
|
}
|
|
|
|
while (!is_undef(r1))
|
|
{
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_rules_arena(r1.p);
|
|
#endif
|
|
match = context->matches[r1.s->idx].head;
|
|
|
|
while (match != NULL)
|
|
{
|
|
// String match at the desired location?
|
|
if (match->base + match->offset == r2.i)
|
|
{
|
|
found++;
|
|
break;
|
|
}
|
|
|
|
// If current match is past desired location, we can stop as matches
|
|
// are sorted by offset in increasing order, so all remaining
|
|
// matches are past it.
|
|
if (match->base + match->offset > r2.i)
|
|
break;
|
|
|
|
match = match->next;
|
|
}
|
|
|
|
count++;
|
|
pop(r1);
|
|
}
|
|
|
|
pop(r2); // Quantifier X in expressions like "X of string_set in range"
|
|
|
|
// Quantifier is "all".
|
|
if (is_undef(r2))
|
|
{
|
|
r1.i = found >= count ? 1 : 0;
|
|
}
|
|
// Quantifier is 0 or none. This is a special case in which we want
|
|
// exactly 0 strings matching. More information at:
|
|
// https://github.com/VirusTotal/yara/issues/1695
|
|
else if (r2.i == 0)
|
|
{
|
|
r1.i = found == 0 ? 1 : 0;
|
|
}
|
|
// In all other cases the number of strings matching should be at least
|
|
// the amount specified by the quantifier.
|
|
else
|
|
{
|
|
r1.i = found >= r2.i ? 1 : 0;
|
|
}
|
|
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_FILESIZE:
|
|
r1.i = context->file_size;
|
|
YR_DEBUG_FPRINTF(
|
|
2,
|
|
stderr,
|
|
"- case OP_FILESIZE: r1.i=%" PRId64 "%s // %s()\n",
|
|
r1.i,
|
|
r1.i == YR_UNDEFINED ? " AKA YR_UNDEFINED" : "",
|
|
__FUNCTION__);
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_ENTRYPOINT:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_ENTRYPOINT: // %s()\n", __FUNCTION__);
|
|
r1.i = context->entry_point;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_INT8:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT8: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
r1.i = read_int8_t_little_endian(context->iterator, (size_t) r1.i);
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_INT16:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT16: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
r1.i = read_int16_t_little_endian(context->iterator, (size_t) r1.i);
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_INT32:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT32: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
r1.i = read_int32_t_little_endian(context->iterator, (size_t) r1.i);
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_UINT8:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_UINT8: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
r1.i = read_uint8_t_little_endian(context->iterator, (size_t) r1.i);
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_UINT16:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_UINT16: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
r1.i = read_uint16_t_little_endian(context->iterator, (size_t) r1.i);
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_UINT32:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_UINT32: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
r1.i = read_uint32_t_little_endian(context->iterator, (size_t) r1.i);
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_INT8BE:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT8BE: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
r1.i = read_int8_t_big_endian(context->iterator, (size_t) r1.i);
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_INT16BE:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT16BE: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
r1.i = read_int16_t_big_endian(context->iterator, (size_t) r1.i);
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_INT32BE:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT32BE: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
r1.i = read_int32_t_big_endian(context->iterator, (size_t) r1.i);
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_UINT8BE:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_UINT8BE: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
r1.i = read_uint8_t_big_endian(context->iterator, (size_t) r1.i);
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_UINT16BE:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_UINT16BE: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
r1.i = read_uint16_t_big_endian(context->iterator, (size_t) r1.i);
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_UINT32BE:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_UINT32BE: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
r1.i = read_uint32_t_big_endian(context->iterator, (size_t) r1.i);
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_IMPORT:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_IMPORT: // %s()\n", __FUNCTION__);
|
|
r1.i = yr_unaligned_u64(ip);
|
|
ip += sizeof(uint64_t);
|
|
|
|
#if YR_PARANOID_EXEC
|
|
ensure_within_rules_arena(r1.p);
|
|
#endif
|
|
|
|
result = yr_modules_load((char*) r1.p, context);
|
|
|
|
if (result != ERROR_SUCCESS)
|
|
stop = true;
|
|
|
|
break;
|
|
|
|
case OP_MATCHES:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_MATCHES: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
|
|
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:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_INT_TO_DBL: // %s()\n", __FUNCTION__);
|
|
r1.i = yr_unaligned_u64(ip);
|
|
ip += sizeof(uint64_t);
|
|
|
|
#if YR_PARANOID_EXEC
|
|
if (r1.i > stack.sp || stack.sp - r1.i >= stack.capacity)
|
|
{
|
|
stop = true;
|
|
result = ERROR_INTERNAL_FATAL_ERROR;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
r2 = stack.items[stack.sp - r1.i];
|
|
|
|
if (is_undef(r2))
|
|
stack.items[stack.sp - r1.i].i = YR_UNDEFINED;
|
|
else
|
|
stack.items[stack.sp - r1.i].d = (double) r2.i;
|
|
break;
|
|
|
|
case OP_STR_TO_BOOL:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_STR_TO_BOOL: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
ensure_defined(r1);
|
|
r1.i = r1.ss->length > 0;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_INT_EQ:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT_EQ: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
r1.i = r1.i == r2.i;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_INT_NEQ:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT_NEQ: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
r1.i = r1.i != r2.i;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_INT_LT:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT_LT: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
r1.i = r1.i < r2.i;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_INT_GT:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT_GT: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
r1.i = r1.i > r2.i;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_INT_LE:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT_LE: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
r1.i = r1.i <= r2.i;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_INT_GE:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT_GE: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
r1.i = r1.i >= r2.i;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_INT_ADD:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT_ADD: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
r1.i = r1.i + r2.i;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_INT_SUB:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT_SUB: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
r1.i = r1.i - r2.i;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_INT_MUL:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT_MUL: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
r1.i = r1.i * r2.i;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_INT_DIV:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT_DIV: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
// If divisor is zero the result is undefined. It's also undefined
|
|
// when dividing INT64_MIN by -1.
|
|
if (r2.i == 0 || (r1.i == INT64_MIN && r2.i == -1))
|
|
r1.i = YR_UNDEFINED;
|
|
else
|
|
r1.i = r1.i / r2.i;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_INT_MINUS:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_INT_MINUS: // %s()\n", __FUNCTION__);
|
|
pop(r1);
|
|
ensure_defined(r1);
|
|
r1.i = -r1.i;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_DBL_LT:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_DBL_LT: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
if (is_undef(r1) || is_undef(r2))
|
|
r1.i = false;
|
|
else
|
|
r1.i = r1.d < r2.d;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_DBL_GT:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_DBL_GT: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
r1.i = r1.d > r2.d;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_DBL_LE:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_DBL_LE: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
r1.i = r1.d <= r2.d;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_DBL_GE:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_DBL_GE: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
r1.i = r1.d >= r2.d;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_DBL_EQ:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_DBL_EQ: // %s()\n", __FUNCTION__);
|
|
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:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_DBL_NEQ: // %s()\n", __FUNCTION__);
|
|
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:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_DBL_ADD: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
r1.d = r1.d + r2.d;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_DBL_SUB:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_DBL_SUB: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
r1.d = r1.d - r2.d;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_DBL_MUL:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_DBL_MUL: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
r1.d = r1.d * r2.d;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_DBL_DIV:
|
|
YR_DEBUG_FPRINTF(2, stderr, "- case OP_DBL_DIV: // %s()\n", __FUNCTION__);
|
|
pop(r2);
|
|
pop(r1);
|
|
ensure_defined(r2);
|
|
ensure_defined(r1);
|
|
r1.d = r1.d / r2.d;
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_DBL_MINUS:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_DBL_MINUS: // %s()\n", __FUNCTION__);
|
|
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(r2);
|
|
ensure_defined(r1);
|
|
|
|
switch (opcode)
|
|
{
|
|
case OP_STR_EQ:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_STR_EQ: // %s()\n", __FUNCTION__);
|
|
r1.i = (ss_compare(r1.ss, r2.ss) == 0);
|
|
break;
|
|
case OP_STR_NEQ:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_STR_NEQ: // %s()\n", __FUNCTION__);
|
|
r1.i = (ss_compare(r1.ss, r2.ss) != 0);
|
|
break;
|
|
case OP_STR_LT:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_STR_LT: // %s()\n", __FUNCTION__);
|
|
r1.i = (ss_compare(r1.ss, r2.ss) < 0);
|
|
break;
|
|
case OP_STR_LE:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_STR_LE: // %s()\n", __FUNCTION__);
|
|
r1.i = (ss_compare(r1.ss, r2.ss) <= 0);
|
|
break;
|
|
case OP_STR_GT:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_STR_GT: // %s()\n", __FUNCTION__);
|
|
r1.i = (ss_compare(r1.ss, r2.ss) > 0);
|
|
break;
|
|
case OP_STR_GE:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_STR_GE: // %s()\n", __FUNCTION__);
|
|
r1.i = (ss_compare(r1.ss, r2.ss) >= 0);
|
|
break;
|
|
}
|
|
|
|
push(r1);
|
|
break;
|
|
|
|
case OP_CONTAINS:
|
|
case OP_ICONTAINS:
|
|
case OP_STARTSWITH:
|
|
case OP_ISTARTSWITH:
|
|
case OP_ENDSWITH:
|
|
case OP_IENDSWITH:
|
|
case OP_IEQUALS:
|
|
pop(r2);
|
|
pop(r1);
|
|
|
|
ensure_defined(r1);
|
|
ensure_defined(r2);
|
|
|
|
switch (opcode)
|
|
{
|
|
case OP_CONTAINS:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_CONTAINS: // %s()\n", __FUNCTION__);
|
|
r1.i = ss_contains(r1.ss, r2.ss);
|
|
break;
|
|
case OP_ICONTAINS:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_ICONTAINS: // %s()\n", __FUNCTION__);
|
|
r1.i = ss_icontains(r1.ss, r2.ss);
|
|
break;
|
|
case OP_STARTSWITH:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_STARTSWITH: // %s()\n", __FUNCTION__);
|
|
r1.i = ss_startswith(r1.ss, r2.ss);
|
|
break;
|
|
case OP_ISTARTSWITH:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_ISTARTSWITH: // %s()\n", __FUNCTION__);
|
|
r1.i = ss_istartswith(r1.ss, r2.ss);
|
|
break;
|
|
case OP_ENDSWITH:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_ENDSWITH: // %s()\n", __FUNCTION__);
|
|
r1.i = ss_endswith(r1.ss, r2.ss);
|
|
break;
|
|
case OP_IENDSWITH:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_IENDSWITH: // %s()\n", __FUNCTION__);
|
|
r1.i = ss_iendswith(r1.ss, r2.ss);
|
|
break;
|
|
case OP_IEQUALS:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case OP_IEQUALS: // %s()\n", __FUNCTION__);
|
|
r1.i = ss_icompare(r1.ss, r2.ss) == 0;
|
|
break;
|
|
}
|
|
|
|
push(r1);
|
|
break;
|
|
|
|
default:
|
|
YR_DEBUG_FPRINTF(
|
|
2, stderr, "- case <unknown instruction>: // %s()\n", __FUNCTION__);
|
|
// Unknown instruction, this shouldn't happen.
|
|
assert(false);
|
|
}
|
|
|
|
// Check for timeout every 100 instruction cycles. If timeout == 0 it means
|
|
// no timeout at all.
|
|
|
|
if (context->timeout > 0ULL && ++cycle == 100)
|
|
{
|
|
elapsed_time = yr_stopwatch_elapsed_ns(&context->stopwatch);
|
|
|
|
if (elapsed_time > context->timeout)
|
|
{
|
|
#ifdef YR_PROFILING_ENABLED
|
|
context->profiling_info[current_rule_idx].exec_time +=
|
|
(elapsed_time - start_time);
|
|
#endif
|
|
result = ERROR_SCAN_TIMEOUT;
|
|
stop = true;
|
|
}
|
|
|
|
cycle = 0;
|
|
}
|
|
}
|
|
|
|
obj_ptr = yr_arena_get_ptr(obj_arena, 0, 0);
|
|
|
|
for (int i = 0; i < obj_count; i++) yr_object_destroy(obj_ptr[i]);
|
|
|
|
yr_arena_release(obj_arena);
|
|
yr_notebook_destroy(it_notebook);
|
|
yr_modules_unload_all(context);
|
|
yr_free(stack.items);
|
|
|
|
YR_DEBUG_FPRINTF(
|
|
2,
|
|
stderr,
|
|
"} = %d AKA %s // %s()\n",
|
|
result,
|
|
yr_debug_error_as_string(result),
|
|
__FUNCTION__);
|
|
|
|
return result;
|
|
}
|