[Forensics-changes] [yara] 16/407: Implement resources parser and language identification in PE module

Hilko Bengen bengen at moszumanska.debian.org
Sat Jul 1 10:27:59 UTC 2017


This is an automated email from the git hooks/post-receive script.

bengen pushed a commit to annotated tag v3.3.0
in repository yara.

commit 367b0eab221684d3a818e695c4889193b2c53031
Author: Victor Manuel Alvarez <vmalvarez at virustotal.com>
Date:   Fri Aug 29 18:44:13 2014 +0200

    Implement resources parser and language identification in PE module
---
 libyara/include/yara/pe.h |  16 +++
 libyara/modules/pe.c      | 296 ++++++++++++++++++++++++++++++++++++----------
 2 files changed, 250 insertions(+), 62 deletions(-)

diff --git a/libyara/include/yara/pe.h b/libyara/include/yara/pe.h
index f6019a7..da90319 100644
--- a/libyara/include/yara/pe.h
+++ b/libyara/include/yara/pe.h
@@ -346,4 +346,20 @@ typedef struct _IMAGE_THUNK_DATA64 {
 } IMAGE_THUNK_DATA64, *PIMAGE_THUNK_DATA64;
 
 
+typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
+    DWORD Name;
+    DWORD OffsetToData;
+} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;
+
+
+typedef struct _IMAGE_RESOURCE_DIRECTORY {
+    DWORD Characteristics;
+    DWORD TimeDateStamp;
+    WORD  MajorVersion;
+    WORD  MinorVersion;
+    WORD  NumberOfNamedEntries;
+    WORD  NumberOfIdEntries;
+    IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[1];
+} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;
+
 #pragma pack(pop)
diff --git a/libyara/modules/pe.c b/libyara/modules/pe.c
index 65c778d..fa64f66 100644
--- a/libyara/modules/pe.c
+++ b/libyara/modules/pe.c
@@ -24,26 +24,60 @@ limitations under the License.
 #include <yara/mem.h>
 #include <yara/strutils.h>
 
-
 #define MODULE_NAME pe
 
+#define RESOURCE_TYPE_CURSOR         1
+#define RESOURCE_TYPE_BITMAP         2
+#define RESOURCE_TYPE_ICON           3
+#define RESOURCE_TYPE_MENU           4
+#define RESOURCE_TYPE_DIALOG         5
+#define RESOURCE_TYPE_STRING         6
+#define RESOURCE_TYPE_FONTDIR        7
+#define RESOURCE_TYPE_FONT           8
+#define RESOURCE_TYPE_ACCELERATOR    9
+#define RESOURCE_TYPE_RCDATA         10
+#define RESOURCE_TYPE_MESSAGETABLE   11
+#define RESOURCE_TYPE_VERSION        16
+#define RESOURCE_TYPE_MANIFEST       24
+
+
+#define RESOURCE_CALLBACK_CONTINUE   0
+#define RESOURCE_CALLBACK_ABORT      1
+
+
+#define RESOURCE_ITERATOR_FINISHED   0
+#define RESOURCE_ITERATOR_ABORTED    1
+
+
+#define MAX_PE_SECTIONS              96
+
+
+#define IS_RESOURCE_SUBDIRECTORY(entry) \
+    ((entry)->OffsetToData & 0x80000000)
+
+
+#define RESOURCE_OFFSET(entry) \
+    ((entry)->OffsetToData & 0x7FFFFFFF)
+
+
+typedef int (*RESOURCE_CALLBACK_FUNC) \
+    (int type, int id, int language, void* cb_data);
+
 
 typedef struct _DATA
 {
-  uint8_t* data;
-  size_t size;
   PIMAGE_NT_HEADERS32 pe_header;
-  size_t pe_size;
 
-} DATA;
+  size_t pe_size;
+  size_t pe_offset;
 
+  uint8_t* data;
+  size_t data_size;
 
