[Forensics-changes] [yara] 122/192: Refactor exports and new exports-related features to PE module.
Hilko Bengen
bengen at moszumanska.debian.org
Sat Jul 1 10:31:55 UTC 2017
This is an automated email from the git hooks/post-receive script.
bengen pushed a commit to annotated tag v3.6.0
in repository yara.
commit 30de6c776c57567095f2d31fe3e72b8131b598f6
Author: Wesley Shields <wxs at atarininja.org>
Date: Thu Mar 16 07:46:53 2017 -0400
Refactor exports and new exports-related features to PE module.
---
docs/modules/pe.rst | 15 +++
libyara/include/yara/pe_utils.h | 22 ++--
libyara/modules/pe.c | 221 +++++++++++++++++++++++++++++-----------
3 files changed, 194 insertions(+), 64 deletions(-)
diff --git a/docs/modules/pe.rst b/docs/modules/pe.rst
index 4b0e19b..4a156f9 100644
--- a/docs/modules/pe.rst
+++ b/docs/modules/pe.rst
@@ -524,6 +524,21 @@ Reference
*Example: pe.exports("CPlApplet")*
+.. c:function:: exports(ordinal)
+
+ .. versionadded:: 3.6.0
+
+ Function returning true if the PE exports *ordinal* or
+ false otherwise.
+
+ *Example: pe.exports(72)*
+
+.. c:type:: number_of_exports
+
+ .. versionadded:: 3.6.0
+
+ Number of exports in the PE.
+
.. c:type:: number_of_imports
.. versionadded:: 3.6.0
diff --git a/libyara/include/yara/pe_utils.h b/libyara/include/yara/pe_utils.h
index 86571c5..a692548 100644
--- a/libyara/include/yara/pe_utils.h
+++ b/libyara/include/yara/pe_utils.h
@@ -18,29 +18,38 @@
//
// Imports are stored in a linked list. Each node (IMPORTED_DLL) contains the
-// name of the DLL and a pointer to another linked list of IMPORTED_FUNCTION
-// structures containing the names of imported functions.
+// name of the DLL and a pointer to another linked list of
+// IMPORT_EXPORT_FUNCTION structures containing the details of imported
+// functions.
//
typedef struct _IMPORTED_DLL
{
char *name;
- struct _IMPORTED_FUNCTION *functions;
+ struct _IMPORT_EXPORT_FUNCTION *functions;
struct _IMPORTED_DLL *next;
} IMPORTED_DLL, *PIMPORTED_DLL;
-typedef struct _IMPORTED_FUNCTION
+//
+// This is used to track imported and exported functions. The "has_ordinal"
+// field is only used in the case of imports as those are optional. Every export
+// has an ordinal so we don't need the field there, but in the interest of
+// keeping duplicate code to a minimum we use this function for both imports and
+// exports.
+//
+
+typedef struct _IMPORT_EXPORT_FUNCTION
{
char *name;
uint8_t has_ordinal;
uint16_t ordinal;
- struct _IMPORTED_FUNCTION *next;
+ struct _IMPORT_EXPORT_FUNCTION *next;
-} IMPORTED_FUNCTION, *PIMPORTED_FUNCTION;
+} IMPORT_EXPORT_FUNCTION, *PIMPORT_EXPORT_FUNCTION;
typedef struct _PE
@@ -55,6 +64,7 @@ typedef struct _PE
YR_OBJECT* object;
IMPORTED_DLL* imported_dlls;
+ IMPORT_EXPORT_FUNCTION* exported_dlls;
uint32_t resources;
} PE;
diff --git a/libyara/modules/pe.c b/libyara/modules/pe.c
index 8ddc126..de3f647 100644
--- a/libyara/modules/pe.c
+++ b/libyara/modules/pe.c
@@ -661,13 +661,13 @@ int pe_collect_resources(
}
-IMPORTED_FUNCTION* pe_parse_import_descriptor(
+IMPORT_EXPORT_FUNCTION* pe_parse_import_descriptor(
PE* pe,
PIMAGE_IMPORT_DESCRIPTOR import_descriptor,
char* dll_name)
{
- IMPORTED_FUNCTION* head = NULL;
- IMPORTED_FUNCTION* tail = NULL;
+ IMPORT_EXPORT_FUNCTION* head = NULL;
+ IMPORT_EXPORT_FUNCTION* tail = NULL;
int num_functions = 0;
@@ -723,8 +723,8 @@ IMPORTED_FUNCTION* pe_parse_import_descriptor(
if (name != NULL || has_ordinal == 1)
{
- IMPORTED_FUNCTION* imported_func = (IMPORTED_FUNCTION*)
- yr_calloc(1, sizeof(IMPORTED_FUNCTION));
+ IMPORT_EXPORT_FUNCTION* imported_func = (IMPORT_EXPORT_FUNCTION*)
+ yr_calloc(1, sizeof(IMPORT_EXPORT_FUNCTION));
if (imported_func == NULL)
{
@@ -790,8 +790,8 @@ IMPORTED_FUNCTION* pe_parse_import_descriptor(
if (name != NULL || has_ordinal == 1)
{
- IMPORTED_FUNCTION* imported_func = (IMPORTED_FUNCTION*)
- yr_calloc(1, sizeof(IMPORTED_FUNCTION));
+ IMPORT_EXPORT_FUNCTION* imported_func = (IMPORT_EXPORT_FUNCTION*)
+ yr_calloc(1, sizeof(IMPORT_EXPORT_FUNCTION));
if (imported_func == NULL)
{
@@ -897,7 +897,7 @@ IMPORTED_DLL* pe_parse_imports(
if (imported_dll != NULL)
{
- IMPORTED_FUNCTION* functions = pe_parse_import_descriptor(
+ IMPORT_EXPORT_FUNCTION* functions = pe_parse_import_descriptor(
pe, imports, dll_name);
if (functions != NULL)
@@ -929,6 +929,123 @@ IMPORTED_DLL* pe_parse_imports(
return head;
}
+//
+// Walk the exports and collect relevant information. It is used in the
+// "exports" function for comparison.
+//
+
+IMPORT_EXPORT_FUNCTION* pe_parse_exports(
+ PE* pe)
+{
+ IMPORT_EXPORT_FUNCTION* head = NULL;
+ IMPORT_EXPORT_FUNCTION* tail = NULL;
+
+ PIMAGE_DATA_DIRECTORY directory;
+ PIMAGE_EXPORT_DIRECTORY exports;
+ DWORD* names;
+ WORD* ordinals;
+
+ int64_t offset;
+ uint32_t i;
+ size_t remaining;
+ uint8_t* eos;
+ int num_exports = 0;
+
+ // If not a PE file, return UNDEFINED
+
+ if (pe == NULL)
+ return NULL;
+
+ directory = pe_get_directory_entry(
+ pe, IMAGE_DIRECTORY_ENTRY_EXPORT);
+
+ if (yr_le32toh(directory->VirtualAddress) == 0)
+ return NULL;
+
+ offset = pe_rva_to_offset(pe, directory->VirtualAddress);
+
+ if (offset < 0)
+ return NULL;
+
+ exports = (PIMAGE_EXPORT_DIRECTORY) \
+ (pe->data + offset);
+
+ if (!struct_fits_in_pe(pe, exports, IMAGE_EXPORT_DIRECTORY))
+ return NULL;
+
+ offset = pe_rva_to_offset(pe, yr_le32toh(exports->AddressOfNames));
+
+ if (offset < 0)
+ return NULL;
+
+ if (yr_le32toh(exports->NumberOfFunctions) > MAX_PE_EXPORTS ||
+ yr_le32toh(exports->NumberOfFunctions) * sizeof(DWORD) > pe->data_size - offset)
+ return NULL;
+
+ names = (DWORD*)(pe->data + offset);
+
+ offset = pe_rva_to_offset(pe, yr_le32toh(exports->AddressOfNameOrdinals));
+
+ if (offset < 0)
+ return NULL;
+
+ ordinals = (WORD*)(pe->data + offset);
+
+ // Walk the number of functions, not the number of names as each exported
+ // symbol has an ordinal value, but names are optional.
+ for (i = 0; i < yr_le32toh(exports->NumberOfFunctions); i++)
+ {
+ char* name;
+ uint16_t ordinal = 0;
+ offset = pe_rva_to_offset(pe, names[i]);
+
+ if (offset < 0)
+ return head;
+
+ // The name is a NULL terminated string of variable length, so search for it
+ // but be sure not to go too far.
+ remaining = pe->data_size - (size_t) offset;
+ eos = (uint8_t*) memmem((void*) (pe->data + offset), remaining, "\0", 1);
+ if (eos == NULL)
+ // No NULL terminator found. Abort!
+ continue;
+ else
+ // NULL found. Save the string!
+ name = (char *) yr_strndup((char*) (pe->data + offset), (size_t) (eos - (pe->data + offset)));
+
+ // Get the corresponding ordinal. Note that we are not subtracting the
+ // ordinal base here as we don't intend to index into the export address
+ // table.
+ ordinal = yr_le16toh(ordinals[i]);
+
+ // Now add it to the list...
+ IMPORT_EXPORT_FUNCTION* exported_func = (IMPORT_EXPORT_FUNCTION*)
+ yr_calloc(1, sizeof(IMPORT_EXPORT_FUNCTION));
+
+ if (exported_func == NULL)
+ {
+ yr_free(name);
+ continue;
+ }
+
+ exported_func->name = name;
+ exported_func->ordinal = ordinal;
+ exported_func->next = NULL;
+
+ if (head == NULL)
+ head = exported_func;
+
+ if (tail != NULL)
+ tail->next = exported_func;
+
+ tail = exported_func;
+ num_exports++;
+ }
+
+ set_integer(num_exports, pe->object, "number_of_exports");
+ return head;
+}
+
#if defined(HAVE_LIBCRYPTO)
@@ -1400,71 +1517,48 @@ define_function(exports)
YR_OBJECT* module = module();
PE* pe = (PE*) module->data;
- PIMAGE_DATA_DIRECTORY directory;
- PIMAGE_EXPORT_DIRECTORY exports;
- DWORD* names;
-
- int64_t offset;
- uint32_t i;
- size_t remaining;
-
- // If not a PE file, return UNDEFINED
+ // If not a PE, return UNDEFINED.
if (pe == NULL)
return_integer(UNDEFINED);
- directory = pe_get_directory_entry(
- pe, IMAGE_DIRECTORY_ENTRY_EXPORT);
-
- // If the PE doesn't export any functions, return FALSE
+ IMPORT_EXPORT_FUNCTION* exported_func = pe->exported_dlls;
- if (yr_le32toh(directory->VirtualAddress) == 0)
- return_integer(0);
-
- offset = pe_rva_to_offset(pe, directory->VirtualAddress);
+ while (exported_func != NULL)
+ {
+ if (strcasecmp(exported_func->name, function_name->c_string) == 0)
+ return_integer(1);
- if (offset < 0)
- return_integer(0);
+ exported_func = exported_func->next;
+ }
- exports = (PIMAGE_EXPORT_DIRECTORY) \
- (pe->data + offset);
+ return_integer(0);
+}
- if (!struct_fits_in_pe(pe, exports, IMAGE_EXPORT_DIRECTORY))
- return_integer(0);
- offset = pe_rva_to_offset(pe, yr_le32toh(exports->AddressOfNames));
+define_function(exports_ordinal)
+{
+ uint64_t ordinal = integer_argument(1);
- if (offset < 0)
- return_integer(0);
+ YR_OBJECT* module = module();
+ PE* pe = (PE*) module->data;
- if (yr_le32toh(exports->NumberOfNames) > MAX_PE_EXPORTS ||
- yr_le32toh(exports->NumberOfNames) * sizeof(DWORD) > pe->data_size - offset)
- return_integer(0);
+ if (!pe)
+ return_integer(UNDEFINED);
- names = (DWORD*)(pe->data + offset);
+ IMPORT_EXPORT_FUNCTION* exported_func = pe->exported_dlls;
- for (i = 0; i < yr_le32toh(exports->NumberOfNames); i++)
+ while (exported_func != NULL)
{
- char* name;
- offset = pe_rva_to_offset(pe, names[i]);
-
- if (offset < 0)
- return_integer(0);
-
- remaining = pe->data_size - (size_t) offset;
- name = (char*)(pe->data + offset);
-
- if (remaining >= function_name->length &&
- strncmp(name, function_name->c_string, remaining) == 0)
- {
+ if (exported_func->ordinal == ordinal)
return_integer(1);
- }
+
+ exported_func = exported_func->next;
}
return_integer(0);
}
-
#if defined(HAVE_LIBCRYPTO)
//
@@ -1498,7 +1592,7 @@ define_function(imphash)
while (dll)
{
- IMPORTED_FUNCTION* func;
+ IMPORT_EXPORT_FUNCTION* func;
size_t dll_name_len;
char* dll_name;
@@ -1598,7 +1692,7 @@ define_function(imports)
{
if (strcasecmp(imported_dll->name, dll_name) == 0)
{
- IMPORTED_FUNCTION* imported_func = imported_dll->functions;
+ IMPORT_EXPORT_FUNCTION* imported_func = imported_dll->functions;
while (imported_func != NULL)
{
@@ -1635,7 +1729,7 @@ define_function(imports_ordinal)
{
if (strcasecmp(imported_dll->name, dll_name) == 0)
{
- IMPORTED_FUNCTION* imported_func = imported_dll->functions;
+ IMPORT_EXPORT_FUNCTION* imported_func = imported_dll->functions;
while (imported_func != NULL)
{
@@ -2106,6 +2200,7 @@ begin_declarations;
declare_function("section_index", "s", "i", section_index_name);
declare_function("section_index", "i", "i", section_index_addr);
declare_function("exports", "s", "i", exports);
+ declare_function("exports", "i", "i", exports_ordinal);
declare_function("imports", "ss", "i", imports);
declare_function("imports", "si", "i", imports_ordinal);
declare_function("imports", "s", "i", imports_dll);
@@ -2116,6 +2211,7 @@ begin_declarations;
declare_function("is_64bit", "", "i", is_64bit);
declare_integer("number_of_imports");
+ declare_integer("number_of_exports");
declare_integer("resource_timestamp");
@@ -2503,6 +2599,7 @@ int module_load(
#endif
pe->imported_dlls = pe_parse_imports(pe);
+ pe->exported_dlls = pe_parse_exports(pe);
break;
}
@@ -2518,8 +2615,8 @@ int module_unload(
{
IMPORTED_DLL* dll = NULL;
IMPORTED_DLL* next_dll = NULL;
- IMPORTED_FUNCTION* func = NULL;
- IMPORTED_FUNCTION* next_func = NULL;
+ IMPORT_EXPORT_FUNCTION* func = NULL;
+ IMPORT_EXPORT_FUNCTION* next_func = NULL;
PE* pe = (PE *) module_object->data;
@@ -2552,5 +2649,13 @@ int module_unload(
yr_free(pe);
+ func = pe->exported_dlls;
+ while (func)
+ {
+ yr_free(func->name);
+ next_func = func->next;
+ yr_free(func);
+ func = next_func;
+ }
return ERROR_SUCCESS;
}
--
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