From 605b2edf07ed8eb9a2c61ba22eb2e7c362f47ba7 Mon Sep 17 00:00:00 2001 From: secDre4mer <61268450+secDre4mer@users.noreply.github.com> Date: Thu, 28 Oct 2021 16:38:04 +0200 Subject: [PATCH] Add maximum size for process memory chunk (#1393) Co-authored-by: Victor M. Alvarez --- .github/workflows/build.yml | 2 +- .gitignore | 2 + cli/yara.c | 23 +++++-- libyara/include/yara/libyara.h | 8 ++- libyara/libyara.c | 19 +++++- libyara/proc/freebsd.c | 40 ++++++++---- libyara/proc/linux.c | 110 ++++++++++++++++++++++----------- libyara/proc/mach.c | 29 ++++++--- libyara/proc/openbsd.c | 38 +++++++++--- libyara/proc/windows.c | 29 ++++++--- 10 files changed, 210 insertions(+), 90 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c4d087f6..4da63448 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -80,4 +80,4 @@ jobs: ${{ runner.os }}-${{ env.cache-name }}-development - uses: actions/checkout@v2 - name: Build and run tests - run: bazel test @jansson//... //tests/... + run: bazel test --test_output=errors @jansson//... //tests/... diff --git a/.gitignore b/.gitignore index 289a5ebd..0370c415 100644 --- a/.gitignore +++ b/.gitignore @@ -85,3 +85,5 @@ windows/*/packages/ .dirstamp .idea/ + +tests/mapper diff --git a/cli/yara.c b/cli/yara.c index ea2c041c..5872958e 100644 --- a/cli/yara.c +++ b/cli/yara.c @@ -157,12 +157,13 @@ static bool negate = false; static bool print_count_only = false; static bool fail_on_warnings = false; static bool rules_are_compiled = false; -static long total_count = 0; -static long limit = 0; -static long timeout = 1000000; -static long stack_size = DEFAULT_STACK_SIZE; -static long threads = YR_MAX_THREADS; -static long max_strings_per_rule = DEFAULT_MAX_STRINGS_PER_RULE; +static int total_count = 0; +static int limit = 0; +static int timeout = 1000000; +static int stack_size = DEFAULT_STACK_SIZE; +static int threads = YR_MAX_THREADS; +static int max_strings_per_rule = DEFAULT_MAX_STRINGS_PER_RULE; +static int max_process_memory_chunk = DEFAULT_MAX_PROCESS_MEMORY_CHUNK; #define USAGE_STRING \ "Usage: yara [OPTION]... [NAMESPACE:]RULES_FILE... FILE | DIR | PID" @@ -307,6 +308,14 @@ args_option_t options[] = { _T("set maximum stack size (default=16384)"), _T("SLOTS")), + OPT_INTEGER( + 0, + _T("max-process-memory-chunk"), + &max_process_memory_chunk, + _T("set maximum chunk size while reading process memory") + _T(" (default=1073741824)"), + _T("NUMBER")), + OPT_STRING_MULTI( 't', _T("tag"), @@ -1359,6 +1368,8 @@ int _tmain(int argc, const char_t** argv) yr_set_configuration(YR_CONFIG_STACK_SIZE, &stack_size); yr_set_configuration(YR_CONFIG_MAX_STRINGS_PER_RULE, &max_strings_per_rule); + yr_set_configuration( + YR_CONFIG_MAX_PROCESS_MEMORY_CHUNK, &max_process_memory_chunk); // Try to load the rules file as a binary file containing // compiled rules first diff --git a/libyara/include/yara/libyara.h b/libyara/include/yara/libyara.h index 5e2f5b10..8039f060 100644 --- a/libyara/include/yara/libyara.h +++ b/libyara/include/yara/libyara.h @@ -72,14 +72,16 @@ typedef enum _YR_CONFIG_NAME YR_CONFIG_STACK_SIZE, YR_CONFIG_MAX_STRINGS_PER_RULE, YR_CONFIG_MAX_MATCH_DATA, + YR_CONFIG_MAX_PROCESS_MEMORY_CHUNK, YR_CONFIG_LAST // End-of-enum marker, not a configuration } YR_CONFIG_NAME; -#define DEFAULT_STACK_SIZE 16384 -#define DEFAULT_MAX_STRINGS_PER_RULE 10000 -#define DEFAULT_MAX_MATCH_DATA 512 +#define DEFAULT_STACK_SIZE 16384 +#define DEFAULT_MAX_STRINGS_PER_RULE 10000 +#define DEFAULT_MAX_MATCH_DATA 512 +#define DEFAULT_MAX_PROCESS_MEMORY_CHUNK 1073741824 YR_API int yr_initialize(void); diff --git a/libyara/libyara.c b/libyara/libyara.c index e48ed8aa..22e9f78f 100644 --- a/libyara/libyara.c +++ b/libyara/libyara.c @@ -235,6 +235,7 @@ YR_API int yr_initialize(void) uint32_t def_stack_size = DEFAULT_STACK_SIZE; uint32_t def_max_strings_per_rule = DEFAULT_MAX_STRINGS_PER_RULE; uint32_t def_max_match_data = DEFAULT_MAX_MATCH_DATA; + uint64_t def_max_process_memory_chunk = DEFAULT_MAX_PROCESS_MEMORY_CHUNK; init_count++; @@ -295,6 +296,9 @@ YR_API int yr_initialize(void) FAIL_ON_ERROR(yr_set_configuration( YR_CONFIG_MAX_STRINGS_PER_RULE, &def_max_strings_per_rule)); + FAIL_ON_ERROR(yr_set_configuration( + YR_CONFIG_MAX_PROCESS_MEMORY_CHUNK, &def_max_process_memory_chunk)); + FAIL_ON_ERROR( yr_set_configuration(YR_CONFIG_MAX_MATCH_DATA, &def_max_match_data)); @@ -363,9 +367,10 @@ YR_API int yr_finalize(void) // Args: // name: Any of the values defined by the YR_CONFIG_NAME enum. Possible values // are: -// YR_CONFIG_STACK_SIZE data type: uint32_t -// YR_CONFIG_MAX_STRINGS_PER_RULE data type: uint32_t -// YR_CONFIG_MAX_MATCH_DATA data type: uint32_t +// YR_CONFIG_STACK_SIZE data type: uint32_t +// YR_CONFIG_MAX_STRINGS_PER_RULE data type: uint32_t +// YR_CONFIG_MAX_MATCH_DATA data type: uint32_t +// YR_CONFIG_MAX_PROCESS_MEMORY_CHUNK data type: uint64_t // // src: Pointer to the value being set for the option. // @@ -386,6 +391,10 @@ YR_API int yr_set_configuration(YR_CONFIG_NAME name, void *src) yr_cfgs[name].ui32 = *(uint32_t *) src; break; + case YR_CONFIG_MAX_PROCESS_MEMORY_CHUNK: + yr_cfgs[name].ui64 = *(uint64_t *) src; + break; + default: return ERROR_INTERNAL_FATAL_ERROR; } @@ -406,6 +415,10 @@ YR_API int yr_get_configuration(YR_CONFIG_NAME name, void *dest) *(uint32_t *) dest = yr_cfgs[name].ui32; break; + case YR_CONFIG_MAX_PROCESS_MEMORY_CHUNK: + *(uint64_t *) dest = yr_cfgs[name].ui64; + break; + default: return ERROR_INTERNAL_FATAL_ERROR; } diff --git a/libyara/proc/freebsd.c b/libyara/proc/freebsd.c index fd240a56..79625dbf 100644 --- a/libyara/proc/freebsd.c +++ b/libyara/proc/freebsd.c @@ -36,17 +36,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include #include - typedef struct _YR_PROC_INFO { int pid; struct ptrace_vm_entry vm_entry; } YR_PROC_INFO; - int _yr_process_attach(int pid, YR_PROC_ITERATOR_CTX* context) { int status; @@ -56,6 +55,8 @@ int _yr_process_attach(int pid, YR_PROC_ITERATOR_CTX* context) if (proc_info == NULL) return ERROR_INSUFFICIENT_MEMORY; + memset(proc_info, 0, sizeof(YR_PROC_INFO)); + context->proc_info = proc_info; proc_info->pid = pid; @@ -79,7 +80,6 @@ int _yr_process_attach(int pid, YR_PROC_ITERATOR_CTX* context) return ERROR_SUCCESS; } - int _yr_process_detach(YR_PROC_ITERATOR_CTX* context) { YR_PROC_INFO* proc_info = (YR_PROC_INFO*) context->proc_info; @@ -90,7 +90,6 @@ int _yr_process_detach(YR_PROC_ITERATOR_CTX* context) return ERROR_SUCCESS; } - YR_API const uint8_t* yr_process_fetch_memory_block_data(YR_MEMORY_BLOCK* block) { YR_PROC_ITERATOR_CTX* context = (YR_PROC_ITERATOR_CTX*) block->context; @@ -128,7 +127,6 @@ YR_API const uint8_t* yr_process_fetch_memory_block_data(YR_MEMORY_BLOCK* block) return context->buffer; } - YR_API YR_MEMORY_BLOCK* yr_process_get_next_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator) { @@ -142,20 +140,38 @@ YR_API YR_MEMORY_BLOCK* yr_process_get_next_memory_block( iterator->last_error = ERROR_SUCCESS; - if (ptrace(PT_VM_ENTRY, proc_info->pid, (char*) (&proc_info->vm_entry), 0) == - -1) + uint64_t current_begin = context->current_block.base + + context->current_block.size; + + uint64_t max_process_memory_chunk; + + yr_get_configuration( + YR_CONFIG_MAX_PROCESS_MEMORY_CHUNK, (void*) &max_process_memory_chunk); + + if (proc_info->vm_entry.pve_end <= current_begin) { - return NULL; + if (ptrace( + PT_VM_ENTRY, proc_info->pid, (char*) (&proc_info->vm_entry), 0) == + -1) + { + return NULL; + } + else + { + current_begin = proc_info->vm_entry.pve_start; + } } - context->current_block.base = proc_info->vm_entry.pve_start; - context->current_block.size = proc_info->vm_entry.pve_end - - proc_info->vm_entry.pve_start + 1; + context->current_block.base = current_begin; + context->current_block.size = yr_min( + proc_info->vm_entry.pve_end - current_begin + 1, + max_process_memory_chunk); + + assert(context->current_block.size > 0); return &context->current_block; } - YR_API YR_MEMORY_BLOCK* yr_process_get_first_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator) { diff --git a/libyara/proc/linux.c b/libyara/proc/linux.c index b3cf6184..2439f067 100644 --- a/libyara/proc/linux.c +++ b/libyara/proc/linux.c @@ -40,6 +40,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include #include @@ -49,12 +50,13 @@ typedef struct _YR_PROC_INFO int mem_fd; int pagemap_fd; FILE* maps; + uint64_t map_offset; + uint64_t next_block_end; int page_size; char map_path[PATH_MAX]; uint64_t map_dmaj; uint64_t map_dmin; uint64_t map_ino; - uint64_t map_offset; } YR_PROC_INFO; static int page_size = -1; @@ -78,6 +80,7 @@ int _yr_process_attach(int pid, YR_PROC_ITERATOR_CTX* context) proc_info->maps = NULL; proc_info->mem_fd = -1; proc_info->pagemap_fd = -1; + proc_info->next_block_end = 0; snprintf(buffer, sizeof(buffer), "/proc/%u/maps", pid); proc_info->maps = fopen(buffer, "r"); @@ -303,54 +306,89 @@ _exit:; YR_API YR_MEMORY_BLOCK* yr_process_get_next_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator) { - YR_MEMORY_BLOCK* result = NULL; YR_PROC_ITERATOR_CTX* context = (YR_PROC_ITERATOR_CTX*) iterator->context; YR_PROC_INFO* proc_info = (YR_PROC_INFO*) context->proc_info; char buffer[PATH_MAX]; char perm[5]; uint64_t begin, end; - int n, len; - while (fgets(buffer, sizeof(buffer), proc_info->maps) != NULL) + uint64_t current_begin = context->current_block.base + + context->current_block.size; + + uint64_t max_process_memory_chunk; + + yr_get_configuration( + YR_CONFIG_MAX_PROCESS_MEMORY_CHUNK, (void*) &max_process_memory_chunk); + + if (proc_info->next_block_end <= current_begin) { - // If we haven't read the whole line, skip over the rest. - if (strrchr(buffer, '\n') == NULL) + int n, path_start; + + while (fgets(buffer, sizeof(buffer), proc_info->maps) != NULL) { - int c; - do + // If we haven't read the whole line, skip over the rest. + if (strrchr(buffer, '\n') == NULL) { - c = fgetc(proc_info->maps); - } while (c >= 0 && c != '\n'); + int c; + do + { + c = fgetc(proc_info->maps); + } while (c >= 0 && c != '\n'); + } + + // Each row in /proc/$PID/maps describes a region of contiguous virtual + // memory in a process or thread. Each row has the following fields: + // + // address perms offset dev inode pathname + // 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm + // + n = sscanf( + buffer, + "%" SCNx64 "-%" SCNx64 " %4s " + "%" SCNx64 " %" SCNx64 ":%" SCNx64 " %" SCNu64 " %n", + &begin, + &end, + perm, + &(proc_info->map_offset), + &(proc_info->map_dmaj), + &(proc_info->map_dmin), + &(proc_info->map_ino), + &path_start); + + // If the row was parsed correctly sscan must return 7. + if (n == 7) + { + // path_start contains the offset within buffer where the path starts, + // the path should start with /. + if (buffer[path_start] == '/') + strncpy( + proc_info->map_path, + buffer + path_start, + sizeof(proc_info->map_path) - 1); + else + proc_info->map_path[0] = '\0'; + break; + } } - n = sscanf( - buffer, - "%" SCNx64 "-%" SCNx64 " %4s " - "%" SCNx64 " %" SCNx64 ":%" SCNx64 " %" SCNu64 " %n", - &begin, - &end, - perm, - &(proc_info->map_offset), - &(proc_info->map_dmaj), - &(proc_info->map_dmin), - &(proc_info->map_ino), - &len); + if (n == 7) { - if (buffer[len] == '/') - strncpy( - proc_info->map_path, buffer + len, sizeof(proc_info->map_path) - 1); - else - proc_info->map_path[0] = '\0'; - break; + current_begin = begin; + proc_info->next_block_end = end; + } + else + { + YR_DEBUG_FPRINTF(2, stderr, "+ %s() = NULL\n", __FUNCTION__); + return NULL; } } - if (n == 7) - { - context->current_block.base = begin; - context->current_block.size = end - begin; - result = &context->current_block; - } + + context->current_block.base = current_begin; + context->current_block.size = yr_min( + proc_info->next_block_end - current_begin, max_process_memory_chunk); + + assert(context->current_block.size > 0); iterator->last_error = ERROR_SUCCESS; @@ -363,7 +401,7 @@ YR_API YR_MEMORY_BLOCK* yr_process_get_next_memory_block( context->current_block.base, context->current_block.size); - return result; + return &context->current_block; } YR_API YR_MEMORY_BLOCK* yr_process_get_first_memory_block( @@ -381,6 +419,8 @@ YR_API YR_MEMORY_BLOCK* yr_process_get_first_memory_block( goto _exit; } + proc_info->next_block_end = 0; + result = yr_process_get_next_memory_block(iterator); _exit: diff --git a/libyara/proc/mach.c b/libyara/proc/mach.c index 5e3b6c70..78a7c58c 100644 --- a/libyara/proc/mach.c +++ b/libyara/proc/mach.c @@ -34,16 +34,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include #include - typedef struct _YR_PROC_INFO { task_t task; } YR_PROC_INFO; - int _yr_process_attach(int pid, YR_PROC_ITERATOR_CTX* context) { YR_PROC_INFO* proc_info = (YR_PROC_INFO*) yr_malloc(sizeof(YR_PROC_INFO)); @@ -64,7 +63,6 @@ int _yr_process_attach(int pid, YR_PROC_ITERATOR_CTX* context) return ERROR_SUCCESS; } - int _yr_process_detach(YR_PROC_ITERATOR_CTX* context) { YR_PROC_INFO* proc_info = context->proc_info; @@ -75,7 +73,6 @@ int _yr_process_detach(YR_PROC_ITERATOR_CTX* context) return ERROR_SUCCESS; } - YR_API const uint8_t* yr_process_fetch_memory_block_data(YR_MEMORY_BLOCK* block) { YR_PROC_ITERATOR_CTX* context = (YR_PROC_ITERATOR_CTX*) block->context; @@ -113,7 +110,6 @@ YR_API const uint8_t* yr_process_fetch_memory_block_data(YR_MEMORY_BLOCK* block) return context->buffer; } - YR_API YR_MEMORY_BLOCK* yr_process_get_next_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator) { @@ -126,8 +122,13 @@ YR_API YR_MEMORY_BLOCK* yr_process_get_next_memory_block( vm_region_basic_info_data_64_t info; vm_size_t size = 0; - vm_address_t address = (vm_address_t) context->current_block.base + - context->current_block.size; + uint64_t current_begin = (vm_address_t) context->current_block.base + + context->current_block.size; + vm_address_t address = current_begin; + uint64_t max_process_memory_chunk; + + yr_get_configuration( + YR_CONFIG_MAX_PROCESS_MEMORY_CHUNK, (void*) &max_process_memory_chunk); iterator->last_error = ERROR_SUCCESS; @@ -146,18 +147,26 @@ YR_API YR_MEMORY_BLOCK* yr_process_get_next_memory_block( if (kr == KERN_SUCCESS) { - context->current_block.base = address; - context->current_block.size = size; + size_t chunk_size = size - (size_t) (current_begin - address); + + if (((uint64_t) chunk_size) > max_process_memory_chunk) + { + chunk_size = (size_t) max_process_memory_chunk; + } + + context->current_block.base = (size_t) current_begin; + context->current_block.size = chunk_size; return &context->current_block; } + current_begin = address; + } while (kr != KERN_INVALID_ADDRESS); return NULL; } - YR_API YR_MEMORY_BLOCK* yr_process_get_first_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator) { diff --git a/libyara/proc/openbsd.c b/libyara/proc/openbsd.c index 874f3f6a..c11b7265 100644 --- a/libyara/proc/openbsd.c +++ b/libyara/proc/openbsd.c @@ -42,6 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // clang-format on #include +#include #include #include @@ -63,6 +64,8 @@ int _yr_process_attach(int pid, YR_PROC_ITERATOR_CTX* context) if (proc_info == NULL) return ERROR_INSUFFICIENT_MEMORY; + memset(proc_info, 0, sizeof(YR_PROC_INFO)); + proc_info->pid = pid; if (ptrace(PT_ATTACH, pid, NULL, 0) == -1) { @@ -149,19 +152,34 @@ YR_API YR_MEMORY_BLOCK* yr_process_get_next_memory_block( iterator->last_error = ERROR_SUCCESS; - if (sysctl(mib, 3, &proc_info->vm_entry, &len, NULL, 0) < 0) - return NULL; + uint64_t current_begin = context->current_block.base + + context->current_block.size; - // no more blocks - if (proc_info->old_end == proc_info->vm_entry.kve_end) - return NULL; + uint64_t max_process_memory_chunk; - proc_info->old_end = proc_info->vm_entry.kve_end; - context->current_block.base = proc_info->vm_entry.kve_start; - context->current_block.size = proc_info->vm_entry.kve_end - - proc_info->vm_entry.kve_start; + yr_get_configuration( + YR_CONFIG_MAX_PROCESS_MEMORY_CHUNK, (void*) &max_process_memory_chunk); - proc_info->vm_entry.kve_start = proc_info->vm_entry.kve_start + 1; + if (proc_info->old_end <= current_begin) + { + if (sysctl(mib, 3, &proc_info->vm_entry, &len, NULL, 0) < 0) + return NULL; + + // no more blocks + if (proc_info->old_end == proc_info->vm_entry.kve_end) + return NULL; + + current_begin = proc_info->vm_entry.kve_start; + proc_info->old_end = proc_info->vm_entry.kve_end; + + proc_info->vm_entry.kve_start = proc_info->vm_entry.kve_start + 1; + } + + context->current_block.base = current_begin; + context->current_block.size = yr_min( + proc_info->old_end - current_begin, max_process_memory_chunk); + + assert(context->current_block.size > 0); return &context->current_block; } diff --git a/libyara/proc/windows.c b/libyara/proc/windows.c index 30238f51..cb247e4e 100644 --- a/libyara/proc/windows.c +++ b/libyara/proc/windows.c @@ -31,10 +31,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include #include - typedef struct _YR_PROC_INFO { HANDLE hProcess; @@ -82,7 +82,6 @@ int _yr_process_attach(int pid, YR_PROC_ITERATOR_CTX* context) return ERROR_SUCCESS; } - int _yr_process_detach(YR_PROC_ITERATOR_CTX* context) { YR_PROC_INFO* proc_info = (YR_PROC_INFO*) context->proc_info; @@ -91,7 +90,6 @@ int _yr_process_detach(YR_PROC_ITERATOR_CTX* context) return ERROR_SUCCESS; } - YR_API const uint8_t* yr_process_fetch_memory_block_data(YR_MEMORY_BLOCK* block) { SIZE_T read; @@ -130,7 +128,6 @@ YR_API const uint8_t* yr_process_fetch_memory_block_data(YR_MEMORY_BLOCK* block) return context->buffer; } - YR_API YR_MEMORY_BLOCK* yr_process_get_next_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator) { @@ -138,8 +135,12 @@ YR_API YR_MEMORY_BLOCK* yr_process_get_next_memory_block( YR_PROC_INFO* proc_info = (YR_PROC_INFO*) context->proc_info; MEMORY_BASIC_INFORMATION mbi; - PVOID address = (PVOID)( - context->current_block.base + context->current_block.size); + void* address = + (void*) (context->current_block.base + context->current_block.size); + uint64_t max_process_memory_chunk; + + yr_get_configuration( + YR_CONFIG_MAX_PROCESS_MEMORY_CHUNK, (void*) &max_process_memory_chunk); iterator->last_error = ERROR_SUCCESS; @@ -153,19 +154,27 @@ YR_API YR_MEMORY_BLOCK* yr_process_get_next_memory_block( if (mbi.State == MEM_COMMIT && ((mbi.Protect & PAGE_NOACCESS) == 0)) { - context->current_block.base = (size_t) mbi.BaseAddress; - context->current_block.size = mbi.RegionSize; + size_t chuck_size = + mbi.RegionSize - + (size_t) (((uint8_t*) address) - ((uint8_t*) mbi.BaseAddress)); + + if (((uint64_t) chuck_size) > max_process_memory_chunk) + { + chuck_size = (size_t) max_process_memory_chunk; + } + + context->current_block.base = (size_t) address; + context->current_block.size = chuck_size; return &context->current_block; } - address = (uint8_t*) address + mbi.RegionSize; + address = (uint8_t*) mbi.BaseAddress + mbi.RegionSize; } return NULL; } - YR_API YR_MEMORY_BLOCK* yr_process_get_first_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator) {