-#ifndef MIN
-#define MIN(x,y) ((x < y)?(x):(y))
-#endif
+} DATA;
 
 
-PIMAGE_NT_HEADERS32 get_pe_header(
+PIMAGE_NT_HEADERS32 pe_get_header(
     uint8_t* buffer,
     size_t buffer_length)
 {
@@ -88,7 +122,7 @@ PIMAGE_NT_HEADERS32 get_pe_header(
 }
 
 
-PIMAGE_DATA_DIRECTORY get_data_directory(
+PIMAGE_DATA_DIRECTORY pe_get_directory_entry(
     PIMAGE_NT_HEADERS32 pe_header,
     int entry)
 {
@@ -104,8 +138,7 @@ PIMAGE_DATA_DIRECTORY get_data_directory(
 }
 
 
-
-uint64_t rva_to_offset(
+uint64_t pe_rva_to_offset(
     PIMAGE_NT_HEADERS32 pe_header,
     size_t pe_size,
     uint64_t rva)
@@ -120,7 +153,7 @@ uint64_t rva_to_offset(
   section_rva = 0;
   section_offset = 0;
 
-  while(i < MIN(pe_header->FileHeader.NumberOfSections, 60))
+  while(i < min(pe_header->FileHeader.NumberOfSections, MAX_PE_SECTIONS))
   {
     if ((uint8_t*) section - \
         (uint8_t*) pe_header + sizeof(IMAGE_SECTION_HEADER) < pe_size)
@@ -145,15 +178,118 @@ uint64_t rva_to_offset(
 }
 
 
-void parse_pe_resources(
+int _pe_iterate_resources(
     PIMAGE_RESOURCE_DIRECTORY resource_dir,
-    size_t resource_size)
+    uint8_t* rsrc_data,
+    size_t rsrc_size,
+    int rsrc_tree_level,
+    int* type,
+    int* id,
+    int* language,
+    RESOURCE_CALLBACK_FUNC callback,
+    void* callback_data)
 {
+  int result;
+  int total_entries = resource_dir->NumberOfNamedEntries +
+                      resource_dir->NumberOfIdEntries;
+
+  PIMAGE_RESOURCE_DIRECTORY_ENTRY entry = &resource_dir->DirectoryEntries[0];
 
+  for (int i = 0; i < total_entries; i++)
+  {
+    switch(rsrc_tree_level)
+    {
+      case 0:
+        *type = entry->Name;
+        break;
+      case 1:
+        *id = entry->Name;
+        break;
+      case 2:
+        *language = entry->Name;
+        break;
+    }
+
+    if (IS_RESOURCE_SUBDIRECTORY(entry))
+    {
+      result = _pe_iterate_resources(
+          (PIMAGE_RESOURCE_DIRECTORY) (rsrc_data + RESOURCE_OFFSET(entry)),
+          rsrc_data,
+          rsrc_size,
+          rsrc_tree_level + 1,
+          type,
+          id,
+          language,
+          callback,
+          callback_data);
+
+      if (result == RESOURCE_ITERATOR_ABORTED)
+        return RESOURCE_ITERATOR_ABORTED;
+    }
+    else
+    {
+      result = callback(*type, *id, *language, callback_data);
+
+      if (result == RESOURCE_CALLBACK_ABORT)
+        return RESOURCE_ITERATOR_ABORTED;
+    }
+
+    if (result == RESOURCE_ITERATOR_ABORTED)
+      return result;
+
+    entry++;
+  }
+
+  return RESOURCE_ITERATOR_FINISHED;
 }
 
 
-void parse_pe_header(
+int pe_iterate_resources(
+    PIMAGE_NT_HEADERS32 pe_header,
+    size_t pe_size,
+    size_t pe_offset,
+    RESOURCE_CALLBACK_FUNC callback,
+    void* callback_data)
+{
+  uint64_t offset;
+
+  int type = -1;
+  int id = -1;
+  int language = -1;
+
+  PIMAGE_DATA_DIRECTORY directory = pe_get_directory_entry(
+      pe_header, IMAGE_DIRECTORY_ENTRY_RESOURCE);
+
+  if (directory->VirtualAddress != 0)
+  {
+    offset = pe_rva_to_offset(
+        pe_header, pe_size, directory->VirtualAddress);
+
+    if (offset != 0 &&
+        offset < pe_size &&
+        directory->Size < pe_size - offset)
+    {
+      _pe_iterate_resources(
+          (PIMAGE_RESOURCE_DIRECTORY) \
+              ((uint8_t*) pe_header - pe_offset + offset),
+          (uint8_t*) pe_header - pe_offset + offset,
+          directory->Size,
+          0,
+          &type,
+          &id,
+          &language,
+          callback,
+          callback_data);
+
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+
+void pe_parse_header(
     PIMAGE_NT_HEADERS32 pe,
     size_t base_address,
     size_t pe_size,
@@ -161,9 +297,6 @@ void parse_pe_header(
     YR_OBJECT* pe_obj)
 {
   PIMAGE_SECTION_HEADER section;
-  PIMAGE_DATA_DIRECTORY directory;
-
-  uint64_t offset;
 
   char section_name[IMAGE_SIZEOF_SHORT_NAME + 1];
   int i;
@@ -191,7 +324,7 @@ void parse_pe_header(
   set_integer(
       flags & SCAN_FLAGS_PROCESS_MEMORY ?
         base_address + OptionalHeader(AddressOfEntryPoint) :
-        rva_to_offset(
+        pe_rva_to_offset(
             pe, pe_size, OptionalHeader(AddressOfEntryPoint)),
       pe_obj, "entry_point");
 
@@ -237,7 +370,7 @@ void parse_pe_header(
 
   section = IMAGE_FIRST_SECTION(pe);
 
-  for (i = 0; i < min(pe->FileHeader.NumberOfSections, 60); i++)
+  for (i = 0; i < min(pe->FileHeader.NumberOfSections, MAX_PE_SECTIONS); i++)
   {
     if ((uint8_t*) section -
         (uint8_t*) pe + sizeof(IMAGE_SECTION_HEADER) >= pe_size)
@@ -270,22 +403,6 @@ void parse_pe_header(
 
     section++;
   }
-
-  directory = get_data_directory(pe, IMAGE_DIRECTORY_ENTRY_RESOURCE);
-
-  if (directory->VirtualAddress != 0)
-  {
-    offset = rva_to_offset(pe, pe_size, directory->VirtualAddress);
-
-    if (offset != 0 &&
-        offset < pe_size &&
-        directory->Size < pe_size - offset)
-    {
-      parse_pe_resources(
-          (PIMAGE_RESOURCE_DIRECTORY)((uint8_t*) pe + offset),
-          directory->Size);
-    }
-  }
 }
 
 
@@ -330,7 +447,7 @@ define_function(exports)
   if (data == NULL)
     return_integer(UNDEFINED);
 
-  directory = get_data_directory(
+  directory = pe_get_directory_entry(
       data->pe_header,
       IMAGE_DIRECTORY_ENTRY_EXPORT);
 
@@ -339,41 +456,41 @@ define_function(exports)
   if (directory->VirtualAddress == 0)
     return_integer(0);
 
-  offset = rva_to_offset(
+  offset = pe_rva_to_offset(
       data->pe_header,
       data->pe_size,
       directory->VirtualAddress);
 
   if (offset == 0 ||
-      offset >= data->size)
+      offset >= data->data_size)
     return_integer(0);
 
   exports = (PIMAGE_EXPORT_DIRECTORY)(data->data + offset);
 
-  offset = rva_to_offset(
+  offset = pe_rva_to_offset(
       data->pe_header,
       data->pe_size,
       exports->AddressOfNames);
 
   if (offset == 0 ||
-      offset + exports->NumberOfNames * sizeof(DWORD) > data->size)
+      offset + exports->NumberOfNames * sizeof(DWORD) > data->data_size)
     return_integer(0);
 
   names = (DWORD*)(data->data + offset);
 
   for (i = 0; i < exports->NumberOfNames; i++)
   {
-    offset = rva_to_offset(
+    offset = pe_rva_to_offset(
         data->pe_header,
         data->pe_size,
         names[i]);
 
-    if (offset == 0 || offset >= data->size)
+    if (offset == 0 || offset >= data->data_size)
       return_integer(0);
 
     name = (char*)(data->data + offset);
 
-    if (strncmp(name, function_name, data->size - offset) == 0)
+    if (strncmp(name, function_name, data->data_size - offset) == 0)
       return_integer(1);
   }
 
@@ -407,22 +524,22 @@ define_function(imports)
   if (data == NULL)
     return_integer(UNDEFINED);
 
-  data_end = data->data + data->size;
+  data_end = data->data + data->data_size;
 
-  directory = get_data_directory(
+  directory = pe_get_directory_entry(
       data->pe_header,
       IMAGE_DIRECTORY_ENTRY_IMPORT);
 
   if (directory->VirtualAddress == 0)
     return_integer(0);
 
-  offset = rva_to_offset(
+  offset = pe_rva_to_offset(
       data->pe_header,
       data->pe_size,
       directory->VirtualAddress);
 
   if (offset == 0 ||
-      offset + sizeof(IMAGE_IMPORT_DESCRIPTOR) > data->size)
+      offset + sizeof(IMAGE_IMPORT_DESCRIPTOR) > data->data_size)
     return_integer(0);
 
   imports = (PIMAGE_IMPORT_DESCRIPTOR)(data->data + offset);
@@ -430,19 +547,19 @@ define_function(imports)
   while (check_bounds(imports, IMAGE_IMPORT_DESCRIPTOR, data_end) &&
          imports->Name != 0)
   {
-    offset = rva_to_offset(
+    offset = pe_rva_to_offset(
         data->pe_header,
         data->pe_size,
         imports->Name);
 
     if (offset > 0 &&
-        offset <= data->size &&
+        offset <= data->data_size &&
         strncasecmp(
             dll_name,
             (char*)(data->data + offset),
-            data->size - offset) == 0)
+            data->data_size - offset) == 0)
     {
-      offset = rva_to_offset(
+      offset = pe_rva_to_offset(
           data->pe_header,
           data->pe_size,
           imports->OriginalFirstThunk);
@@ -459,13 +576,13 @@ define_function(imports)
             if (!(thunks64->u1.Ordinal & IMAGE_ORDINAL_FLAG64))
             {
               // if not exported by ordinal
-              offset = rva_to_offset(
+              offset = pe_rva_to_offset(
                   data->pe_header,
                   data->pe_size,
                   thunks64->u1.Function);
 
               if (offset != 0 &&
-                  offset <= data->size - sizeof(IMAGE_IMPORT_BY_NAME))
+                  offset <= data->data_size - sizeof(IMAGE_IMPORT_BY_NAME))
               {
                 import = (PIMAGE_IMPORT_BY_NAME)(data->data + offset);
 
@@ -494,13 +611,13 @@ define_function(imports)
             if (!(thunks32->u1.Ordinal & IMAGE_ORDINAL_FLAG32))
             {
               // if not exported by ordinal
-              offset = rva_to_offset(
+              offset = pe_rva_to_offset(
                   data->pe_header,
                   data->pe_size,
                   thunks32->u1.Function);
 
               if (offset != 0 &&
-                  offset <= data->size - sizeof(IMAGE_IMPORT_BY_NAME))
+                  offset <= data->data_size - sizeof(IMAGE_IMPORT_BY_NAME))
               {
                 import = (PIMAGE_IMPORT_BY_NAME)(data->data + offset);
 
@@ -529,6 +646,60 @@ define_function(imports)
 }
 
 
+typedef struct _FIND_LANGUAGE_CB_DATA
+{
+  uint64_t language;
+  int found;
+
+} FIND_LANGUAGE_CB_DATA;
+
+
+int pe_find_language_cb(
+    int rsrc_type,
+    int rsrc_id,
+    int rsrc_language,
+    FIND_LANGUAGE_CB_DATA* cb_data)
+{
+  if (rsrc_language == cb_data->language)
+  {
+    cb_data->found = TRUE;
+    return RESOURCE_CALLBACK_ABORT;
+  }
+
+  return RESOURCE_CALLBACK_CONTINUE;
+}
+
+
+define_function(language)
+{
+  FIND_LANGUAGE_CB_DATA cb_data;
+
+  cb_data.language = integer_argument(1);
+  cb_data.found = FALSE;
+
+  YR_OBJECT* module = module();
+  DATA* data = (DATA*) module->data;
+
+  // if not a PE file, return UNDEFINED
+
+  if (data == NULL)
+    return_integer(UNDEFINED);
+
+  if (pe_iterate_resources(
+      data->pe_header,
+      data->pe_size,
+      data->pe_offset,
+      (RESOURCE_CALLBACK_FUNC) pe_find_language_cb,
+      (void*) &cb_data))
+  {
+    return_integer(cb_data.found);
+  }
+  else
+  {
+    return_integer(UNDEFINED);
+  }
+}
+
 begin_declarations;
 
   declare_integer("MACHINE_I386");
@@ -600,6 +771,7 @@ begin_declarations;
   declare_function("section_index", "s", "i", section_index);
   declare_function("exports", "s", "i", exports);
   declare_function("imports", "ss", "i", imports);
+  declare_function("language", "i", "i", language);
 
 end_declarations;
 
@@ -624,9 +796,8 @@ int module_load(
     void* module_data,
     size_t module_data_size)
 {
-  YR_MEMORY_BLOCK* block;
-
   PIMAGE_NT_HEADERS32 pe_header;
+  YR_MEMORY_BLOCK* block;
   DATA* data;
 
   size_t pe_size;
@@ -708,7 +879,7 @@ int module_load(
 
   foreach_memory_block(context, block)
   {
-    pe_header = get_pe_header(block->data, block->size);
+    pe_header = pe_get_header(block->data, block->size);
 
     if (pe_header != NULL)
     {
@@ -719,7 +890,7 @@ int module_load(
       {
         pe_size = block->size - ((uint8_t*) pe_header - block->data);
 
-        parse_pe_header(
+        pe_parse_header(
             pe_header,
             block->base,
             pe_size,
@@ -732,9 +903,10 @@ int module_load(
           return ERROR_INSUFICIENT_MEMORY;
 
         data->data = block->data;
-        data->size = block->size;
+        data->data_size = block->size;
         data->pe_header = pe_header;
         data->pe_size = pe_size;
+        data->pe_offset = (uint8_t*) pe_header - block->data;
 
         module_object->data = data;
         break;

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/forensics/yara.git



More information about the forensics-changes mailing list