mirror of
https://github.com/VirusTotal/yara.git
synced 2026-10-12 02:10:00 +00:00
4407 lines
128 KiB
C
4407 lines
128 KiB
C
/*
|
|
Copyright (c) 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 <ctype.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
|
|
#include "../crypto.h"
|
|
#if defined(HAVE_LIBCRYPTO)
|
|
#include <authenticode-parser/authenticode.h>
|
|
#include <openssl/evp.h>
|
|
#endif
|
|
|
|
#include <yara/dotnet.h>
|
|
#include <yara/endian.h>
|
|
#include <yara/mem.h>
|
|
#include <yara/modules.h>
|
|
#include <yara/pe.h>
|
|
#include <yara/pe_utils.h>
|
|
#include <yara/strutils.h>
|
|
#include <yara/utils.h>
|
|
|
|
#define MODULE_NAME pe
|
|
|
|
#define IMPORT_STANDARD 1
|
|
#define IMPORT_DELAYED 2
|
|
#define IMPORT_ANY (~0)
|
|
|
|
// http://msdn.microsoft.com/en-us/library/ms648009(v=vs.85).aspx
|
|
#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_GROUP_CURSOR \
|
|
12 // MAKEINTRESOURCE((ULONG_PTR)(RT_CURSOR) + 11)
|
|
#define RESOURCE_TYPE_GROUP_ICON \
|
|
14 // MAKEINTRESOURCE((ULONG_PTR)(RT_ICON) + 11)
|
|
#define RESOURCE_TYPE_VERSION 16
|
|
#define RESOURCE_TYPE_DLGINCLUDE 17
|
|
#define RESOURCE_TYPE_PLUGPLAY 19
|
|
#define RESOURCE_TYPE_VXD 20
|
|
#define RESOURCE_TYPE_ANICURSOR 21
|
|
#define RESOURCE_TYPE_ANIICON 22
|
|
#define RESOURCE_TYPE_HTML 23
|
|
#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_IMPORTS 16384
|
|
#define MAX_PE_EXPORTS 16384
|
|
#define MAX_EXPORT_NAME_LENGTH 512
|
|
#define MAX_IMPORT_DLL_NAME_LENGTH 256
|
|
#define MAX_RESOURCES 65536
|
|
|
|
#define IS_RESOURCE_SUBDIRECTORY(entry) \
|
|
(yr_le32toh((entry)->OffsetToData) & 0x80000000)
|
|
|
|
#define RESOURCE_OFFSET(entry) (yr_le32toh((entry)->OffsetToData) & 0x7FFFFFFF)
|
|
|
|
typedef int (*RESOURCE_CALLBACK_FUNC)(
|
|
PIMAGE_RESOURCE_DATA_ENTRY rsrc_data,
|
|
int rsrc_type,
|
|
int rsrc_id,
|
|
int rsrc_language,
|
|
const IMAGE_RESOURCE_DIR_STRING_U* type_string,
|
|
const IMAGE_RESOURCE_DIR_STRING_U* name_string,
|
|
const IMAGE_RESOURCE_DIR_STRING_U* lang_string,
|
|
void* cb_data);
|
|
|
|
static size_t available_space(PE* pe, void* pointer)
|
|
{
|
|
if ((uint8_t*) pointer < pe->data)
|
|
return 0;
|
|
|
|
if ((uint8_t*) pointer >= pe->data + pe->data_size)
|
|
return 0;
|
|
|
|
return pe->data + pe->data_size - (uint8_t*) pointer;
|
|
}
|
|
|
|
static int wide_string_fits_in_pe(PE* pe, char* data)
|
|
{
|
|
size_t i = 0;
|
|
size_t space_left = available_space(pe, data);
|
|
|
|
while (space_left >= 2)
|
|
{
|
|
if (data[i] == 0 && data[i + 1] == 0)
|
|
return 1;
|
|
space_left -= 2;
|
|
i += 2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Parse the rich signature.
|
|
// http://www.ntcore.com/files/richsign.htm
|
|
|
|
static void pe_parse_rich_signature(PE* pe, uint64_t base_address)
|
|
{
|
|
PIMAGE_DOS_HEADER mz_header;
|
|
PRICH_SIGNATURE rich_signature = NULL;
|
|
|
|
DWORD* rich_ptr = NULL;
|
|
BYTE* raw_data = NULL;
|
|
BYTE* clear_data = NULL;
|
|
BYTE* version_data = NULL;
|
|
DWORD* p = NULL;
|
|
uint32_t nthdr_offset = 0;
|
|
uint32_t key = 0;
|
|
size_t rich_len = 0;
|
|
int64_t rich_count = 0;
|
|
|
|
if (pe->data_size < sizeof(IMAGE_DOS_HEADER))
|
|
return;
|
|
|
|
mz_header = (PIMAGE_DOS_HEADER) pe->data;
|
|
|
|
if (yr_le16toh(mz_header->e_magic) != IMAGE_DOS_SIGNATURE)
|
|
return;
|
|
|
|
// To find the Rich marker we start at the NT header and work backwards, so
|
|
// make sure we have at least enough data to get to the NT header.
|
|
nthdr_offset = yr_le32toh(mz_header->e_lfanew);
|
|
if (nthdr_offset > pe->data_size + sizeof(uint32_t) || nthdr_offset < 4)
|
|
return;
|
|
|
|
// Most files have the Rich header at offset 0x80, but that is not always
|
|
// true. 582ce3eea9c97d5e89f7d83953a6d518b16770e635a19a456c0225449c6967a4 is
|
|
// one sample which has a Rich header starting at offset 0x200. To properly
|
|
// find the Rich header we need to start at the NT header and work backwards.
|
|
p = (DWORD*) (pe->data + nthdr_offset - 4);
|
|
|
|
while (p >= (DWORD*) (pe->data + sizeof(IMAGE_DOS_HEADER)))
|
|
{
|
|
if (yr_le32toh(*p) == RICH_RICH)
|
|
{
|
|
// The XOR key is the dword following the Rich value. We use this to find
|
|
// DanS header only.
|
|
key = *(p + 1);
|
|
rich_ptr = p;
|
|
--p;
|
|
break;
|
|
}
|
|
|
|
// The NT header is 8 byte aligned so we can move back in 4 byte increments.
|
|
--p;
|
|
}
|
|
|
|
// If we haven't found a key we can skip processing the rest.
|
|
if (key == 0)
|
|
return;
|
|
|
|
// If we have found the key we need to now find the start (DanS).
|
|
while (p >= (DWORD*) (pe->data + sizeof(IMAGE_DOS_HEADER)))
|
|
{
|
|
if (yr_le32toh((*(p) ^ key)) == RICH_DANS)
|
|
{
|
|
rich_signature = (PRICH_SIGNATURE) p;
|
|
break;
|
|
}
|
|
|
|
--p;
|
|
}
|
|
|
|
if (rich_signature == NULL)
|
|
return;
|
|
|
|
// Multiply by 4 because we are counting in DWORDs.
|
|
rich_len = (rich_ptr - (DWORD*) rich_signature) * 4;
|
|
raw_data = (BYTE*) yr_malloc(rich_len);
|
|
|
|
if (!raw_data)
|
|
return;
|
|
|
|
memcpy(raw_data, rich_signature, rich_len);
|
|
|
|
yr_set_integer(
|
|
base_address + ((uint8_t*) rich_signature - pe->data),
|
|
pe->object,
|
|
"rich_signature.offset");
|
|
|
|
yr_set_integer(rich_len, pe->object, "rich_signature.length");
|
|
yr_set_integer(yr_le32toh(key), pe->object, "rich_signature.key");
|
|
|
|
clear_data = (BYTE*) yr_malloc(rich_len);
|
|
|
|
if (!clear_data)
|
|
{
|
|
yr_free(raw_data);
|
|
return;
|
|
}
|
|
|
|
// Copy the entire block here to be XORed.
|
|
memcpy(clear_data, raw_data, rich_len);
|
|
|
|
for (rich_ptr = (DWORD*) clear_data;
|
|
rich_ptr < (DWORD*) (clear_data + rich_len);
|
|
rich_ptr++)
|
|
{
|
|
*rich_ptr ^= key;
|
|
}
|
|
|
|
yr_set_sized_string(
|
|
(char*) raw_data, rich_len, pe->object, "rich_signature.raw_data");
|
|
|
|
yr_free(raw_data);
|
|
|
|
yr_set_sized_string(
|
|
(char*) clear_data, rich_len, pe->object, "rich_signature.clear_data");
|
|
|
|
// Allocate space for just the version data. This is a series of every other
|
|
// dword from the clear data. This is useful to be able to hash alone.
|
|
// We need to skip the first 3 DWORDs of the RICH_SIGNATURE, which are DanS
|
|
// and XOR keys.
|
|
rich_count = (rich_len - sizeof(RICH_SIGNATURE)) / sizeof(RICH_VERSION_INFO);
|
|
version_data = (BYTE*) yr_malloc(rich_count * sizeof(DWORD));
|
|
if (!version_data)
|
|
{
|
|
yr_free(clear_data);
|
|
return;
|
|
}
|
|
|
|
rich_signature = (PRICH_SIGNATURE) clear_data;
|
|
for (int i = 0; i < rich_count; i++)
|
|
{
|
|
memcpy(
|
|
version_data + (i * sizeof(DWORD)),
|
|
&rich_signature->versions[i],
|
|
sizeof(DWORD));
|
|
}
|
|
|
|
yr_set_sized_string(
|
|
(char*) version_data,
|
|
rich_count * sizeof(DWORD),
|
|
pe->object,
|
|
"rich_signature.version_data");
|
|
|
|
yr_free(clear_data);
|
|
yr_free(version_data);
|
|
}
|
|
|
|
static void pe_parse_debug_directory(PE* pe)
|
|
{
|
|
PIMAGE_DATA_DIRECTORY data_dir;
|
|
PIMAGE_DEBUG_DIRECTORY debug_dir;
|
|
int64_t debug_dir_offset;
|
|
int i, dcount;
|
|
size_t pdb_path_len;
|
|
char* pdb_path = NULL;
|
|
|
|
data_dir = pe_get_directory_entry(pe, IMAGE_DIRECTORY_ENTRY_DEBUG);
|
|
|
|
if (data_dir == NULL)
|
|
return;
|
|
|
|
if (yr_le32toh(data_dir->Size) == 0)
|
|
return;
|
|
|
|
if (yr_le32toh(data_dir->VirtualAddress) == 0)
|
|
return;
|
|
|
|
debug_dir_offset = pe_rva_to_offset(pe, yr_le32toh(data_dir->VirtualAddress));
|
|
|
|
if (debug_dir_offset < 0)
|
|
return;
|
|
|
|
dcount = yr_le32toh(data_dir->Size) / sizeof(IMAGE_DEBUG_DIRECTORY);
|
|
|
|
for (i = 0; i < dcount; i++)
|
|
{
|
|
int64_t pcv_hdr_offset = 0;
|
|
|
|
debug_dir = (PIMAGE_DEBUG_DIRECTORY) (pe->data + debug_dir_offset +
|
|
i * sizeof(IMAGE_DEBUG_DIRECTORY));
|
|
|
|
if (!struct_fits_in_pe(pe, debug_dir, IMAGE_DEBUG_DIRECTORY))
|
|
break;
|
|
|
|
if (yr_le32toh(debug_dir->Type) != IMAGE_DEBUG_TYPE_CODEVIEW)
|
|
continue;
|
|
|
|
// The debug info offset may be present either as RVA or as raw offset
|
|
// Sample: 0249e00b6d46bee5a17096559f18e671cd0ceee36373e8708f614a9a6c7c079e
|
|
if (debug_dir->AddressOfRawData != 0)
|
|
{
|
|
pcv_hdr_offset = pe_rva_to_offset(
|
|
pe, yr_le32toh(debug_dir->AddressOfRawData));
|
|
}
|
|
|
|
// Give it chance to read it from the RAW offset
|
|
// Sample: 735f72b3fcd72789f01e923c9de2a9ab5b5ffbece23633da81d976ad0ad159e3
|
|
if (pcv_hdr_offset <= 0 && debug_dir->PointerToRawData != 0)
|
|
{
|
|
pcv_hdr_offset = yr_le32toh(debug_dir->PointerToRawData);
|
|
}
|
|
|
|
if (pcv_hdr_offset <= 0)
|
|
continue;
|
|
|
|
PCV_HEADER cv_hdr = (PCV_HEADER) (pe->data + pcv_hdr_offset);
|
|
|
|
if (!struct_fits_in_pe(pe, cv_hdr, CV_HEADER))
|
|
continue;
|
|
|
|
if (yr_le32toh(cv_hdr->dwSignature) == CVINFO_PDB20_CVSIGNATURE)
|
|
{
|
|
PCV_INFO_PDB20 pdb20 = (PCV_INFO_PDB20) cv_hdr;
|
|
|
|
if (struct_fits_in_pe(pe, pdb20, CV_INFO_PDB20))
|
|
pdb_path = (char*) (pdb20->PdbFileName);
|
|
}
|
|
else if (yr_le32toh(cv_hdr->dwSignature) == CVINFO_PDB70_CVSIGNATURE)
|
|
{
|
|
PCV_INFO_PDB70 pdb70 = (PCV_INFO_PDB70) cv_hdr;
|
|
|
|
if (struct_fits_in_pe(pe, pdb70, CV_INFO_PDB70))
|
|
pdb_path = (char*) (pdb70->PdbFileName);
|
|
}
|
|
else if (yr_le32toh(cv_hdr->dwSignature) == CODEVIEW_SIGNATURE_MTOC)
|
|
{
|
|
PMTOC_ENTRY mtoc = (PMTOC_ENTRY) cv_hdr;
|
|
|
|
if (struct_fits_in_pe(pe, mtoc, MTOC_ENTRY))
|
|
pdb_path = (char*) (mtoc->PdbFileName);
|
|
}
|
|
|
|
if (pdb_path != NULL)
|
|
{
|
|
pdb_path_len = strnlen(
|
|
pdb_path, yr_min(available_space(pe, pdb_path), MAX_PATH));
|
|
|
|
if (pdb_path_len >= 0 && pdb_path_len < MAX_PATH)
|
|
{
|
|
yr_set_sized_string(pdb_path, pdb_path_len, pe->object, "pdb_path");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return a pointer to the resource directory string or NULL.
|
|
// The callback function will parse this and call yr_set_sized_string().
|
|
// The pointer is guaranteed to have enough space to contain the entire string.
|
|
|
|
static const PIMAGE_RESOURCE_DIR_STRING_U parse_resource_name(
|
|
PE* pe,
|
|
const uint8_t* rsrc_data,
|
|
PIMAGE_RESOURCE_DIRECTORY_ENTRY entry)
|
|
{
|
|
// If high bit is set it is an offset relative to rsrc_data, which contains
|
|
// a resource directory string.
|
|
|
|
if (yr_le32toh(entry->Name) & 0x80000000)
|
|
{
|
|
const PIMAGE_RESOURCE_DIR_STRING_U pNameString =
|
|
(PIMAGE_RESOURCE_DIR_STRING_U) (rsrc_data +
|
|
(yr_le32toh(entry->Name) & 0x7FFFFFFF));
|
|
|
|
// A resource directory string is 2 bytes for the length and then a variable
|
|
// length Unicode string. Make sure we have at least 2 bytes.
|
|
|
|
if (!fits_in_pe(pe, pNameString, 2))
|
|
return NULL;
|
|
|
|
// Move past the length and make sure we have enough bytes for the string.
|
|
if (!fits_in_pe(
|
|
pe,
|
|
pNameString,
|
|
sizeof(uint16_t) + yr_le16toh(pNameString->Length) * 2))
|
|
return NULL;
|
|
|
|
return pNameString;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int _pe_iterate_resources(
|
|
PE* pe,
|
|
PIMAGE_RESOURCE_DIRECTORY resource_dir,
|
|
const uint8_t* rsrc_data,
|
|
int rsrc_tree_level,
|
|
int* type,
|
|
int* id,
|
|
int* language,
|
|
const IMAGE_RESOURCE_DIR_STRING_U* type_string,
|
|
const IMAGE_RESOURCE_DIR_STRING_U* name_string,
|
|
const IMAGE_RESOURCE_DIR_STRING_U* lang_string,
|
|
RESOURCE_CALLBACK_FUNC callback,
|
|
void* callback_data)
|
|
{
|
|
int i, result = RESOURCE_ITERATOR_FINISHED;
|
|
int total_entries;
|
|
|
|
PIMAGE_RESOURCE_DIRECTORY_ENTRY entry;
|
|
|
|
// A few sanity checks to avoid corrupt files
|
|
|
|
if (yr_le32toh(resource_dir->Characteristics) != 0 ||
|
|
yr_le16toh(resource_dir->NumberOfNamedEntries) > 32768 ||
|
|
yr_le16toh(resource_dir->NumberOfIdEntries) > 32768)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
total_entries = yr_le16toh(resource_dir->NumberOfNamedEntries) +
|
|
yr_le16toh(resource_dir->NumberOfIdEntries);
|
|
|
|
// The first directory entry is just after the resource directory,
|
|
// by incrementing resource_dir we skip sizeof(resource_dir) bytes
|
|
// and get a pointer to the end of the resource directory.
|
|
|
|
entry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY) (resource_dir + 1);
|
|
|
|
if (!fits_in_pe(
|
|
pe, entry, total_entries * sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY)))
|
|
return result;
|
|
|
|
for (i = 0; i < total_entries; i++)
|
|
{
|
|
switch (rsrc_tree_level)
|
|
{
|
|
case 0:
|
|
*type = yr_le32toh(entry->Name);
|
|
type_string = parse_resource_name(pe, rsrc_data, entry);
|
|
break;
|
|
case 1:
|
|
*id = yr_le32toh(entry->Name);
|
|
name_string = parse_resource_name(pe, rsrc_data, entry);
|
|
break;
|
|
case 2:
|
|
*language = yr_le32toh(entry->Name);
|
|
lang_string = parse_resource_name(pe, rsrc_data, entry);
|
|
break;
|
|
}
|
|
|
|
if (IS_RESOURCE_SUBDIRECTORY(entry) && rsrc_tree_level < 2)
|
|
{
|
|
PIMAGE_RESOURCE_DIRECTORY directory =
|
|
(PIMAGE_RESOURCE_DIRECTORY) (rsrc_data + RESOURCE_OFFSET(entry));
|
|
|
|
if (struct_fits_in_pe(pe, directory, IMAGE_RESOURCE_DIRECTORY))
|
|
{
|
|
result = _pe_iterate_resources(
|
|
pe,
|
|
directory,
|
|
rsrc_data,
|
|
rsrc_tree_level + 1,
|
|
type,
|
|
id,
|
|
language,
|
|
type_string,
|
|
name_string,
|
|
lang_string,
|
|
callback,
|
|
callback_data);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PIMAGE_RESOURCE_DATA_ENTRY data_entry =
|
|
(PIMAGE_RESOURCE_DATA_ENTRY) (rsrc_data + RESOURCE_OFFSET(entry));
|
|
|
|
if (struct_fits_in_pe(pe, data_entry, IMAGE_RESOURCE_DATA_ENTRY))
|
|
{
|
|
if (yr_le32toh(data_entry->Size) > 0 &&
|
|
yr_le32toh(data_entry->Size) < pe->data_size)
|
|
{
|
|
if (callback(
|
|
data_entry,
|
|
*type,
|
|
*id,
|
|
*language,
|
|
type_string,
|
|
name_string,
|
|
lang_string,
|
|
callback_data) == RESOURCE_CALLBACK_ABORT)
|
|
{
|
|
result = RESOURCE_ITERATOR_ABORTED;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result == RESOURCE_ITERATOR_ABORTED)
|
|
break;
|
|
|
|
entry++;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static int pe_iterate_resources(
|
|
PE* pe,
|
|
RESOURCE_CALLBACK_FUNC callback,
|
|
void* callback_data)
|
|
{
|
|
int64_t offset;
|
|
|
|
int type = -1;
|
|
int id = -1;
|
|
int language = -1;
|
|
|
|
IMAGE_RESOURCE_DIR_STRING_U* type_string = NULL;
|
|
IMAGE_RESOURCE_DIR_STRING_U* name_string = NULL;
|
|
IMAGE_RESOURCE_DIR_STRING_U* lang_string = NULL;
|
|
|
|
PIMAGE_DATA_DIRECTORY directory = pe_get_directory_entry(
|
|
pe, IMAGE_DIRECTORY_ENTRY_RESOURCE);
|
|
|
|
if (directory == NULL)
|
|
return 0;
|
|
|
|
if (yr_le32toh(directory->VirtualAddress) != 0)
|
|
{
|
|
PIMAGE_RESOURCE_DIRECTORY rsrc_dir;
|
|
|
|
offset = pe_rva_to_offset(pe, yr_le32toh(directory->VirtualAddress));
|
|
|
|
if (offset < 0)
|
|
return 0;
|
|
|
|
rsrc_dir = (PIMAGE_RESOURCE_DIRECTORY) (pe->data + offset);
|
|
|
|
if (struct_fits_in_pe(pe, rsrc_dir, IMAGE_RESOURCE_DIRECTORY))
|
|
{
|
|
yr_set_integer(
|
|
yr_le32toh(rsrc_dir->TimeDateStamp),
|
|
pe->object,
|
|
"resource_timestamp");
|
|
|
|
yr_set_integer(
|
|
yr_le16toh(rsrc_dir->MajorVersion),
|
|
pe->object,
|
|
"resource_version.major");
|
|
|
|
yr_set_integer(
|
|
yr_le16toh(rsrc_dir->MinorVersion),
|
|
pe->object,
|
|
"resource_version.minor");
|
|
|
|
_pe_iterate_resources(
|
|
pe,
|
|
rsrc_dir,
|
|
pe->data + offset,
|
|
0,
|
|
&type,
|
|
&id,
|
|
&language,
|
|
type_string,
|
|
name_string,
|
|
lang_string,
|
|
callback,
|
|
callback_data);
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Align offset to a 32-bit boundary and add it to a pointer
|
|
|
|
#define ADD_OFFSET(ptr, offset) \
|
|
(PVERSION_INFO)((uint8_t*) (ptr) + ((offset + 3) & ~3))
|
|
|
|
static void pe_parse_version_info(PIMAGE_RESOURCE_DATA_ENTRY rsrc_data, PE* pe)
|
|
{
|
|
PVERSION_INFO version_info;
|
|
|
|
int64_t version_info_offset = pe_rva_to_offset(
|
|
pe, yr_le32toh(rsrc_data->OffsetToData));
|
|
|
|
if (version_info_offset < 0)
|
|
return;
|
|
|
|
version_info = (PVERSION_INFO) (pe->data + version_info_offset);
|
|
|
|
if (!struct_fits_in_pe(pe, version_info, VERSION_INFO))
|
|
return;
|
|
|
|
if (!fits_in_pe(pe, version_info->Key, sizeof("VS_VERSION_INFO") * 2))
|
|
return;
|
|
|
|
if (strcmp_w(version_info->Key, "VS_VERSION_INFO") != 0)
|
|
return;
|
|
|
|
version_info = ADD_OFFSET(version_info, sizeof(VERSION_INFO) + 86);
|
|
|
|
while (fits_in_pe(pe, version_info->Key, sizeof("VarFileInfo") * 2) &&
|
|
strcmp_w(version_info->Key, "VarFileInfo") == 0 &&
|
|
yr_le16toh(version_info->Length) != 0)
|
|
{
|
|
version_info = ADD_OFFSET(version_info, yr_le16toh(version_info->Length));
|
|
}
|
|
|
|
while (fits_in_pe(pe, version_info->Key, sizeof("StringFileInfo") * 2) &&
|
|
strcmp_w(version_info->Key, "StringFileInfo") == 0 &&
|
|
yr_le16toh(version_info->Length) != 0)
|
|
{
|
|
PVERSION_INFO string_table = ADD_OFFSET(
|
|
version_info, sizeof(VERSION_INFO) + 30);
|
|
|
|
version_info = ADD_OFFSET(version_info, yr_le16toh(version_info->Length));
|
|
|
|
while (struct_fits_in_pe(pe, string_table, VERSION_INFO) &&
|
|
wide_string_fits_in_pe(pe, string_table->Key) &&
|
|
yr_le16toh(string_table->Length) != 0 && string_table < version_info)
|
|
{
|
|
PVERSION_INFO string = ADD_OFFSET(
|
|
string_table,
|
|
sizeof(VERSION_INFO) + 2 * (strnlen_w(string_table->Key) + 1));
|
|
|
|
string_table = ADD_OFFSET(string_table, yr_le16toh(string_table->Length));
|
|
|
|
while (struct_fits_in_pe(pe, string, VERSION_INFO) &&
|
|
wide_string_fits_in_pe(pe, string->Key) &&
|
|
yr_le16toh(string->Length) != 0 && string < string_table)
|
|
{
|
|
char* string_value = (char*) ADD_OFFSET(
|
|
string, sizeof(VERSION_INFO) + 2 * (strnlen_w(string->Key) + 1));
|
|
|
|
if (wide_string_fits_in_pe(pe, string_value))
|
|
{
|
|
char key[64];
|
|
char value[256];
|
|
|
|
strlcpy_w(key, string->Key, sizeof(key));
|
|
strlcpy_w(value, string_value, sizeof(value));
|
|
|
|
// null terminator of string is not included in version value when
|
|
// ValueLength is zero
|
|
if (yr_le16toh(string->ValueLength) == 0)
|
|
value[yr_le16toh(string->ValueLength)] = '\0';
|
|
|
|
yr_set_string(value, pe->object, "version_info[%s]", key);
|
|
|
|
yr_set_string(
|
|
key, pe->object, "version_info_list[%i].key", pe->version_infos);
|
|
|
|
yr_set_string(
|
|
value,
|
|
pe->object,
|
|
"version_info_list[%i].value",
|
|
pe->version_infos);
|
|
|
|
pe->version_infos += 1;
|
|
}
|
|
|
|
string = ADD_OFFSET(string, yr_le16toh(string->Length));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void pe_set_resource_string_or_id(
|
|
IMAGE_RESOURCE_DIR_STRING_U* rsrc_string,
|
|
int rsrc_int,
|
|
const char* string_description,
|
|
const char* int_description,
|
|
PE* pe)
|
|
{
|
|
if (rsrc_string)
|
|
{
|
|
// Multiply by 2 because it is a Unicode string.
|
|
size_t length = yr_le16toh(rsrc_string->Length) * 2;
|
|
|
|
// Check if the whole string fits in the PE image.
|
|
// If not, the name becomes UNDEFINED by default.
|
|
if (fits_in_pe(pe, rsrc_string->NameString, length))
|
|
{
|
|
yr_set_sized_string(
|
|
(char*) rsrc_string->NameString,
|
|
length,
|
|
pe->object,
|
|
string_description,
|
|
pe->resources);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (rsrc_int != -1)
|
|
yr_set_integer(rsrc_int, pe->object, int_description, pe->resources);
|
|
}
|
|
}
|
|
|
|
static int pe_collect_resources(
|
|
PIMAGE_RESOURCE_DATA_ENTRY rsrc_data,
|
|
int rsrc_type,
|
|
int rsrc_id,
|
|
int rsrc_language,
|
|
IMAGE_RESOURCE_DIR_STRING_U* type_string,
|
|
IMAGE_RESOURCE_DIR_STRING_U* name_string,
|
|
IMAGE_RESOURCE_DIR_STRING_U* lang_string,
|
|
PE* pe)
|
|
{
|
|
// Don't collect too many resources.
|
|
if (pe->resources >= MAX_RESOURCES)
|
|
return RESOURCE_CALLBACK_CONTINUE;
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(rsrc_data->OffsetToData),
|
|
pe->object,
|
|
"resources[%i].rva",
|
|
pe->resources);
|
|
|
|
int64_t offset = pe_rva_to_offset(pe, yr_le32toh(rsrc_data->OffsetToData));
|
|
|
|
if (offset < 0)
|
|
offset = YR_UNDEFINED;
|
|
|
|
yr_set_integer(offset, pe->object, "resources[%i].offset", pe->resources);
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(rsrc_data->Size),
|
|
pe->object,
|
|
"resources[%i].length",
|
|
pe->resources);
|
|
|
|
pe_set_resource_string_or_id(
|
|
type_string,
|
|
rsrc_type,
|
|
"resources[%i].type_string",
|
|
"resources[%i].type",
|
|
pe);
|
|
|
|
pe_set_resource_string_or_id(
|
|
name_string,
|
|
rsrc_id,
|
|
"resources[%i].name_string",
|
|
"resources[%i].id",
|
|
pe);
|
|
|
|
pe_set_resource_string_or_id(
|
|
lang_string,
|
|
rsrc_language,
|
|
"resources[%i].language_string",
|
|
"resources[%i].language",
|
|
pe);
|
|
|
|
// Resources we do extra parsing on
|
|
if (rsrc_type == RESOURCE_TYPE_VERSION)
|
|
pe_parse_version_info(rsrc_data, pe);
|
|
|
|
pe->resources += 1;
|
|
return RESOURCE_CALLBACK_CONTINUE;
|
|
}
|
|
|
|
// Function names should have only lowercase, uppercase, digits and a small
|
|
// subset of special characters. This is to match behavior of pefile. See
|
|
// https://github.com/erocarrera/pefile/blob/593d094e35198dad92aaf040bef17eb800c8a373/pefile.py#L2326-L2348
|
|
static int valid_function_name(char* name)
|
|
{
|
|
if (!strcmp(name, ""))
|
|
return 0;
|
|
|
|
size_t i = 0;
|
|
for (char c = name[i]; c != '\x00'; c = name[++i])
|
|
{
|
|
if (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') &&
|
|
!(c >= '0' && c <= '9') && c != '.' && c != '_' && c != '?' &&
|
|
c != '@' && c != '$' && c != '(' && c != ')' && c != '<' && c != '>')
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static IMPORT_FUNCTION* pe_parse_import_descriptor(
|
|
PE* pe,
|
|
PIMAGE_IMPORT_DESCRIPTOR import_descriptor,
|
|
char* dll_name,
|
|
int* num_function_imports)
|
|
{
|
|
IMPORT_FUNCTION* head = NULL;
|
|
IMPORT_FUNCTION* tail = NULL;
|
|
// This is tracked separately from num_function_imports because that is the
|
|
// number of successfully parsed imports, while this is the number of imports
|
|
// attempted to be parsed. This allows us to stop parsing on too many imports
|
|
// while still accurately recording the number of successfully parsed imports.
|
|
int parsed_imports = 0;
|
|
|
|
int64_t offset = pe_rva_to_offset(
|
|
pe, yr_le32toh(import_descriptor->OriginalFirstThunk));
|
|
|
|
// I've seen binaries where OriginalFirstThunk is zero. In this case
|
|
// use FirstThunk.
|
|
|
|
if (offset <= 0)
|
|
offset = pe_rva_to_offset(pe, yr_le32toh(import_descriptor->FirstThunk));
|
|
|
|
if (offset < 0)
|
|
return NULL;
|
|
|
|
if (IS_64BITS_PE(pe))
|
|
{
|
|
PIMAGE_THUNK_DATA64 thunks64 = (PIMAGE_THUNK_DATA64) (pe->data + offset);
|
|
uint64_t func_idx = 0;
|
|
|
|
while (struct_fits_in_pe(pe, thunks64, IMAGE_THUNK_DATA64) &&
|
|
yr_le64toh(thunks64->u1.Ordinal) != 0 &&
|
|
parsed_imports < MAX_PE_IMPORTS)
|
|
{
|
|
char* name = NULL;
|
|
uint16_t ordinal = 0;
|
|
uint8_t has_ordinal = 0;
|
|
uint64_t rva_address = 0;
|
|
|
|
parsed_imports++;
|
|
|
|
if (!(yr_le64toh(thunks64->u1.Ordinal) & IMAGE_ORDINAL_FLAG64))
|
|
{
|
|
// If imported by name
|
|
offset = pe_rva_to_offset(pe, yr_le64toh(thunks64->u1.Function));
|
|
|
|
if (offset >= 0)
|
|
{
|
|
PIMAGE_IMPORT_BY_NAME import = (PIMAGE_IMPORT_BY_NAME) (pe->data +
|
|
offset);
|
|
|
|
if (struct_fits_in_pe(pe, import, IMAGE_IMPORT_BY_NAME))
|
|
{
|
|
name = (char*) yr_strndup(
|
|
(char*) import->Name,
|
|
yr_min(available_space(pe, import->Name), 512));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If imported by ordinal. Lookup the ordinal.
|
|
name = ord_lookup(dll_name, yr_le64toh(thunks64->u1.Ordinal) & 0xFFFF);
|
|
// Also store the ordinal.
|
|
ordinal = yr_le64toh(thunks64->u1.Ordinal) & 0xFFFF;
|
|
has_ordinal = 1;
|
|
}
|
|
|
|
rva_address = yr_le32toh(import_descriptor->FirstThunk) +
|
|
(sizeof(uint64_t) * func_idx);
|
|
|
|
if (name != NULL && !valid_function_name(name))
|
|
{
|
|
yr_free(name);
|
|
thunks64++;
|
|
func_idx++;
|
|
continue;
|
|
}
|
|
|
|
if (name != NULL || has_ordinal == 1)
|
|
{
|
|
IMPORT_FUNCTION* imported_func = (IMPORT_FUNCTION*) yr_calloc(
|
|
1, sizeof(IMPORT_FUNCTION));
|
|
|
|
if (imported_func == NULL)
|
|
{
|
|
yr_free(name);
|
|
}
|
|
else
|
|
{
|
|
imported_func->name = name;
|
|
imported_func->ordinal = ordinal;
|
|
imported_func->has_ordinal = has_ordinal;
|
|
imported_func->rva = rva_address;
|
|
imported_func->next = NULL;
|
|
|
|
if (head == NULL)
|
|
head = imported_func;
|
|
|
|
if (tail != NULL)
|
|
tail->next = imported_func;
|
|
|
|
tail = imported_func;
|
|
(*num_function_imports)++;
|
|
}
|
|
}
|
|
|
|
thunks64++;
|
|
func_idx++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PIMAGE_THUNK_DATA32 thunks32 = (PIMAGE_THUNK_DATA32) (pe->data + offset);
|
|
uint32_t func_idx = 0;
|
|
|
|
while (struct_fits_in_pe(pe, thunks32, IMAGE_THUNK_DATA32) &&
|
|
yr_le32toh(thunks32->u1.Ordinal) != 0 &&
|
|
*num_function_imports < MAX_PE_IMPORTS)
|
|
{
|
|
char* name = NULL;
|
|
uint16_t ordinal = 0;
|
|
uint8_t has_ordinal = 0;
|
|
uint32_t rva_address = 0;
|
|
|
|
parsed_imports++;
|
|
|
|
if (!(yr_le32toh(thunks32->u1.Ordinal) & IMAGE_ORDINAL_FLAG32))
|
|
{
|
|
// If imported by name
|
|
offset = pe_rva_to_offset(pe, yr_le32toh(thunks32->u1.Function));
|
|
|
|
if (offset >= 0)
|
|
{
|
|
PIMAGE_IMPORT_BY_NAME import = (PIMAGE_IMPORT_BY_NAME) (pe->data +
|
|
offset);
|
|
|
|
if (struct_fits_in_pe(pe, import, IMAGE_IMPORT_BY_NAME))
|
|
{
|
|
name = (char*) yr_strndup(
|
|
(char*) import->Name,
|
|
yr_min(available_space(pe, import->Name), 512));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If imported by ordinal. Lookup the ordinal.
|
|
name = ord_lookup(dll_name, yr_le32toh(thunks32->u1.Ordinal) & 0xFFFF);
|
|
// Also store the ordinal.
|
|
ordinal = yr_le32toh(thunks32->u1.Ordinal) & 0xFFFF;
|
|
has_ordinal = 1;
|
|
}
|
|
|
|
rva_address = yr_le32toh(import_descriptor->FirstThunk) +
|
|
(sizeof(uint32_t) * func_idx);
|
|
|
|
if (name != NULL && !valid_function_name(name))
|
|
{
|
|
yr_free(name);
|
|
thunks32++;
|
|
func_idx++;
|
|
continue;
|
|
}
|
|
|
|
if (name != NULL || has_ordinal == 1)
|
|
{
|
|
IMPORT_FUNCTION* imported_func = (IMPORT_FUNCTION*) yr_calloc(
|
|
1, sizeof(IMPORT_FUNCTION));
|
|
|
|
if (imported_func == NULL)
|
|
{
|
|
yr_free(name);
|
|
}
|
|
else
|
|
{
|
|
imported_func->name = name;
|
|
imported_func->ordinal = ordinal;
|
|
imported_func->has_ordinal = has_ordinal;
|
|
imported_func->rva = rva_address;
|
|
imported_func->next = NULL;
|
|
|
|
if (head == NULL)
|
|
head = imported_func;
|
|
|
|
if (tail != NULL)
|
|
tail->next = imported_func;
|
|
|
|
tail = imported_func;
|
|
(*num_function_imports)++;
|
|
}
|
|
}
|
|
|
|
thunks32++;
|
|
func_idx++;
|
|
}
|
|
}
|
|
|
|
return head;
|
|
}
|
|
|
|
//
|
|
// In Windows PE files, any printable character including 0x20 and above is
|
|
// allowed. The only exceptions are characters that are invalid for file names
|
|
// in Windows, which are "*<>?|. While they still can be present in the import
|
|
// directory, such module can never be present in Windows, so we can treat them
|
|
// as invalid.
|
|
//
|
|
// Explicit: The above also applies to slash, backslash (these form a relative
|
|
// path in a subdirectory, which is allowed in the import directory) and colon
|
|
// (which forms a file name with an Alternate Data Stream "test:file.dll" - also
|
|
// allowed).
|
|
//
|
|
// Proof of concept: https://github.com/ladislav-zezula/ImportTest
|
|
//
|
|
// Samples
|
|
// -------
|
|
// f561d60bff4342e529b2c793e216b73a72e6256f90ab24c3cc460646371130ca (imports
|
|
// "test/file.dll")
|
|
// b7f7b8a001769eb0f9c36cb27626b62cabdca9a716a222066028fcd206244b40 (imports
|
|
// "test\file.dll")
|
|
// 94cfb8223132da0a76f9dfbd35a29ab78e5806651758650292ab9c7baf2c0bc2 (imports
|
|
// "test:file.dll")
|
|
// eb2e2c443840276afe095fff05a3a24c00e610ac0e020233d6cd7a0b0b340fb1 (the
|
|
// imported DLL)
|
|
//
|
|
|
|
static int pe_valid_dll_name(const char* dll_name, size_t n)
|
|
{
|
|
const unsigned char* c = (const unsigned char*) dll_name;
|
|
size_t l = 0;
|
|
|
|
while (l < n && *c != '\0')
|
|
{
|
|
if (*c < ' ' || *c > 0x7e || *c == '\"' || *c == '*' || *c == '<' ||
|
|
*c == '>' || *c == '?' || *c == '|')
|
|
{
|
|
return false;
|
|
}
|
|
|
|
c++;
|
|
l++;
|
|
}
|
|
|
|
return (l > 0 && l < n);
|
|
}
|
|
|
|
void pe_set_imports(
|
|
PE* pe,
|
|
IMPORTED_DLL* dll,
|
|
const char* dll_name,
|
|
const char* dll_number_of_functions,
|
|
const char* fun_name,
|
|
const char* fun_ordinal,
|
|
const char* rva)
|
|
{
|
|
int dll_cnt = 0;
|
|
|
|
for (; dll != NULL; dll = dll->next, dll_cnt++)
|
|
{
|
|
int fun_cnt = 0;
|
|
|
|
for (IMPORT_FUNCTION* func = dll->functions; func != NULL;
|
|
func = func->next, fun_cnt++)
|
|
{
|
|
yr_set_string(func->name, pe->object, fun_name, dll_cnt, fun_cnt);
|
|
|
|
if (func->has_ordinal)
|
|
yr_set_integer(
|
|
func->ordinal, pe->object, fun_ordinal, dll_cnt, fun_cnt);
|
|
else
|
|
yr_set_integer(YR_UNDEFINED, pe->object, fun_ordinal, dll_cnt, fun_cnt);
|
|
|
|
if (func->rva)
|
|
yr_set_integer(func->rva, pe->object, rva, dll_cnt, fun_cnt);
|
|
else
|
|
yr_set_integer(YR_UNDEFINED, pe->object, rva, dll_cnt, fun_cnt);
|
|
}
|
|
yr_set_string(dll->name, pe->object, dll_name, dll_cnt);
|
|
yr_set_integer(fun_cnt, pe->object, dll_number_of_functions, dll_cnt);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Walk the imports and collect relevant information. It is used in the
|
|
// "imports" function for comparison and in the "imphash" function for
|
|
// calculation.
|
|
//
|
|
|
|
static IMPORTED_DLL* pe_parse_imports(PE* pe)
|
|
{
|
|
int64_t offset;
|
|
int parsed_imports = 0; // Number of parsed DLLs
|
|
int num_imports = 0; // Number of imported DLLs
|
|
int num_function_imports = 0; // Total number of functions imported
|
|
|
|
IMPORTED_DLL* head = NULL;
|
|
IMPORTED_DLL* tail = NULL;
|
|
|
|
PIMAGE_IMPORT_DESCRIPTOR imports;
|
|
PIMAGE_DATA_DIRECTORY directory;
|
|
|
|
// Default to 0 imports until we know there are any
|
|
yr_set_integer(0, pe->object, "number_of_imports");
|
|
yr_set_integer(0, pe->object, "number_of_imported_functions");
|
|
|
|
directory = pe_get_directory_entry(pe, IMAGE_DIRECTORY_ENTRY_IMPORT);
|
|
|
|
if (directory == NULL)
|
|
return NULL;
|
|
|
|
if (yr_le32toh(directory->VirtualAddress) == 0)
|
|
return NULL;
|
|
|
|
offset = pe_rva_to_offset(pe, yr_le32toh(directory->VirtualAddress));
|
|
|
|
if (offset < 0)
|
|
return NULL;
|
|
|
|
imports = (PIMAGE_IMPORT_DESCRIPTOR) (pe->data + offset);
|
|
|
|
while (struct_fits_in_pe(pe, imports, IMAGE_IMPORT_DESCRIPTOR) &&
|
|
yr_le32toh(imports->Name) != 0 && parsed_imports < MAX_PE_IMPORTS)
|
|
{
|
|
parsed_imports++;
|
|
|
|
int64_t offset = pe_rva_to_offset(pe, yr_le32toh(imports->Name));
|
|
|
|
if (offset >= 0)
|
|
{
|
|
IMPORTED_DLL* imported_dll;
|
|
|
|
char* dll_name = (char*) (pe->data + offset);
|
|
|
|
if (!pe_valid_dll_name(
|
|
dll_name,
|
|
yr_min(
|
|
// DLL names longer than MAX_IMPORT_DLL_NAME_LENGTH
|
|
// are considered invalid.
|
|
pe->data_size - (size_t) offset,
|
|
MAX_IMPORT_DLL_NAME_LENGTH)))
|
|
{
|
|
imports++;
|
|
continue;
|
|
}
|
|
|
|
imported_dll = (IMPORTED_DLL*) yr_calloc(1, sizeof(IMPORTED_DLL));
|
|
|
|
if (imported_dll != NULL)
|
|
{
|
|
IMPORT_FUNCTION* functions = pe_parse_import_descriptor(
|
|
pe, imports, dll_name, &num_function_imports);
|
|
|
|
if (functions != NULL)
|
|
{
|
|
imported_dll->name = yr_strdup(dll_name);
|
|
imported_dll->functions = functions;
|
|
imported_dll->next = NULL;
|
|
|
|
if (head == NULL)
|
|
head = imported_dll;
|
|
|
|
if (tail != NULL)
|
|
tail->next = imported_dll;
|
|
|
|
tail = imported_dll;
|
|
num_imports++;
|
|
}
|
|
else
|
|
{
|
|
yr_free(imported_dll);
|
|
}
|
|
}
|
|
}
|
|
|
|
imports++;
|
|
}
|
|
|
|
yr_set_integer(num_imports, pe->object, "number_of_imports");
|
|
yr_set_integer(
|
|
num_function_imports, pe->object, "number_of_imported_functions");
|
|
pe_set_imports(
|
|
pe,
|
|
head,
|
|
"import_details[%i].library_name",
|
|
"import_details[%i].number_of_functions",
|
|
"import_details[%i].functions[%i].name",
|
|
"import_details[%i].functions[%i].ordinal",
|
|
"import_details[%i].functions[%i].rva");
|
|
|
|
return head;
|
|
}
|
|
|
|
// Delay-import descriptors made by MS Visual C++ 6.0 have old format
|
|
// of delay import directory, where all entries are VAs (as opposite to RVAs
|
|
// from newer MS compilers). We convert the delay-import directory entries to
|
|
// RVAs by checking the lowest bit in the delay-import descriptor's Attributes
|
|
// value
|
|
uint64_t pe_normalize_delay_import_value(
|
|
uint64_t image_base,
|
|
uint64_t virtual_address)
|
|
{
|
|
// Ignore zero items
|
|
if (virtual_address != 0)
|
|
{
|
|
// Sample: 0fc4cb0620f95bdd624f2c78eea4d2b59594244c6671cf249526adf2f2cb71ec
|
|
// Contains artificially created delay import directory with incorrect
|
|
// values:
|
|
//
|
|
// Attributes 0x00000000 <-- Old MS delay import
|
|
// record, contains VAs NameRva 0x004010e6
|
|
// ModuleHandleRva 0x00000000
|
|
// DelayImportAddressTableRva 0x00001140 <-- WRONG! This is an RVA
|
|
// DelayImportNameTableRva 0x004010c0
|
|
// BoundDelayImportTableRva 0x00000000
|
|
// ...
|
|
|
|
if (virtual_address > image_base)
|
|
{
|
|
virtual_address = virtual_address - image_base;
|
|
}
|
|
}
|
|
|
|
return virtual_address;
|
|
}
|
|
|
|
int pe_is_termination_delay_import_entry(
|
|
PIMAGE_DELAYLOAD_DESCRIPTOR importDescriptor)
|
|
{
|
|
return (
|
|
importDescriptor->Attributes.AllAttributes == 0 &&
|
|
importDescriptor->DllNameRVA == 0 &&
|
|
importDescriptor->ModuleHandleRVA == 0 &&
|
|
importDescriptor->ImportAddressTableRVA == 0 &&
|
|
importDescriptor->ImportNameTableRVA == 0 &&
|
|
importDescriptor->BoundImportAddressTableRVA == 0 &&
|
|
importDescriptor->UnloadInformationTableRVA == 0 &&
|
|
importDescriptor->TimeDateStamp == 0);
|
|
}
|
|
|
|
char* pe_parse_delay_import_dll_name(PE* pe, uint64_t rva)
|
|
{
|
|
const int64_t offset = pe_rva_to_offset(pe, rva);
|
|
|
|
if (offset < 0)
|
|
return NULL;
|
|
|
|
char* dll_name = (char*) (pe->data + offset);
|
|
|
|
if (!pe_valid_dll_name(dll_name, pe->data_size - (size_t) offset))
|
|
return NULL;
|
|
|
|
return yr_strdup(dll_name);
|
|
}
|
|
|
|
uint64_t pe_parse_delay_import_pointer(
|
|
PE* pe,
|
|
uint64_t pointerSize,
|
|
uint64_t rva)
|
|
{
|
|
const int64_t offset = pe_rva_to_offset(pe, rva);
|
|
const uint8_t* data = pe->data + offset;
|
|
|
|
if (!fits_in_pe(pe, data, pointerSize))
|
|
return YR_UNDEFINED;
|
|
|
|
if (IS_64BITS_PE(pe))
|
|
return yr_le64toh(*(uint64_t*) data);
|
|
else
|
|
return yr_le32toh(*(uint32_t*) data);
|
|
}
|
|
|
|
static void* pe_parse_delayed_imports(PE* pe)
|
|
{
|
|
int64_t offset;
|
|
uint64_t num_imports = 0; // Number of imported DLLs
|
|
uint64_t num_function_imports = 0; // Total number of functions imported
|
|
uint64_t image_base = OptionalHeader(pe, ImageBase);
|
|
uint64_t size_of_image = OptionalHeader(pe, SizeOfImage);
|
|
uint64_t pointer_size = (IS_64BITS_PE(pe)) ? 8 : 4;
|
|
uint64_t ordinal_mask = (IS_64BITS_PE(pe)) ? IMAGE_ORDINAL_FLAG64
|
|
: IMAGE_ORDINAL_FLAG32;
|
|
|
|
IMPORTED_DLL* head_dll = NULL;
|
|
IMPORTED_DLL* tail_dll = NULL;
|
|
|
|
IMPORT_FUNCTION* head_fun = NULL;
|
|
IMPORT_FUNCTION* tail_fun = NULL;
|
|
|
|
PIMAGE_DELAYLOAD_DESCRIPTOR import_descriptor = NULL;
|
|
PIMAGE_DATA_DIRECTORY directory = NULL;
|
|
|
|
// Default to 0 imports until we know there are any
|
|
yr_set_integer(0, pe->object, "number_of_delayed_imports");
|
|
yr_set_integer(0, pe->object, "number_of_delayed_imported_functions");
|
|
|
|
directory = pe_get_directory_entry(pe, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
|
|
|
|
if (directory == NULL)
|
|
return NULL;
|
|
|
|
if (yr_le32toh(directory->VirtualAddress) == 0)
|
|
return NULL;
|
|
|
|
offset = pe_rva_to_offset(pe, yr_le32toh(directory->VirtualAddress));
|
|
|
|
if (offset < 0)
|
|
return NULL;
|
|
|
|
import_descriptor = (PIMAGE_DELAYLOAD_DESCRIPTOR) (pe->data + offset);
|
|
|
|
for (; struct_fits_in_pe(pe, import_descriptor, IMAGE_DELAYLOAD_DESCRIPTOR);
|
|
import_descriptor++)
|
|
{
|
|
// Check for the termination entry
|
|
if (pe_is_termination_delay_import_entry(import_descriptor))
|
|
break;
|
|
|
|
DWORD Attributes = yr_le32toh(import_descriptor->Attributes.AllAttributes);
|
|
DWORD DllNameRVA = yr_le32toh(import_descriptor->DllNameRVA);
|
|
DWORD ModuleHandleRVA = yr_le32toh(import_descriptor->ModuleHandleRVA);
|
|
DWORD ImportAddressTableRVA = yr_le32toh(
|
|
import_descriptor->ImportAddressTableRVA);
|
|
DWORD ImportNameTableRVA = yr_le32toh(
|
|
import_descriptor->ImportNameTableRVA);
|
|
DWORD BoundImportAddressTableRVA = yr_le32toh(
|
|
import_descriptor->BoundImportAddressTableRVA);
|
|
DWORD UnloadInformationTableRVA = yr_le32toh(
|
|
import_descriptor->UnloadInformationTableRVA);
|
|
|
|
// Valid delayed import entry starts either with 0 or 0x01.
|
|
// We strict require one of the valid values here
|
|
if (Attributes > 0x1)
|
|
break;
|
|
|
|
// Convert older (MS Visual C++ 6.0) delay-import descriptor to newer one.
|
|
// These delay-import descriptors are distinguishable by lowest bit in
|
|
// rec.Attributes to be zero. Sample:
|
|
// 2775d97f8bdb3311ace960a42eee35dbec84b9d71a6abbacb26c14e83f5897e4
|
|
if (!IS_64BITS_PE(pe) && !Attributes)
|
|
{
|
|
DllNameRVA = (DWORD) pe_normalize_delay_import_value(
|
|
image_base, DllNameRVA);
|
|
ModuleHandleRVA = (DWORD) pe_normalize_delay_import_value(
|
|
image_base, ModuleHandleRVA);
|
|
ImportAddressTableRVA = (DWORD) pe_normalize_delay_import_value(
|
|
image_base, ImportAddressTableRVA);
|
|
ImportNameTableRVA = (DWORD) pe_normalize_delay_import_value(
|
|
image_base, ImportNameTableRVA);
|
|
BoundImportAddressTableRVA = (DWORD) pe_normalize_delay_import_value(
|
|
image_base, BoundImportAddressTableRVA);
|
|
UnloadInformationTableRVA = (DWORD) pe_normalize_delay_import_value(
|
|
image_base, UnloadInformationTableRVA);
|
|
}
|
|
|
|
// Stop on blatantly invalid delay import entries (old PELIB behavior)
|
|
if (ImportNameTableRVA >= size_of_image ||
|
|
ImportAddressTableRVA >= size_of_image ||
|
|
DllNameRVA < sizeof(IMAGE_DOS_HEADER) ||
|
|
ImportNameTableRVA < sizeof(IMAGE_DOS_HEADER))
|
|
break;
|
|
|
|
char* dll_name = pe_parse_delay_import_dll_name(pe, DllNameRVA);
|
|
|
|
if (dll_name == NULL)
|
|
continue;
|
|
|
|
IMPORTED_DLL* imported_dll = (IMPORTED_DLL*) yr_calloc(
|
|
1, sizeof(IMPORTED_DLL));
|
|
|
|
if (imported_dll == NULL)
|
|
{
|
|
yr_free(dll_name);
|
|
continue;
|
|
}
|
|
|
|
imported_dll->name = dll_name;
|
|
imported_dll->next = NULL;
|
|
imported_dll->functions = NULL;
|
|
|
|
head_fun = tail_fun = NULL;
|
|
|
|
uint64_t name_rva = ImportNameTableRVA;
|
|
uint64_t func_rva = ImportAddressTableRVA;
|
|
|
|
for (;;)
|
|
{
|
|
uint64_t nameAddress = pe_parse_delay_import_pointer(
|
|
pe, pointer_size, name_rva);
|
|
|
|
uint64_t funcAddress = pe_parse_delay_import_pointer(
|
|
pe, pointer_size, func_rva);
|
|
|
|
// Value of YR_UNDEFINED means that value is outside of pe->data
|
|
if (nameAddress == YR_UNDEFINED || funcAddress == YR_UNDEFINED)
|
|
break;
|
|
|
|
// Value of zero means that this is the end of the bound import name table
|
|
if (nameAddress == 0 || funcAddress == 0)
|
|
break;
|
|
|
|
IMPORT_FUNCTION* imported_func = (IMPORT_FUNCTION*) yr_malloc(
|
|
sizeof(IMPORT_FUNCTION));
|
|
|
|
if (imported_func == NULL)
|
|
continue;
|
|
|
|
imported_func->name = NULL;
|
|
imported_func->has_ordinal = 0;
|
|
imported_func->ordinal = 0;
|
|
imported_func->rva = 0;
|
|
imported_func->next = NULL;
|
|
|
|
// Check name address. It could be ordinal, VA or RVA
|
|
if (!(nameAddress & ordinal_mask))
|
|
{
|
|
// Convert name address to RVA, if needed
|
|
if (!Attributes)
|
|
nameAddress = pe_normalize_delay_import_value(
|
|
image_base, nameAddress);
|
|
|
|
offset = pe_rva_to_offset(pe, nameAddress + sizeof(uint16_t));
|
|
|
|
imported_func->name = (char*) yr_strndup(
|
|
(char*) (pe->data + offset),
|
|
yr_min(available_space(pe, (char*) (pe->data + offset)), 512));
|
|
}
|
|
else
|
|
{
|
|
// If imported by ordinal. Lookup the ordinal.
|
|
imported_func->name = ord_lookup(dll_name, nameAddress & 0xFFFF);
|
|
|
|
// Also store the ordinal.
|
|
imported_func->ordinal = nameAddress & 0xFFFF;
|
|
imported_func->has_ordinal = 1;
|
|
}
|
|
|
|
imported_func->rva = func_rva;
|
|
|
|
num_function_imports++;
|
|
name_rva += pointer_size;
|
|
func_rva += pointer_size;
|
|
|
|
if (head_fun == NULL)
|
|
head_fun = imported_func;
|
|
|
|
if (tail_fun != NULL)
|
|
tail_fun->next = imported_func;
|
|
|
|
tail_fun = imported_func;
|
|
}
|
|
|
|
num_imports++;
|
|
|
|
imported_dll->functions = head_fun;
|
|
|
|
if (head_dll == NULL)
|
|
head_dll = imported_dll;
|
|
|
|
if (tail_dll != NULL)
|
|
tail_dll->next = imported_dll;
|
|
|
|
tail_dll = imported_dll;
|
|
}
|
|
|
|
yr_set_integer(num_imports, pe->object, "number_of_delayed_imports");
|
|
yr_set_integer(
|
|
num_function_imports, pe->object, "number_of_delayed_imported_functions");
|
|
|
|
pe_set_imports(
|
|
pe,
|
|
head_dll,
|
|
"delayed_import_details[%i].library_name",
|
|
"delayed_import_details[%i].number_of_functions",
|
|
"delayed_import_details[%i].functions[%i].name",
|
|
"delayed_import_details[%i].functions[%i].ordinal",
|
|
"delayed_import_details[%i].functions[%i].rva");
|
|
|
|
return head_dll;
|
|
}
|
|
|
|
//
|
|
// Walk the exports and collect relevant information. It is used in the
|
|
// "exports" function for comparison.
|
|
//
|
|
|
|
static void pe_parse_exports(PE* pe)
|
|
{
|
|
PIMAGE_DATA_DIRECTORY directory;
|
|
PIMAGE_EXPORT_DIRECTORY exports;
|
|
|
|
int64_t offset;
|
|
int64_t export_start;
|
|
|
|
uint32_t i, j;
|
|
uint32_t number_of_exports;
|
|
uint32_t number_of_names;
|
|
uint32_t ordinal_base;
|
|
|
|
size_t export_size;
|
|
size_t remaining;
|
|
size_t name_len;
|
|
|
|
uint32_t exp_sz = 0;
|
|
DWORD* names = NULL;
|
|
WORD* ordinals = NULL;
|
|
DWORD* function_addrs = NULL;
|
|
|
|
// If not a PE file, return YR_UNDEFINED
|
|
|
|
if (pe == NULL)
|
|
return;
|
|
|
|
// Default to 0 exports until we know there are any
|
|
yr_set_integer(0, pe->object, "number_of_exports");
|
|
|
|
directory = pe_get_directory_entry(pe, IMAGE_DIRECTORY_ENTRY_EXPORT);
|
|
|
|
if (directory == NULL)
|
|
return;
|
|
|
|
if (yr_le32toh(directory->VirtualAddress) == 0)
|
|
return;
|
|
|
|
offset = pe_rva_to_offset(pe, yr_le32toh(directory->VirtualAddress));
|
|
|
|
if (offset < 0)
|
|
return;
|
|
|
|
export_start = offset;
|
|
export_size = yr_le32toh(directory->Size);
|
|
|
|
exports = (PIMAGE_EXPORT_DIRECTORY) (pe->data + offset);
|
|
|
|
if (!struct_fits_in_pe(pe, exports, IMAGE_EXPORT_DIRECTORY))
|
|
return;
|
|
|
|
number_of_exports = yr_min(
|
|
yr_le32toh(exports->NumberOfFunctions), MAX_PE_EXPORTS);
|
|
|
|
ordinal_base = yr_le32toh(exports->Base);
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(exports->TimeDateStamp), pe->object, "export_timestamp");
|
|
|
|
offset = pe_rva_to_offset(pe, yr_le32toh(exports->Name));
|
|
|
|
if (offset > 0)
|
|
{
|
|
remaining = pe->data_size - (size_t) offset;
|
|
name_len = strnlen((char*) (pe->data + offset), remaining);
|
|
yr_set_sized_string(
|
|
(char*) (pe->data + offset), name_len, pe->object, "dll_name");
|
|
}
|
|
|
|
if (number_of_exports * sizeof(DWORD) > pe->data_size - offset)
|
|
return;
|
|
|
|
if (yr_le32toh(exports->NumberOfNames) > 0)
|
|
{
|
|
offset = pe_rva_to_offset(pe, yr_le32toh(exports->AddressOfNames));
|
|
|
|
if (offset < 0)
|
|
return;
|
|
|
|
if (yr_le32toh(exports->NumberOfNames) * sizeof(DWORD) >
|
|
pe->data_size - offset)
|
|
return;
|
|
|
|
names = (DWORD*) (pe->data + offset);
|
|
}
|
|
|
|
offset = pe_rva_to_offset(pe, yr_le32toh(exports->AddressOfNameOrdinals));
|
|
|
|
if (offset < 0)
|
|
return;
|
|
|
|
ordinals = (WORD*) (pe->data + offset);
|
|
|
|
if (available_space(pe, ordinals) < sizeof(WORD) * number_of_exports)
|
|
return;
|
|
|
|
offset = pe_rva_to_offset(pe, yr_le32toh(exports->AddressOfFunctions));
|
|
|
|
if (offset < 0)
|
|
return;
|
|
|
|
function_addrs = (DWORD*) (pe->data + offset);
|
|
|
|
if (available_space(pe, function_addrs) < sizeof(DWORD) * number_of_exports)
|
|
return;
|
|
|
|
number_of_names = yr_min(
|
|
yr_le32toh(yr_le32toh(exports->NumberOfNames)), number_of_exports);
|
|
|
|
// Mapping out the exports is a bit janky. We start with the export address
|
|
// array. The index from that array plus the ordinal base is the ordinal for
|
|
// that export. To find the name we walk the ordinal array looking for a value
|
|
// that matches our index. If one exists we look up the corresponding RVA from
|
|
// the names array and follow it to get the name. If one does not exist then
|
|
// the export has no name.
|
|
//
|
|
// Ordinal base: 5
|
|
// 0 1 2
|
|
// Address array: [ 0x00000011 | 0x00000022 | 0x00000033 ]
|
|
// 0 1 2
|
|
// Ordinal array: [ 0x0000 | 0x0002 | 0x0001 ]
|
|
// 0 1
|
|
// Names array: [ 0x00000044 | 0x00000055 ]
|
|
//
|
|
// The function at RVA 0x00000011 (index 0) has ordinal 5 (base + index). The
|
|
// index can be found in position 0 in the ordinal array. Using 0 to index
|
|
// into the name array gives us an RVA (0x00000044) which we can follow to get
|
|
// the name.
|
|
//
|
|
// The function at RVA 0x00000022 (index 1) has ordinal 6 (base + index). The
|
|
// index can be found in position 2 in the ordinal array. 2 is out of bounds
|
|
// for the names array so this function is exported without a name.
|
|
//
|
|
// The function at RVA 0x00000033 (index 2) has ordinal 7 (base + index). The
|
|
// index can be found in position 1 in the ordinal array. Using 1 to index
|
|
// into the name array gives us an RVA (0x00000055) which we can follow to get
|
|
// the name.
|
|
//
|
|
// If the RVA from the address array is within the export directory it is a
|
|
// forwarder RVA and points to a NULL terminated ASCII string.
|
|
|
|
for (i = 0; i < number_of_exports; i++)
|
|
{
|
|
yr_set_integer(
|
|
ordinal_base + i, pe->object, "export_details[%i].ordinal", exp_sz);
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(function_addrs[i]),
|
|
pe->object,
|
|
"export_details[%i].rva",
|
|
exp_sz);
|
|
|
|
// Don't check for a failure here since some packers make this an invalid
|
|
// value.
|
|
offset = pe_rva_to_offset(pe, yr_le32toh(function_addrs[i]));
|
|
|
|
if (offset > export_start && offset < export_start + export_size)
|
|
{
|
|
remaining = pe->data_size - (size_t) offset;
|
|
name_len = strnlen((char*) (pe->data + offset), remaining);
|
|
|
|
yr_set_sized_string(
|
|
(char*) (pe->data + offset),
|
|
yr_min(name_len, MAX_EXPORT_NAME_LENGTH),
|
|
pe->object,
|
|
"export_details[%i].forward_name",
|
|
exp_sz);
|
|
}
|
|
else
|
|
{
|
|
if (offset < 0)
|
|
offset = YR_UNDEFINED;
|
|
yr_set_integer(offset, pe->object, "export_details[%i].offset", exp_sz);
|
|
}
|
|
|
|
if (names != NULL)
|
|
{
|
|
for (j = 0; j < number_of_exports; j++)
|
|
{
|
|
if (yr_le16toh(ordinals[j]) == i && j < number_of_names)
|
|
{
|
|
offset = pe_rva_to_offset(pe, yr_le32toh(names[j]));
|
|
|
|
if (offset > 0)
|
|
{
|
|
remaining = pe->data_size - (size_t) offset;
|
|
name_len = strnlen((char*) (pe->data + offset), remaining);
|
|
|
|
yr_set_sized_string(
|
|
(char*) (pe->data + offset),
|
|
yr_min(name_len, MAX_EXPORT_NAME_LENGTH),
|
|
pe->object,
|
|
"export_details[%i].name",
|
|
exp_sz);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
exp_sz++;
|
|
}
|
|
|
|
yr_set_integer(exp_sz, pe->object, "number_of_exports");
|
|
}
|
|
|
|
// BoringSSL (https://boringssl.googlesource.com/boringssl/) doesn't support
|
|
// some features used in pe_parse_certificates, if you are using BoringSSL
|
|
// instead of OpenSSL you should define BORINGSSL for YARA to compile properly,
|
|
// but you won't have signature-related features in the PE module.
|
|
#if defined(HAVE_LIBCRYPTO) && !defined(BORINGSSL)
|
|
|
|
#define write_certificate(cert, pe, fmt, ...) \
|
|
do \
|
|
{ \
|
|
char thumbprint_ascii[YR_SHA1_LEN * 2 + 1]; \
|
|
for (int j = 0; j < cert->sha1.len; ++j) \
|
|
sprintf(thumbprint_ascii + (j * 2), "%02x", cert->sha1.data[j]); \
|
|
\
|
|
yr_set_string( \
|
|
(char*) thumbprint_ascii, pe->object, fmt ".thumbprint", __VA_ARGS__); \
|
|
\
|
|
yr_set_string(cert->issuer, pe->object, fmt ".issuer", __VA_ARGS__); \
|
|
yr_set_string(cert->subject, pe->object, fmt ".subject", __VA_ARGS__); \
|
|
/* Versions are zero based, so add one. */ \
|
|
yr_set_integer( \
|
|
cert->version + 1, pe->object, fmt ".version", __VA_ARGS__); \
|
|
yr_set_string(cert->sig_alg, pe->object, fmt ".algorithm", __VA_ARGS__); \
|
|
yr_set_string( \
|
|
cert->sig_alg_oid, pe->object, fmt ".algorithm_oid", __VA_ARGS__); \
|
|
yr_set_string(cert->serial, pe->object, fmt ".serial", __VA_ARGS__); \
|
|
yr_set_integer( \
|
|
cert->not_before, pe->object, fmt ".not_before", __VA_ARGS__); \
|
|
yr_set_integer( \
|
|
cert->not_after, pe->object, fmt ".not_after", __VA_ARGS__); \
|
|
} while (0)
|
|
|
|
void _process_authenticode(
|
|
PE* pe,
|
|
AuthenticodeArray* auth_array,
|
|
int* sig_count)
|
|
{
|
|
if (!auth_array || !auth_array->count)
|
|
return;
|
|
|
|
bool signature_valid = false;
|
|
|
|
for (size_t i = 0; i < auth_array->count; ++i)
|
|
{
|
|
const Authenticode* authenticode = auth_array->signatures[i];
|
|
bool verified = authenticode->verify_flags == AUTHENTICODE_VFY_VALID;
|
|
|
|
/* If any signature is valid -> file is correctly signed */
|
|
signature_valid |= verified;
|
|
|
|
yr_set_integer(verified, pe->object, "signatures[%i].verified", *sig_count);
|
|
|
|
yr_set_string(
|
|
authenticode->digest_alg,
|
|
pe->object,
|
|
"signatures[%i].digest_alg",
|
|
*sig_count);
|
|
|
|
if (authenticode->digest.data)
|
|
{
|
|
char* digest_ascii = yr_malloc(authenticode->digest.len * 2 + 1);
|
|
for (int j = 0; j < authenticode->digest.len; ++j)
|
|
sprintf(digest_ascii + (j * 2), "%02x", authenticode->digest.data[j]);
|
|
|
|
yr_set_string(
|
|
digest_ascii, pe->object, "signatures[%i].digest", *sig_count);
|
|
yr_free(digest_ascii);
|
|
}
|
|
|
|
if (authenticode->file_digest.data)
|
|
{
|
|
char* digest_ascii = yr_malloc(authenticode->file_digest.len * 2 + 1);
|
|
for (int j = 0; j < authenticode->file_digest.len; ++j)
|
|
sprintf(
|
|
digest_ascii + (j * 2), "%02x", authenticode->file_digest.data[j]);
|
|
|
|
yr_set_string(
|
|
digest_ascii, pe->object, "signatures[%i].file_digest", *sig_count);
|
|
yr_free(digest_ascii);
|
|
}
|
|
|
|
yr_set_integer(
|
|
authenticode->certs ? authenticode->certs->count : 0,
|
|
pe->object,
|
|
"signatures[%i].number_of_certificates",
|
|
*sig_count);
|
|
|
|
if (authenticode->certs)
|
|
{
|
|
for (int k = 0; k < authenticode->certs->count; ++k)
|
|
{
|
|
write_certificate(
|
|
authenticode->certs->certs[k],
|
|
pe,
|
|
"signatures[%i].certificates[%i]",
|
|
*sig_count,
|
|
k);
|
|
}
|
|
}
|
|
|
|
const Signer* signer = authenticode->signer;
|
|
if (signer)
|
|
{
|
|
/* For compatibility with previous YARA rules, write information
|
|
* about signing certificate in the same way */
|
|
if (signer->chain && signer->chain->count >= 1)
|
|
{
|
|
const Certificate* sign_cert = signer->chain->certs[0];
|
|
write_certificate(sign_cert, pe, "signatures[%i]", *sig_count);
|
|
}
|
|
|
|
yr_set_string(
|
|
signer->program_name,
|
|
pe->object,
|
|
"signatures[%i].signer_info.program_name",
|
|
*sig_count);
|
|
yr_set_string(
|
|
signer->digest_alg,
|
|
pe->object,
|
|
"signatures[%i].signer_info.digest_alg",
|
|
*sig_count);
|
|
|
|
if (signer->digest.data)
|
|
{
|
|
char* digest_ascii = yr_malloc(signer->digest.len * 2 + 1);
|
|
for (int j = 0; j < signer->digest.len; ++j)
|
|
sprintf(digest_ascii + (j * 2), "%02x", signer->digest.data[j]);
|
|
|
|
yr_set_string(
|
|
digest_ascii,
|
|
pe->object,
|
|
"signatures[%i].signer_info.digest",
|
|
*sig_count);
|
|
yr_free(digest_ascii);
|
|
}
|
|
|
|
yr_set_integer(
|
|
signer->chain ? signer->chain->count : 0,
|
|
pe->object,
|
|
"signatures[%i].signer_info.length_of_chain",
|
|
*sig_count);
|
|
|
|
if (signer->chain)
|
|
{
|
|
for (int k = 0; k < signer->chain->count; ++k)
|
|
{
|
|
write_certificate(
|
|
signer->chain->certs[k],
|
|
pe,
|
|
"signatures[%i].signer_info.chain[%i]",
|
|
*sig_count,
|
|
k);
|
|
}
|
|
}
|
|
}
|
|
|
|
yr_set_integer(
|
|
authenticode->countersigs ? authenticode->countersigs->count : 0,
|
|
pe->object,
|
|
"signatures[%i].number_of_countersignatures",
|
|
*sig_count);
|
|
|
|
if (authenticode->countersigs)
|
|
{
|
|
for (int j = 0; j < authenticode->countersigs->count; ++j)
|
|
{
|
|
const Countersignature* counter =
|
|
authenticode->countersigs->counters[j];
|
|
|
|
yr_set_integer(
|
|
counter->verify_flags == COUNTERSIGNATURE_VFY_VALID,
|
|
pe->object,
|
|
"signatures[%i].countersignatures[%i].verified",
|
|
*sig_count,
|
|
j);
|
|
yr_set_string(
|
|
counter->digest_alg,
|
|
pe->object,
|
|
"signatures[%i].countersignatures[%i].digest_alg",
|
|
*sig_count,
|
|
j);
|
|
yr_set_integer(
|
|
counter->sign_time,
|
|
pe->object,
|
|
"signatures[%i].countersignatures[%i].sign_time",
|
|
*sig_count,
|
|
j);
|
|
|
|
if (counter->digest.data)
|
|
{
|
|
char* digest_ascii = yr_malloc(counter->digest.len * 2 + 1);
|
|
for (int j = 0; j < counter->digest.len; ++j)
|
|
sprintf(digest_ascii + (j * 2), "%02x", counter->digest.data[j]);
|
|
|
|
yr_set_string(
|
|
digest_ascii,
|
|
pe->object,
|
|
"signatures[%i].countersignatures[%i].digest",
|
|
*sig_count,
|
|
j);
|
|
yr_free(digest_ascii);
|
|
}
|
|
|
|
yr_set_integer(
|
|
counter->chain ? counter->chain->count : 0,
|
|
pe->object,
|
|
"signatures[%i].countersignatures[%i].length_of_chain",
|
|
*sig_count,
|
|
j);
|
|
|
|
if (counter->chain)
|
|
{
|
|
for (int k = 0; k < counter->chain->count; ++k)
|
|
{
|
|
write_certificate(
|
|
counter->chain->certs[k],
|
|
pe,
|
|
"signatures[%i].countersignatures[%i].chain[%i]",
|
|
*sig_count,
|
|
j,
|
|
k);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
(*sig_count)++;
|
|
}
|
|
|
|
yr_set_integer(signature_valid, pe->object, "is_signed");
|
|
}
|
|
|
|
static void pe_parse_certificates(PE* pe)
|
|
{
|
|
int counter = 0;
|
|
|
|
// Default to 0 signatures until we know otherwise.
|
|
yr_set_integer(0, pe->object, "number_of_signatures");
|
|
// Default to not signed until we know otherwise.
|
|
yr_set_integer(0, pe->object, "is_signed");
|
|
|
|
PIMAGE_DATA_DIRECTORY directory = pe_get_directory_entry(
|
|
pe, IMAGE_DIRECTORY_ENTRY_SECURITY);
|
|
|
|
if (directory == NULL)
|
|
return;
|
|
|
|
// directory->VirtualAddress is a file offset. Don't call pe_rva_to_offset().
|
|
if (yr_le32toh(directory->VirtualAddress) == 0 ||
|
|
yr_le32toh(directory->VirtualAddress) > pe->data_size ||
|
|
yr_le32toh(directory->Size) > pe->data_size ||
|
|
yr_le32toh(directory->VirtualAddress) + yr_le32toh(directory->Size) >
|
|
pe->data_size)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AuthenticodeArray* auth_array = parse_authenticode(pe->data, pe->data_size);
|
|
_process_authenticode(pe, auth_array, &counter);
|
|
authenticode_array_free(auth_array);
|
|
|
|
yr_set_integer(counter, pe->object, "number_of_signatures");
|
|
}
|
|
|
|
#endif // defined(HAVE_LIBCRYPTO)
|
|
|
|
const char* pe_get_section_full_name(
|
|
PE* pe,
|
|
const char* section_name,
|
|
uint64_t section_name_length,
|
|
uint64_t* section_full_name_length)
|
|
{
|
|
// section_name is an 8-byte, null-padded UTF-8 encoded string. If the string
|
|
// is exactly 8 characters long, there is no terminating null. For longer
|
|
// names, this field contains a slash (/) that is followed by an ASCII
|
|
// representation of a decimal number that is an offset into the string table.
|
|
|
|
// Sample: 2e9c671b8a0411f2b397544b368c44d7f095eb395779de0ad1ac946914dfa34c
|
|
|
|
// Check if any param is NULL
|
|
if (pe == NULL || section_name == NULL || section_full_name_length == NULL)
|
|
return NULL;
|
|
|
|
// Set length to zero
|
|
*section_full_name_length = 0;
|
|
|
|
// Offset and number of records in coff table
|
|
uint64_t coff_offset = yr_le32toh(
|
|
pe->header->FileHeader.PointerToSymbolTable);
|
|
uint64_t coff_number = yr_le32toh(pe->header->FileHeader.NumberOfSymbols);
|
|
|
|
// If section name start with '/' and file contain coff table then section
|
|
// name is stored in string table
|
|
if (coff_offset == 0 || section_name[0] != '/')
|
|
{
|
|
*section_full_name_length = section_name_length;
|
|
return section_name;
|
|
}
|
|
|
|
// Calculate offset of string table (String table is immediately after coff
|
|
// table)
|
|
uint64_t string_offset = coff_offset + coff_number * sizeof(IMAGE_SYMBOL);
|
|
uint64_t string_index = 0;
|
|
|
|
// Calculate string index/offset in string table
|
|
for (int i = 1; i < IMAGE_SIZEOF_SHORT_NAME && isdigit(section_name[i]); i++)
|
|
string_index = (string_index * 10) + (section_name[i] - '0');
|
|
|
|
// Calculate string pointer
|
|
const char* string = (char*) (pe->data + string_offset + string_index);
|
|
|
|
// Check string
|
|
for (uint64_t len = 0; fits_in_pe(pe, string, len + 1); len++)
|
|
{
|
|
// Prevent sign extension to 32-bits on bytes > 0x7F
|
|
// The result negative integer would cause assert in MSVC debug version of
|
|
// isprint()
|
|
unsigned int one_char = (unsigned char) (string[len]);
|
|
|
|
// Valid string
|
|
if (one_char == 0)
|
|
{
|
|
*section_full_name_length = len;
|
|
return string;
|
|
}
|
|
|
|
// string contain unprintable character
|
|
if (!isprint(one_char))
|
|
return NULL;
|
|
}
|
|
|
|
// String do not fit into pe file
|
|
return NULL;
|
|
}
|
|
|
|
static void pe_parse_header(PE* pe, uint64_t base_address, int flags)
|
|
{
|
|
PIMAGE_SECTION_HEADER section;
|
|
PIMAGE_DATA_DIRECTORY data_dir;
|
|
|
|
char section_name[IMAGE_SIZEOF_SHORT_NAME + 1];
|
|
int sect_name_length;
|
|
|
|
uint16_t scount;
|
|
uint32_t ddcount;
|
|
|
|
uint64_t highest_sec_siz = 0;
|
|
uint64_t highest_sec_ofs = 0;
|
|
uint64_t section_end;
|
|
uint64_t last_section_end;
|
|
|
|
yr_set_integer(1, pe->object, "is_pe");
|
|
|
|
yr_set_integer(
|
|
yr_le16toh(pe->header->FileHeader.Machine), pe->object, "machine");
|
|
|
|
yr_set_integer(
|
|
yr_le16toh(pe->header->FileHeader.NumberOfSections),
|
|
pe->object,
|
|
"number_of_sections");
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(pe->header->FileHeader.TimeDateStamp),
|
|
pe->object,
|
|
"timestamp");
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(pe->header->FileHeader.PointerToSymbolTable),
|
|
pe->object,
|
|
"pointer_to_symbol_table");
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(pe->header->FileHeader.NumberOfSymbols),
|
|
pe->object,
|
|
"number_of_symbols");
|
|
|
|
yr_set_integer(
|
|
yr_le16toh(pe->header->FileHeader.SizeOfOptionalHeader),
|
|
pe->object,
|
|
"size_of_optional_header");
|
|
|
|
yr_set_integer(
|
|
yr_le16toh(pe->header->FileHeader.Characteristics),
|
|
pe->object,
|
|
"characteristics");
|
|
|
|
yr_set_integer(
|
|
flags & SCAN_FLAGS_PROCESS_MEMORY
|
|
? base_address + yr_le32toh(OptionalHeader(pe, AddressOfEntryPoint))
|
|
: pe_rva_to_offset(
|
|
pe, yr_le32toh(OptionalHeader(pe, AddressOfEntryPoint))),
|
|
pe->object,
|
|
"entry_point");
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(OptionalHeader(pe, AddressOfEntryPoint)),
|
|
pe->object,
|
|
"entry_point_raw");
|
|
|
|
yr_set_integer(
|
|
IS_64BITS_PE(pe) ? yr_le64toh(OptionalHeader(pe, ImageBase))
|
|
: yr_le32toh(OptionalHeader(pe, ImageBase)),
|
|
pe->object,
|
|
"image_base");
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(OptionalHeader(pe, NumberOfRvaAndSizes)),
|
|
pe->object,
|
|
"number_of_rva_and_sizes");
|
|
|
|
yr_set_integer(
|
|
yr_le16toh(OptionalHeader(pe, Magic)), pe->object, "opthdr_magic");
|
|
|
|
yr_set_integer(
|
|
OptionalHeader(pe, MajorLinkerVersion),
|
|
pe->object,
|
|
"linker_version.major");
|
|
|
|
yr_set_integer(
|
|
OptionalHeader(pe, MinorLinkerVersion),
|
|
pe->object,
|
|
"linker_version.minor");
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(OptionalHeader(pe, SizeOfCode)), pe->object, "size_of_code");
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(OptionalHeader(pe, SizeOfInitializedData)),
|
|
pe->object,
|
|
"size_of_initialized_data");
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(OptionalHeader(pe, SizeOfUninitializedData)),
|
|
pe->object,
|
|
"size_of_uninitialized_data");
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(OptionalHeader(pe, BaseOfCode)), pe->object, "base_of_code");
|
|
|
|
if (!IS_64BITS_PE(pe))
|
|
{
|
|
yr_set_integer(
|
|
yr_le32toh(pe->header->OptionalHeader.BaseOfData),
|
|
pe->object,
|
|
"base_of_data");
|
|
}
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(OptionalHeader(pe, SectionAlignment)),
|
|
pe->object,
|
|
"section_alignment");
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(OptionalHeader(pe, FileAlignment)),
|
|
pe->object,
|
|
"file_alignment");
|
|
|
|
yr_set_integer(
|
|
yr_le16toh(OptionalHeader(pe, MajorOperatingSystemVersion)),
|
|
pe->object,
|
|
"os_version.major");
|
|
|
|
yr_set_integer(
|
|
yr_le16toh(OptionalHeader(pe, MinorOperatingSystemVersion)),
|
|
pe->object,
|
|
"os_version.minor");
|
|
|
|
yr_set_integer(
|
|
yr_le16toh(OptionalHeader(pe, MajorImageVersion)),
|
|
pe->object,
|
|
"image_version.major");
|
|
|
|
yr_set_integer(
|
|
yr_le16toh(OptionalHeader(pe, MinorImageVersion)),
|
|
pe->object,
|
|
"image_version.minor");
|
|
|
|
yr_set_integer(
|
|
yr_le16toh(OptionalHeader(pe, MajorSubsystemVersion)),
|
|
pe->object,
|
|
"subsystem_version.major");
|
|
|
|
yr_set_integer(
|
|
yr_le16toh(OptionalHeader(pe, MinorSubsystemVersion)),
|
|
pe->object,
|
|
"subsystem_version.minor");
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(OptionalHeader(pe, Win32VersionValue)),
|
|
pe->object,
|
|
"win32_version_value");
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(OptionalHeader(pe, SizeOfImage)), pe->object, "size_of_image");
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(OptionalHeader(pe, SizeOfHeaders)),
|
|
pe->object,
|
|
"size_of_headers");
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(OptionalHeader(pe, CheckSum)), pe->object, "checksum");
|
|
|
|
yr_set_integer(
|
|
yr_le16toh(OptionalHeader(pe, Subsystem)), pe->object, "subsystem");
|
|
|
|
yr_set_integer(
|
|
yr_le16toh(OptionalHeader(pe, DllCharacteristics)),
|
|
pe->object,
|
|
"dll_characteristics");
|
|
|
|
yr_set_integer(
|
|
IS_64BITS_PE(pe) ? yr_le64toh(OptionalHeader(pe, SizeOfStackReserve))
|
|
: yr_le32toh(OptionalHeader(pe, SizeOfStackReserve)),
|
|
pe->object,
|
|
"size_of_stack_reserve");
|
|
|
|
yr_set_integer(
|
|
IS_64BITS_PE(pe) ? yr_le64toh(OptionalHeader(pe, SizeOfStackCommit))
|
|
: yr_le32toh(OptionalHeader(pe, SizeOfStackCommit)),
|
|
pe->object,
|
|
"size_of_stack_commit");
|
|
|
|
yr_set_integer(
|
|
IS_64BITS_PE(pe) ? yr_le64toh(OptionalHeader(pe, SizeOfHeapReserve))
|
|
: yr_le32toh(OptionalHeader(pe, SizeOfHeapReserve)),
|
|
pe->object,
|
|
"size_of_heap_reserve");
|
|
|
|
yr_set_integer(
|
|
IS_64BITS_PE(pe) ? yr_le64toh(OptionalHeader(pe, SizeOfHeapCommit))
|
|
: yr_le32toh(OptionalHeader(pe, SizeOfHeapCommit)),
|
|
pe->object,
|
|
"size_of_heap_commit");
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(OptionalHeader(pe, LoaderFlags)), pe->object, "loader_flags");
|
|
|
|
data_dir = IS_64BITS_PE(pe) ? pe->header64->OptionalHeader.DataDirectory
|
|
: pe->header->OptionalHeader.DataDirectory;
|
|
|
|
ddcount = yr_le32toh(OptionalHeader(pe, NumberOfRvaAndSizes));
|
|
ddcount = yr_min(ddcount, IMAGE_NUMBEROF_DIRECTORY_ENTRIES);
|
|
|
|
for (int i = 0; i < ddcount; i++)
|
|
{
|
|
if (!struct_fits_in_pe(pe, data_dir, IMAGE_DATA_DIRECTORY))
|
|
break;
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(data_dir->VirtualAddress),
|
|
pe->object,
|
|
"data_directories[%i].virtual_address",
|
|
i);
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(data_dir->Size), pe->object, "data_directories[%i].size", i);
|
|
|
|
data_dir++;
|
|
}
|
|
|
|
pe_iterate_resources(
|
|
pe, (RESOURCE_CALLBACK_FUNC) pe_collect_resources, (void*) pe);
|
|
|
|
yr_set_integer(pe->resources, pe->object, "number_of_resources");
|
|
yr_set_integer(pe->version_infos, pe->object, "number_of_version_infos");
|
|
|
|
section = IMAGE_FIRST_SECTION(pe->header);
|
|
|
|
scount = yr_min(
|
|
yr_le16toh(pe->header->FileHeader.NumberOfSections), MAX_PE_SECTIONS);
|
|
|
|
for (int i = 0; i < scount; i++)
|
|
{
|
|
if (!struct_fits_in_pe(pe, section, IMAGE_SECTION_HEADER))
|
|
break;
|
|
|
|
memcpy(section_name, section->Name, IMAGE_SIZEOF_SHORT_NAME);
|
|
section_name[IMAGE_SIZEOF_SHORT_NAME] = '\0';
|
|
|
|
// Basically do rstrip('\0'), find the rightmost non-null character.
|
|
// Samples like
|
|
// 0043812838495a45449a0ac61a81b9c16eddca1ad249fb4f7fdb1c4505e9bb34 contain
|
|
// sections with additional characters after the first null.
|
|
for (sect_name_length = IMAGE_SIZEOF_SHORT_NAME - 1; sect_name_length >= 0;
|
|
--sect_name_length)
|
|
{
|
|
if (section_name[sect_name_length] != '\0')
|
|
break;
|
|
}
|
|
|
|
uint64_t sect_full_name_length = 0;
|
|
const char* full_section_name = pe_get_section_full_name(
|
|
pe, section_name, sect_name_length + 1, §_full_name_length);
|
|
|
|
yr_set_sized_string(
|
|
(char*) section_name,
|
|
sect_name_length + 1,
|
|
pe->object,
|
|
"sections[%i].name",
|
|
i);
|
|
|
|
yr_set_sized_string(
|
|
full_section_name,
|
|
sect_full_name_length,
|
|
pe->object,
|
|
"sections[%i].full_name",
|
|
i);
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(section->Characteristics),
|
|
pe->object,
|
|
"sections[%i].characteristics",
|
|
i);
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(section->SizeOfRawData),
|
|
pe->object,
|
|
"sections[%i].raw_data_size",
|
|
i);
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(section->PointerToRawData),
|
|
pe->object,
|
|
"sections[%i].raw_data_offset",
|
|
i);
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(section->VirtualAddress),
|
|
pe->object,
|
|
"sections[%i].virtual_address",
|
|
i);
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(section->Misc.VirtualSize),
|
|
pe->object,
|
|
"sections[%i].virtual_size",
|
|
i);
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(section->PointerToRelocations),
|
|
pe->object,
|
|
"sections[%i].pointer_to_relocations",
|
|
i);
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(section->PointerToLinenumbers),
|
|
pe->object,
|
|
"sections[%i].pointer_to_line_numbers",
|
|
i);
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(section->NumberOfRelocations),
|
|
pe->object,
|
|
"sections[%i].number_of_relocations",
|
|
i);
|
|
|
|
yr_set_integer(
|
|
yr_le32toh(section->NumberOfLinenumbers),
|
|
pe->object,
|
|
"sections[%i].number_of_line_numbers",
|
|
i);
|
|
|
|
// This will catch the section with the highest raw offset to help checking
|
|
// if overlay data is present. If two sections have the same raw pointer
|
|
// but different raw sizes the largest one is used. An example of this case
|
|
// is file: cf62bf1815a93e68e6c5189f689286b66c4088b9507cf3ecf835e4ac3f9ededa
|
|
|
|
section_end = yr_le32toh(section->PointerToRawData) +
|
|
yr_le32toh(section->SizeOfRawData);
|
|
|
|
if (section_end > highest_sec_ofs + highest_sec_siz)
|
|
{
|
|
highest_sec_ofs = yr_le32toh(section->PointerToRawData);
|
|
highest_sec_siz = yr_le32toh(section->SizeOfRawData);
|
|
}
|
|
|
|
section++;
|
|
}
|
|
|
|
// An overlay is data appended to a PE file. Its location is at
|
|
// RawData + RawOffset of the last section on the physical file
|
|
last_section_end = highest_sec_siz + highest_sec_ofs;
|
|
|
|
// For PE files that have overlaid data overlay.offset contains the offset
|
|
// within the file where the overlay starts and overlay.size contains the
|
|
// size. If the PE file doesn't have an overlay both fields are 0, if the
|
|
// file is not a PE file (or is a malformed PE) both fields are YR_UNDEFINED.
|
|
if (last_section_end && (pe->data_size > last_section_end))
|
|
{
|
|
yr_set_integer(last_section_end, pe->object, "overlay.offset");
|
|
yr_set_integer(
|
|
pe->data_size - last_section_end, pe->object, "overlay.size");
|
|
}
|
|
else
|
|
{
|
|
yr_set_integer(0, pe->object, "overlay.offset");
|
|
yr_set_integer(0, pe->object, "overlay.size");
|
|
}
|
|
}
|
|
|
|
//
|
|
// Given a posix timestamp argument, make sure not_before <= arg <= not_after
|
|
//
|
|
|
|
define_function(valid_on)
|
|
{
|
|
int64_t timestamp;
|
|
int64_t not_before;
|
|
int64_t not_after;
|
|
|
|
if (yr_is_undefined(yr_parent(), "not_before") ||
|
|
yr_is_undefined(yr_parent(), "not_after"))
|
|
{
|
|
return_integer(YR_UNDEFINED);
|
|
}
|
|
|
|
timestamp = integer_argument(1);
|
|
|
|
not_before = yr_get_integer(yr_parent(), "not_before");
|
|
not_after = yr_get_integer(yr_parent(), "not_after");
|
|
|
|
return_integer(timestamp >= not_before && timestamp <= not_after);
|
|
}
|
|
|
|
define_function(section_index_addr)
|
|
{
|
|
YR_OBJECT* module = yr_module();
|
|
YR_SCAN_CONTEXT* context = yr_scan_context();
|
|
|
|
int64_t offset;
|
|
int64_t size;
|
|
|
|
int64_t addr = integer_argument(1);
|
|
int64_t n = yr_get_integer(module, "number_of_sections");
|
|
|
|
if (yr_is_undefined(module, "number_of_sections"))
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
for (int i = 0; i < yr_min(n, MAX_PE_SECTIONS); i++)
|
|
{
|
|
if (context->flags & SCAN_FLAGS_PROCESS_MEMORY)
|
|
{
|
|
offset = yr_get_integer(module, "sections[%i].virtual_address", i);
|
|
size = yr_get_integer(module, "sections[%i].virtual_size", i);
|
|
}
|
|
else
|
|
{
|
|
offset = yr_get_integer(module, "sections[%i].raw_data_offset", i);
|
|
size = yr_get_integer(module, "sections[%i].raw_data_size", i);
|
|
}
|
|
|
|
if (addr >= offset && addr < offset + size)
|
|
return_integer(i);
|
|
}
|
|
|
|
return_integer(YR_UNDEFINED);
|
|
}
|
|
|
|
define_function(section_index_name)
|
|
{
|
|
YR_OBJECT* module = yr_module();
|
|
|
|
char* name = string_argument(1);
|
|
|
|
int64_t n = yr_get_integer(module, "number_of_sections");
|
|
|
|
if (yr_is_undefined(module, "number_of_sections"))
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
for (int i = 0; i < yr_min(n, MAX_PE_SECTIONS); i++)
|
|
{
|
|
SIZED_STRING* sect = yr_get_string(module, "sections[%i].name", i);
|
|
|
|
if (sect != NULL && strcmp(name, sect->c_string) == 0)
|
|
return_integer(i);
|
|
}
|
|
|
|
return_integer(YR_UNDEFINED);
|
|
}
|
|
|
|
define_function(exports)
|
|
{
|
|
SIZED_STRING* search_name = sized_string_argument(1);
|
|
|
|
SIZED_STRING* function_name = NULL;
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
// If not a PE, return YR_UNDEFINED.
|
|
if (pe == NULL)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
// If PE, but no exported functions, return false.
|
|
int n = (int) yr_get_integer(module, "number_of_exports");
|
|
|
|
if (n == 0)
|
|
return_integer(0);
|
|
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
function_name = yr_get_string(module, "export_details[%i].name", i);
|
|
|
|
if (function_name == NULL)
|
|
continue;
|
|
|
|
if (ss_icompare(function_name, search_name) == 0)
|
|
return_integer(1);
|
|
}
|
|
|
|
return_integer(0);
|
|
}
|
|
|
|
define_function(exports_regexp)
|
|
{
|
|
RE* regex = regexp_argument(1);
|
|
|
|
SIZED_STRING* function_name = NULL;
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
// If not a PE, return YR_UNDEFINED.
|
|
if (pe == NULL)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
// If PE, but no exported functions, return false.
|
|
int n = (int) yr_get_integer(module, "number_of_exports");
|
|
|
|
if (n == 0)
|
|
return_integer(0);
|
|
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
function_name = yr_get_string(module, "export_details[%i].name", i);
|
|
if (function_name == NULL)
|
|
continue;
|
|
|
|
if (yr_re_match(yr_scan_context(), regex, function_name->c_string) != -1)
|
|
return_integer(1);
|
|
}
|
|
|
|
return_integer(0);
|
|
}
|
|
|
|
define_function(exports_ordinal)
|
|
{
|
|
int64_t ordinal = integer_argument(1);
|
|
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
// If not a PE, return YR_UNDEFINED.
|
|
if (pe == NULL)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
// If PE, but no exported functions, return false.
|
|
int n = (int) yr_get_integer(module, "number_of_exports");
|
|
|
|
if (n == 0)
|
|
return_integer(0);
|
|
|
|
if (ordinal == 0 || ordinal > n)
|
|
return_integer(0);
|
|
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
int64_t exported_ordinal = yr_object_get_integer(
|
|
module, "export_details[%i].ordinal", i);
|
|
|
|
if (exported_ordinal == ordinal)
|
|
return_integer(1);
|
|
}
|
|
|
|
return_integer(0);
|
|
}
|
|
|
|
define_function(exports_index_name)
|
|
{
|
|
SIZED_STRING* search_name = sized_string_argument(1);
|
|
|
|
SIZED_STRING* function_name = NULL;
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
// If not a PE, return YR_UNDEFINED.
|
|
if (pe == NULL)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
// If PE, but no exported functions, return false.
|
|
int n = (int) yr_get_integer(module, "number_of_exports");
|
|
|
|
if (n == 0)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
function_name = yr_get_string(module, "export_details[%i].name", i);
|
|
|
|
if (function_name == NULL)
|
|
continue;
|
|
|
|
if (ss_icompare(function_name, search_name) == 0)
|
|
return_integer(i);
|
|
}
|
|
|
|
return_integer(YR_UNDEFINED);
|
|
}
|
|
|
|
define_function(exports_index_ordinal)
|
|
{
|
|
int64_t ordinal = integer_argument(1);
|
|
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
// If not a PE, return YR_UNDEFINED.
|
|
if (pe == NULL)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
// If PE, but no exported functions, return false.
|
|
int n = (int) yr_get_integer(module, "number_of_exports");
|
|
|
|
if (n == 0)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
if (ordinal == 0 || ordinal > n)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
int64_t exported_ordinal = yr_object_get_integer(
|
|
module, "export_details[%i].ordinal", i);
|
|
|
|
if (exported_ordinal == ordinal)
|
|
return_integer(i);
|
|
}
|
|
|
|
return_integer(YR_UNDEFINED);
|
|
}
|
|
|
|
define_function(exports_index_regex)
|
|
{
|
|
RE* regex = regexp_argument(1);
|
|
|
|
SIZED_STRING* function_name = NULL;
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
// If not a PE, return YR_UNDEFINED.
|
|
if (pe == NULL)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
// If PE, but no exported functions, return false.
|
|
int n = (int) yr_get_integer(module, "number_of_exports");
|
|
|
|
if (n == 0)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
function_name = yr_get_string(module, "export_details[%i].name", i);
|
|
if (function_name == NULL)
|
|
continue;
|
|
|
|
if (yr_re_match(yr_scan_context(), regex, function_name->c_string) != -1)
|
|
{
|
|
return_integer(i);
|
|
}
|
|
}
|
|
|
|
return_integer(YR_UNDEFINED);
|
|
}
|
|
|
|
#if defined(HAVE_LIBCRYPTO) || defined(HAVE_WINCRYPT_H) || \
|
|
defined(HAVE_COMMONCRYPTO_COMMONCRYPTO_H)
|
|
|
|
//
|
|
// Generate an import hash:
|
|
// https://www.mandiant.com/blog/tracking-malware-import-hashing/
|
|
// It is important to make duplicates of the strings as we don't want
|
|
// to alter the contents of the parsed import structures.
|
|
//
|
|
|
|
define_function(imphash)
|
|
{
|
|
YR_OBJECT* module = yr_module();
|
|
|
|
IMPORTED_DLL* dll;
|
|
yr_md5_ctx ctx;
|
|
|
|
unsigned char digest[YR_MD5_LEN];
|
|
char* digest_ascii;
|
|
|
|
size_t i;
|
|
bool first = true;
|
|
|
|
PE* pe = (PE*) module->data;
|
|
|
|
// If not a PE, return YR_UNDEFINED.
|
|
|
|
if (!pe)
|
|
return_string(YR_UNDEFINED);
|
|
|
|
// Lookup in cache first.
|
|
digest_ascii = (char*) yr_hash_table_lookup(pe->hash_table, "imphash", NULL);
|
|
|
|
if (digest_ascii != NULL)
|
|
return_string(digest_ascii);
|
|
|
|
yr_md5_init(&ctx);
|
|
|
|
dll = pe->imported_dlls;
|
|
|
|
while (dll)
|
|
{
|
|
IMPORT_FUNCTION* func;
|
|
|
|
size_t dll_name_len;
|
|
char* dll_name;
|
|
|
|
// If extension is 'ocx', 'sys' or 'dll', chop it.
|
|
|
|
char* ext = strrchr(dll->name, '.');
|
|
|
|
if (ext &&
|
|
(strncasecmp(ext, ".ocx", 4) == 0 || strncasecmp(ext, ".sys", 4) == 0 ||
|
|
strncasecmp(ext, ".dll", 4) == 0))
|
|
{
|
|
dll_name_len = (ext - dll->name);
|
|
}
|
|
else
|
|
{
|
|
dll_name_len = strlen(dll->name);
|
|
}
|
|
|
|
// Allocate a new string to hold the dll name.
|
|
|
|
dll_name = (char*) yr_malloc(dll_name_len + 1);
|
|
|
|
if (!dll_name)
|
|
return ERROR_INSUFFICIENT_MEMORY;
|
|
|
|
strlcpy(dll_name, dll->name, dll_name_len + 1);
|
|
|
|
func = dll->functions;
|
|
|
|
while (func)
|
|
{
|
|
char* final_name;
|
|
size_t final_name_len = dll_name_len + strlen(func->name) + 1;
|
|
|
|
if (!first)
|
|
final_name_len++; // Additional byte to accommodate the extra comma
|
|
|
|
final_name = (char*) yr_malloc(final_name_len + 1);
|
|
|
|
if (final_name == NULL)
|
|
break;
|
|
|
|
sprintf(final_name, first ? "%s.%s" : ",%s.%s", dll_name, func->name);
|
|
|
|
// Lowercase the whole thing.
|
|
|
|
for (i = 0; i < final_name_len; i++)
|
|
final_name[i] = tolower(final_name[i]);
|
|
|
|
yr_md5_update(&ctx, final_name, final_name_len);
|
|
|
|
yr_free(final_name);
|
|
|
|
func = func->next;
|
|
first = false;
|
|
}
|
|
|
|
yr_free(dll_name);
|
|
|
|
dll = dll->next;
|
|
}
|
|
|
|
yr_md5_final(digest, &ctx);
|
|
|
|
digest_ascii = (char*) yr_malloc(YR_MD5_LEN * 2 + 1);
|
|
|
|
if (digest_ascii == NULL)
|
|
return ERROR_INSUFFICIENT_MEMORY;
|
|
|
|
// Transform the binary digest to ascii
|
|
|
|
for (i = 0; i < YR_MD5_LEN; i++)
|
|
{
|
|
sprintf(digest_ascii + (i * 2), "%02x", digest[i]);
|
|
}
|
|
|
|
digest_ascii[YR_MD5_LEN * 2] = '\0';
|
|
|
|
yr_hash_table_add(pe->hash_table, "imphash", NULL, digest_ascii);
|
|
|
|
return_string(digest_ascii);
|
|
}
|
|
|
|
#endif // defined(HAVE_LIBCRYPTO) || defined(HAVE_WINCRYPT_H)
|
|
|
|
int64_t pe_imports_dll(IMPORTED_DLL* dll, char* dll_name)
|
|
{
|
|
if (dll == NULL)
|
|
return 0;
|
|
|
|
int64_t result = 0;
|
|
|
|
for (; dll != NULL; dll = dll->next)
|
|
{
|
|
if (strcasecmp(dll->name, dll_name) == 0)
|
|
{
|
|
IMPORT_FUNCTION* fun = dll->functions;
|
|
for (; fun != NULL; fun = fun->next)
|
|
{
|
|
result++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int64_t pe_imports(IMPORTED_DLL* dll, char* dll_name, char* fun_name)
|
|
{
|
|
if (dll == NULL)
|
|
return 0;
|
|
|
|
for (; dll != NULL; dll = dll->next)
|
|
{
|
|
if (strcasecmp(dll->name, dll_name) == 0)
|
|
{
|
|
IMPORT_FUNCTION* fun = dll->functions;
|
|
for (; fun != NULL; fun = fun->next)
|
|
{
|
|
if (strcasecmp(fun->name, fun_name) == 0)
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int64_t pe_imports_regexp(
|
|
YR_SCAN_CONTEXT* context,
|
|
IMPORTED_DLL* dll,
|
|
RE* dll_name,
|
|
RE* fun_name)
|
|
{
|
|
if (dll == NULL)
|
|
return 0;
|
|
|
|
int64_t result = 0;
|
|
|
|
for (; dll != NULL; dll = dll->next)
|
|
{
|
|
if (yr_re_match(context, dll_name, dll->name) > 0)
|
|
{
|
|
IMPORT_FUNCTION* fun = dll->functions;
|
|
for (; fun != NULL; fun = fun->next)
|
|
{
|
|
if (yr_re_match(context, fun_name, fun->name) > 0)
|
|
result++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int64_t pe_imports_ordinal(IMPORTED_DLL* dll, char* dll_name, uint64_t ordinal)
|
|
{
|
|
if (dll == NULL)
|
|
return 0;
|
|
|
|
for (; dll != NULL; dll = dll->next)
|
|
{
|
|
if (strcasecmp(dll->name, dll_name) == 0)
|
|
{
|
|
IMPORT_FUNCTION* fun = dll->functions;
|
|
for (; fun != NULL; fun = fun->next)
|
|
{
|
|
if (fun->has_ordinal && fun->ordinal == ordinal)
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
define_function(imports_standard)
|
|
{
|
|
char* dll_name = string_argument(1);
|
|
char* function_name = string_argument(2);
|
|
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
if (!pe)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
return_integer(pe_imports(pe->imported_dlls, dll_name, function_name));
|
|
}
|
|
|
|
define_function(imports)
|
|
{
|
|
int64_t flags = integer_argument(1);
|
|
char* dll_name = string_argument(2);
|
|
char* function_name = string_argument(3);
|
|
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
if (!pe)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
if (flags & IMPORT_STANDARD &&
|
|
pe_imports(pe->imported_dlls, dll_name, function_name))
|
|
{
|
|
return_integer(1);
|
|
}
|
|
|
|
if (flags & IMPORT_DELAYED &&
|
|
pe_imports(pe->delay_imported_dlls, dll_name, function_name))
|
|
{
|
|
return_integer(1);
|
|
}
|
|
|
|
return_integer(0);
|
|
}
|
|
|
|
define_function(imports_standard_ordinal)
|
|
{
|
|
char* dll_name = string_argument(1);
|
|
int64_t ordinal = integer_argument(2);
|
|
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
if (!pe)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
return_integer(pe_imports_ordinal(pe->imported_dlls, dll_name, ordinal))
|
|
}
|
|
|
|
define_function(imports_ordinal)
|
|
{
|
|
int64_t flags = integer_argument(1);
|
|
char* dll_name = string_argument(2);
|
|
int64_t ordinal = integer_argument(3);
|
|
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
if (!pe)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
if (flags & IMPORT_STANDARD &&
|
|
pe_imports_ordinal(pe->imported_dlls, dll_name, ordinal))
|
|
{
|
|
return_integer(1);
|
|
}
|
|
|
|
if (flags & IMPORT_DELAYED &&
|
|
pe_imports_ordinal(pe->delay_imported_dlls, dll_name, ordinal))
|
|
{
|
|
return_integer(1);
|
|
}
|
|
|
|
return_integer(0);
|
|
}
|
|
|
|
define_function(imports_standard_regex)
|
|
{
|
|
RE* dll_name = regexp_argument(1);
|
|
RE* function_name = regexp_argument(2);
|
|
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
if (!pe)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
return_integer(pe_imports_regexp(
|
|
yr_scan_context(), pe->imported_dlls, dll_name, function_name))
|
|
}
|
|
|
|
define_function(imports_regex)
|
|
{
|
|
int64_t flags = integer_argument(1);
|
|
RE* dll_name = regexp_argument(2);
|
|
RE* function_name = regexp_argument(3);
|
|
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
if (!pe)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
int64_t result = 0;
|
|
|
|
if (flags & IMPORT_STANDARD)
|
|
result += pe_imports_regexp(
|
|
yr_scan_context(), pe->imported_dlls, dll_name, function_name);
|
|
|
|
if (flags & IMPORT_DELAYED)
|
|
result += pe_imports_regexp(
|
|
yr_scan_context(), pe->delay_imported_dlls, dll_name, function_name);
|
|
|
|
return_integer(result);
|
|
}
|
|
|
|
define_function(imports_standard_dll)
|
|
{
|
|
char* dll_name = string_argument(1);
|
|
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
if (!pe)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
return_integer(pe_imports_dll(pe->imported_dlls, dll_name));
|
|
}
|
|
|
|
define_function(imports_dll)
|
|
{
|
|
int64_t flags = integer_argument(1);
|
|
char* dll_name = string_argument(2);
|
|
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
if (!pe)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
int64_t result = 0;
|
|
|
|
if (flags & IMPORT_STANDARD)
|
|
result += pe_imports_dll(pe->imported_dlls, dll_name);
|
|
|
|
if (flags & IMPORT_DELAYED)
|
|
result += pe_imports_dll(pe->delay_imported_dlls, dll_name);
|
|
|
|
return_integer(result);
|
|
}
|
|
|
|
define_function(import_rva)
|
|
{
|
|
SIZED_STRING* in_dll_name = sized_string_argument(1);
|
|
SIZED_STRING* in_function_name = sized_string_argument(2);
|
|
|
|
SIZED_STRING* dll_name;
|
|
SIZED_STRING* function_name;
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
if (!pe)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
int64_t num_imports = yr_get_integer(pe->object, "number_of_imports");
|
|
if (IS_UNDEFINED(num_imports))
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
for (int i = 0; i < num_imports; i++)
|
|
{
|
|
dll_name = yr_get_string(module, "import_details[%i].library_name", i);
|
|
if (dll_name == NULL || IS_UNDEFINED(dll_name) ||
|
|
ss_icompare(in_dll_name, dll_name) != 0)
|
|
continue;
|
|
|
|
int64_t num_functions = yr_get_integer(
|
|
module, "import_details[%i].number_of_functions", i);
|
|
if (IS_UNDEFINED(num_functions))
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
for (int j = 0; j < num_functions; j++)
|
|
{
|
|
function_name = yr_get_string(
|
|
module, "import_details[%i].functions[%i].name", i, j);
|
|
if (function_name == NULL || IS_UNDEFINED(function_name))
|
|
continue;
|
|
|
|
if (ss_icompare(in_function_name, function_name) == 0)
|
|
return_integer(yr_get_integer(
|
|
module, "import_details[%i].functions[%i].rva", i, j));
|
|
}
|
|
}
|
|
|
|
return_integer(YR_UNDEFINED);
|
|
}
|
|
|
|
define_function(import_rva_ordinal)
|
|
{
|
|
SIZED_STRING* in_dll_name = sized_string_argument(1);
|
|
int64_t in_ordinal = integer_argument(2);
|
|
|
|
SIZED_STRING* dll_name;
|
|
int64_t ordinal;
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
if (!pe)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
int64_t num_imports = yr_get_integer(pe->object, "number_of_imports");
|
|
if (IS_UNDEFINED(num_imports))
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
for (int i = 0; i < num_imports; i++)
|
|
{
|
|
dll_name = yr_get_string(module, "import_details[%i].library_name", i);
|
|
if (dll_name == NULL || IS_UNDEFINED(dll_name) ||
|
|
ss_icompare(in_dll_name, dll_name) != 0)
|
|
continue;
|
|
|
|
int64_t num_functions = yr_get_integer(
|
|
module, "import_details[%i].number_of_functions", i);
|
|
if (IS_UNDEFINED(num_functions))
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
for (int j = 0; j < num_functions; j++)
|
|
{
|
|
ordinal = yr_get_integer(
|
|
module, "import_details[%i].functions[%i].ordinal", i, j);
|
|
if (IS_UNDEFINED(ordinal))
|
|
continue;
|
|
|
|
if (ordinal == in_ordinal)
|
|
return_integer(yr_get_integer(
|
|
module, "import_details[%i].functions[%i].rva", i, j));
|
|
}
|
|
}
|
|
|
|
return_integer(YR_UNDEFINED);
|
|
}
|
|
|
|
define_function(delayed_import_rva)
|
|
{
|
|
SIZED_STRING* in_dll_name = sized_string_argument(1);
|
|
SIZED_STRING* in_function_name = sized_string_argument(2);
|
|
|
|
SIZED_STRING* dll_name;
|
|
SIZED_STRING* function_name;
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
if (!pe)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
int64_t num_imports = yr_get_integer(pe->object, "number_of_delayed_imports");
|
|
|
|
if (IS_UNDEFINED(num_imports))
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
for (int i = 0; i < num_imports; i++)
|
|
{
|
|
dll_name = yr_get_string(
|
|
module, "delayed_import_details[%i].library_name", i);
|
|
|
|
if (dll_name == NULL || IS_UNDEFINED(dll_name) ||
|
|
ss_icompare(in_dll_name, dll_name) != 0)
|
|
continue;
|
|
|
|
int64_t num_functions = yr_get_integer(
|
|
module, "delayed_import_details[%i].number_of_functions", i);
|
|
|
|
if (IS_UNDEFINED(num_functions))
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
for (int j = 0; j < num_functions; j++)
|
|
{
|
|
function_name = yr_get_string(
|
|
module, "delayed_import_details[%i].functions[%i].name", i, j);
|
|
|
|
if (function_name == NULL || IS_UNDEFINED(function_name))
|
|
continue;
|
|
|
|
if (ss_icompare(in_function_name, function_name) == 0)
|
|
return_integer(yr_get_integer(
|
|
module, "delayed_import_details[%i].functions[%i].rva", i, j));
|
|
}
|
|
}
|
|
|
|
return_integer(YR_UNDEFINED);
|
|
}
|
|
|
|
define_function(delayed_import_rva_ordinal)
|
|
{
|
|
SIZED_STRING* in_dll_name = sized_string_argument(1);
|
|
int64_t in_ordinal = integer_argument(2);
|
|
|
|
SIZED_STRING* dll_name;
|
|
int64_t ordinal;
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
if (!pe)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
int64_t num_imports = yr_get_integer(pe->object, "number_of_delayed_imports");
|
|
if (IS_UNDEFINED(num_imports))
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
for (int i = 0; i < num_imports; i++)
|
|
{
|
|
dll_name = yr_get_string(
|
|
module, "delayed_import_details[%i].library_name", i);
|
|
|
|
if (dll_name == NULL || IS_UNDEFINED(dll_name) ||
|
|
ss_icompare(in_dll_name, dll_name) != 0)
|
|
continue;
|
|
|
|
int64_t num_functions = yr_get_integer(
|
|
module, "delayed_import_details[%i].number_of_functions", i);
|
|
|
|
if (IS_UNDEFINED(num_functions))
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
for (int j = 0; j < num_functions; j++)
|
|
{
|
|
ordinal = yr_get_integer(
|
|
module, "delayed_import_details[%i].functions[%i].ordinal", i, j);
|
|
|
|
if (IS_UNDEFINED(ordinal))
|
|
continue;
|
|
|
|
if (ordinal == in_ordinal)
|
|
return_integer(yr_get_integer(
|
|
module, "delayed_import_details[%i].functions[%i].rva", i, j));
|
|
}
|
|
}
|
|
|
|
return_integer(YR_UNDEFINED);
|
|
}
|
|
|
|
define_function(locale)
|
|
{
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
uint64_t locale = integer_argument(1);
|
|
|
|
if (yr_is_undefined(module, "number_of_resources"))
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
// If not a PE file, return YR_UNDEFINED
|
|
|
|
if (pe == NULL)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
int n = (int) yr_get_integer(module, "number_of_resources");
|
|
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
uint64_t rsrc_language = yr_get_integer(
|
|
module, "resources[%i].language", i);
|
|
|
|
if ((rsrc_language & 0xFFFF) == locale)
|
|
return_integer(1);
|
|
}
|
|
|
|
return_integer(0);
|
|
}
|
|
|
|
define_function(language)
|
|
{
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
uint64_t language = integer_argument(1);
|
|
|
|
if (yr_is_undefined(module, "number_of_resources"))
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
// If not a PE file, return YR_UNDEFINED
|
|
|
|
if (pe == NULL)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
int n = (int) yr_get_integer(module, "number_of_resources");
|
|
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
uint64_t rsrc_language = yr_get_integer(
|
|
module, "resources[%i].language", i);
|
|
|
|
if ((rsrc_language & 0xFF) == language)
|
|
return_integer(1);
|
|
}
|
|
|
|
return_integer(0);
|
|
}
|
|
|
|
define_function(is_dll)
|
|
{
|
|
int64_t characteristics;
|
|
YR_OBJECT* module = yr_module();
|
|
|
|
if (yr_is_undefined(module, "characteristics"))
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
characteristics = yr_get_integer(module, "characteristics");
|
|
return_integer(characteristics & IMAGE_FILE_DLL);
|
|
}
|
|
|
|
define_function(is_32bit)
|
|
{
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
if (pe == NULL)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
return_integer(IS_64BITS_PE(pe) ? 0 : 1);
|
|
}
|
|
|
|
define_function(is_64bit)
|
|
{
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
if (pe == NULL)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
return_integer(IS_64BITS_PE(pe) ? 1 : 0);
|
|
}
|
|
|
|
// _rich_version
|
|
//
|
|
// Returns the number of rich signatures that match the specified version and
|
|
// toolid numbers.
|
|
//
|
|
static uint64_t _rich_version(
|
|
YR_OBJECT* module,
|
|
uint64_t version,
|
|
uint64_t toolid)
|
|
{
|
|
int64_t rich_length;
|
|
int64_t rich_count;
|
|
|
|
PRICH_SIGNATURE clear_rich_signature;
|
|
SIZED_STRING* rich_string;
|
|
|
|
uint64_t result = 0;
|
|
|
|
// Check if the required fields are set
|
|
if (yr_is_undefined(module, "rich_signature.length"))
|
|
return YR_UNDEFINED;
|
|
|
|
rich_length = yr_get_integer(module, "rich_signature.length");
|
|
rich_string = yr_get_string(module, "rich_signature.clear_data");
|
|
|
|
// If the clear_data was not set, return YR_UNDEFINED
|
|
if (rich_string == NULL)
|
|
return YR_UNDEFINED;
|
|
|
|
if (version == YR_UNDEFINED && toolid == YR_UNDEFINED)
|
|
return false;
|
|
|
|
clear_rich_signature = (PRICH_SIGNATURE) rich_string->c_string;
|
|
|
|
// Loop over the versions in the rich signature
|
|
|
|
rich_count = (rich_length - sizeof(RICH_SIGNATURE)) /
|
|
sizeof(RICH_VERSION_INFO);
|
|
|
|
for (int i = 0; i < rich_count; i++)
|
|
{
|
|
DWORD id_version = yr_le32toh(clear_rich_signature->versions[i].id_version);
|
|
|
|
int match_version = (version == RICH_VERSION_VERSION(id_version));
|
|
int match_toolid = (toolid == RICH_VERSION_ID(id_version));
|
|
|
|
if ((version == YR_UNDEFINED || match_version) &&
|
|
(toolid == YR_UNDEFINED || match_toolid))
|
|
{
|
|
result += yr_le32toh(clear_rich_signature->versions[i].times);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
define_function(rich_version)
|
|
{
|
|
return_integer(_rich_version(yr_module(), integer_argument(1), YR_UNDEFINED));
|
|
}
|
|
|
|
define_function(rich_version_toolid)
|
|
{
|
|
return_integer(
|
|
_rich_version(yr_module(), integer_argument(1), integer_argument(2)));
|
|
}
|
|
|
|
define_function(rich_toolid)
|
|
{
|
|
return_integer(_rich_version(yr_module(), YR_UNDEFINED, integer_argument(1)));
|
|
}
|
|
|
|
define_function(rich_toolid_version)
|
|
{
|
|
return_integer(
|
|
_rich_version(yr_module(), integer_argument(2), integer_argument(1)));
|
|
}
|
|
|
|
define_function(calculate_checksum)
|
|
{
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
uint64_t csum = 0;
|
|
size_t csum_offset;
|
|
|
|
if (pe == NULL)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
csum_offset = ((uint8_t*) &(pe->header->OptionalHeader) +
|
|
offsetof(IMAGE_OPTIONAL_HEADER32, CheckSum)) -
|
|
pe->data;
|
|
|
|
for (size_t i = 0; i <= pe->data_size / 4; i++)
|
|
{
|
|
// Treat the CheckSum field as 0 -- the offset is the same for
|
|
// PE32 and PE64.
|
|
|
|
if (4 * i == csum_offset)
|
|
continue;
|
|
|
|
if (4 * i + 4 <= pe->data_size)
|
|
{
|
|
csum +=
|
|
((uint64_t) pe->data[4 * i] + ((uint64_t) pe->data[4 * i + 1] << 8) +
|
|
((uint64_t) pe->data[4 * i + 2] << 16) +
|
|
((uint64_t) pe->data[4 * i + 3] << 24));
|
|
}
|
|
else
|
|
{
|
|
for (size_t j = 0; j < pe->data_size % 4; j++)
|
|
csum += (uint64_t) pe->data[4 * i + j] << (8 * j);
|
|
}
|
|
|
|
if (csum > 0xffffffff)
|
|
csum = (csum & 0xffffffff) + (csum >> 32);
|
|
}
|
|
|
|
csum = (csum & 0xffff) + (csum >> 16);
|
|
csum += (csum >> 16);
|
|
csum &= 0xffff;
|
|
csum += pe->data_size;
|
|
|
|
return_integer(csum);
|
|
}
|
|
|
|
define_function(rva_to_offset)
|
|
{
|
|
YR_OBJECT* module = yr_module();
|
|
PE* pe = (PE*) module->data;
|
|
|
|
uint64_t rva;
|
|
int64_t offset;
|
|
|
|
if (pe == NULL)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
rva = integer_argument(1);
|
|
offset = pe_rva_to_offset(pe, rva);
|
|
|
|
if (offset == -1)
|
|
return_integer(YR_UNDEFINED);
|
|
|
|
return_integer(offset);
|
|
}
|
|
|
|
begin_declarations
|
|
declare_integer("MACHINE_UNKNOWN");
|
|
declare_integer("MACHINE_AM33");
|
|
declare_integer("MACHINE_AMD64");
|
|
declare_integer("MACHINE_ARM");
|
|
declare_integer("MACHINE_ARMNT");
|
|
declare_integer("MACHINE_ARM64");
|
|
declare_integer("MACHINE_EBC");
|
|
declare_integer("MACHINE_I386");
|
|
declare_integer("MACHINE_IA64");
|
|
declare_integer("MACHINE_M32R");
|
|
declare_integer("MACHINE_MIPS16");
|
|
declare_integer("MACHINE_MIPSFPU");
|
|
declare_integer("MACHINE_MIPSFPU16");
|
|
declare_integer("MACHINE_POWERPC");
|
|
declare_integer("MACHINE_POWERPCFP");
|
|
declare_integer("MACHINE_R4000");
|
|
declare_integer("MACHINE_SH3");
|
|
declare_integer("MACHINE_SH3DSP");
|
|
declare_integer("MACHINE_SH4");
|
|
declare_integer("MACHINE_SH5");
|
|
declare_integer("MACHINE_THUMB");
|
|
declare_integer("MACHINE_WCEMIPSV2");
|
|
declare_integer("MACHINE_TARGET_HOST");
|
|
declare_integer("MACHINE_R3000");
|
|
declare_integer("MACHINE_R10000");
|
|
declare_integer("MACHINE_ALPHA");
|
|
declare_integer("MACHINE_SH3E");
|
|
declare_integer("MACHINE_ALPHA64");
|
|
declare_integer("MACHINE_AXP64");
|
|
declare_integer("MACHINE_TRICORE");
|
|
declare_integer("MACHINE_CEF");
|
|
declare_integer("MACHINE_CEE");
|
|
|
|
declare_integer("SUBSYSTEM_UNKNOWN");
|
|
declare_integer("SUBSYSTEM_NATIVE");
|
|
declare_integer("SUBSYSTEM_WINDOWS_GUI");
|
|
declare_integer("SUBSYSTEM_WINDOWS_CUI");
|
|
declare_integer("SUBSYSTEM_OS2_CUI");
|
|
declare_integer("SUBSYSTEM_POSIX_CUI");
|
|
declare_integer("SUBSYSTEM_NATIVE_WINDOWS");
|
|
declare_integer("SUBSYSTEM_WINDOWS_CE_GUI");
|
|
declare_integer("SUBSYSTEM_EFI_APPLICATION");
|
|
declare_integer("SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER");
|
|
declare_integer("SUBSYSTEM_EFI_RUNTIME_DRIVER");
|
|
declare_integer("SUBSYSTEM_EFI_ROM_IMAGE");
|
|
declare_integer("SUBSYSTEM_XBOX");
|
|
declare_integer("SUBSYSTEM_WINDOWS_BOOT_APPLICATION");
|
|
|
|
declare_integer("HIGH_ENTROPY_VA");
|
|
declare_integer("DYNAMIC_BASE");
|
|
declare_integer("FORCE_INTEGRITY");
|
|
declare_integer("NX_COMPAT");
|
|
declare_integer("NO_ISOLATION");
|
|
declare_integer("NO_SEH");
|
|
declare_integer("NO_BIND");
|
|
declare_integer("APPCONTAINER");
|
|
declare_integer("WDM_DRIVER");
|
|
declare_integer("GUARD_CF");
|
|
declare_integer("TERMINAL_SERVER_AWARE");
|
|
|
|
declare_integer("RELOCS_STRIPPED");
|
|
declare_integer("EXECUTABLE_IMAGE");
|
|
declare_integer("LINE_NUMS_STRIPPED");
|
|
declare_integer("LOCAL_SYMS_STRIPPED");
|
|
declare_integer("AGGRESIVE_WS_TRIM");
|
|
declare_integer("LARGE_ADDRESS_AWARE");
|
|
declare_integer("BYTES_REVERSED_LO");
|
|
declare_integer("MACHINE_32BIT");
|
|
declare_integer("DEBUG_STRIPPED");
|
|
declare_integer("REMOVABLE_RUN_FROM_SWAP");
|
|
declare_integer("NET_RUN_FROM_SWAP");
|
|
declare_integer("SYSTEM");
|
|
declare_integer("DLL");
|
|
declare_integer("UP_SYSTEM_ONLY");
|
|
declare_integer("BYTES_REVERSED_HI");
|
|
|
|
declare_integer("IMAGE_DIRECTORY_ENTRY_EXPORT");
|
|
declare_integer("IMAGE_DIRECTORY_ENTRY_IMPORT");
|
|
declare_integer("IMAGE_DIRECTORY_ENTRY_RESOURCE");
|
|
declare_integer("IMAGE_DIRECTORY_ENTRY_EXCEPTION");
|
|
declare_integer("IMAGE_DIRECTORY_ENTRY_SECURITY");
|
|
declare_integer("IMAGE_DIRECTORY_ENTRY_BASERELOC");
|
|
declare_integer("IMAGE_DIRECTORY_ENTRY_DEBUG");
|
|
declare_integer("IMAGE_DIRECTORY_ENTRY_ARCHITECTURE");
|
|
declare_integer("IMAGE_DIRECTORY_ENTRY_COPYRIGHT");
|
|
declare_integer("IMAGE_DIRECTORY_ENTRY_GLOBALPTR");
|
|
declare_integer("IMAGE_DIRECTORY_ENTRY_TLS");
|
|
declare_integer("IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG");
|
|
declare_integer("IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT");
|
|
declare_integer("IMAGE_DIRECTORY_ENTRY_IAT");
|
|
declare_integer("IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT");
|
|
declare_integer("IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR");
|
|
|
|
declare_integer("IMAGE_NT_OPTIONAL_HDR32_MAGIC");
|
|
declare_integer("IMAGE_NT_OPTIONAL_HDR64_MAGIC");
|
|
declare_integer("IMAGE_ROM_OPTIONAL_HDR_MAGIC");
|
|
|
|
declare_integer("SECTION_NO_PAD");
|
|
declare_integer("SECTION_CNT_CODE");
|
|
declare_integer("SECTION_CNT_INITIALIZED_DATA");
|
|
declare_integer("SECTION_CNT_UNINITIALIZED_DATA");
|
|
declare_integer("SECTION_LNK_OTHER");
|
|
declare_integer("SECTION_LNK_INFO");
|
|
declare_integer("SECTION_LNK_REMOVE");
|
|
declare_integer("SECTION_LNK_COMDAT");
|
|
declare_integer("SECTION_NO_DEFER_SPEC_EXC");
|
|
declare_integer("SECTION_GPREL");
|
|
declare_integer("SECTION_MEM_FARDATA");
|
|
declare_integer("SECTION_MEM_PURGEABLE");
|
|
declare_integer("SECTION_MEM_16BIT");
|
|
declare_integer("SECTION_MEM_LOCKED");
|
|
declare_integer("SECTION_MEM_PRELOAD");
|
|
declare_integer("SECTION_ALIGN_1BYTES");
|
|
declare_integer("SECTION_ALIGN_2BYTES");
|
|
declare_integer("SECTION_ALIGN_4BYTES");
|
|
declare_integer("SECTION_ALIGN_8BYTES");
|
|
declare_integer("SECTION_ALIGN_16BYTES");
|
|
declare_integer("SECTION_ALIGN_32BYTES");
|
|
declare_integer("SECTION_ALIGN_64BYTES");
|
|
declare_integer("SECTION_ALIGN_128BYTES");
|
|
declare_integer("SECTION_ALIGN_256BYTES");
|
|
declare_integer("SECTION_ALIGN_512BYTES");
|
|
declare_integer("SECTION_ALIGN_1024BYTES");
|
|
declare_integer("SECTION_ALIGN_2048BYTES");
|
|
declare_integer("SECTION_ALIGN_4096BYTES");
|
|
declare_integer("SECTION_ALIGN_8192BYTES");
|
|
declare_integer("SECTION_ALIGN_MASK");
|
|
declare_integer("SECTION_LNK_NRELOC_OVFL");
|
|
declare_integer("SECTION_MEM_DISCARDABLE");
|
|
declare_integer("SECTION_MEM_NOT_CACHED");
|
|
declare_integer("SECTION_MEM_NOT_PAGED");
|
|
declare_integer("SECTION_MEM_SHARED");
|
|
declare_integer("SECTION_MEM_EXECUTE");
|
|
declare_integer("SECTION_MEM_READ");
|
|
declare_integer("SECTION_MEM_WRITE");
|
|
declare_integer("SECTION_SCALE_INDEX");
|
|
|
|
declare_integer("RESOURCE_TYPE_CURSOR");
|
|
declare_integer("RESOURCE_TYPE_BITMAP");
|
|
declare_integer("RESOURCE_TYPE_ICON");
|
|
declare_integer("RESOURCE_TYPE_MENU");
|
|
declare_integer("RESOURCE_TYPE_DIALOG");
|
|
declare_integer("RESOURCE_TYPE_STRING");
|
|
declare_integer("RESOURCE_TYPE_FONTDIR");
|
|
declare_integer("RESOURCE_TYPE_FONT");
|
|
declare_integer("RESOURCE_TYPE_ACCELERATOR");
|
|
declare_integer("RESOURCE_TYPE_RCDATA");
|
|
declare_integer("RESOURCE_TYPE_MESSAGETABLE");
|
|
declare_integer("RESOURCE_TYPE_GROUP_CURSOR");
|
|
declare_integer("RESOURCE_TYPE_GROUP_ICON");
|
|
declare_integer("RESOURCE_TYPE_VERSION");
|
|
declare_integer("RESOURCE_TYPE_DLGINCLUDE");
|
|
declare_integer("RESOURCE_TYPE_PLUGPLAY");
|
|
declare_integer("RESOURCE_TYPE_VXD");
|
|
declare_integer("RESOURCE_TYPE_ANICURSOR");
|
|
declare_integer("RESOURCE_TYPE_ANIICON");
|
|
declare_integer("RESOURCE_TYPE_HTML");
|
|
declare_integer("RESOURCE_TYPE_MANIFEST");
|
|
|
|
declare_integer("IMAGE_DEBUG_TYPE_UNKNOWN");
|
|
declare_integer("IMAGE_DEBUG_TYPE_COFF");
|
|
declare_integer("IMAGE_DEBUG_TYPE_CODEVIEW");
|
|
declare_integer("IMAGE_DEBUG_TYPE_FPO");
|
|
declare_integer("IMAGE_DEBUG_TYPE_MISC");
|
|
declare_integer("IMAGE_DEBUG_TYPE_EXCEPTION");
|
|
declare_integer("IMAGE_DEBUG_TYPE_FIXUP");
|
|
declare_integer("IMAGE_DEBUG_TYPE_OMAP_TO_SRC");
|
|
declare_integer("IMAGE_DEBUG_TYPE_OMAP_FROM_SRC");
|
|
declare_integer("IMAGE_DEBUG_TYPE_BORLAND");
|
|
declare_integer("IMAGE_DEBUG_TYPE_RESERVED10");
|
|
declare_integer("IMAGE_DEBUG_TYPE_CLSID");
|
|
declare_integer("IMAGE_DEBUG_TYPE_VC_FEATURE");
|
|
declare_integer("IMAGE_DEBUG_TYPE_POGO");
|
|
declare_integer("IMAGE_DEBUG_TYPE_ILTCG");
|
|
declare_integer("IMAGE_DEBUG_TYPE_MPX");
|
|
declare_integer("IMAGE_DEBUG_TYPE_REPRO");
|
|
|
|
declare_integer("IMPORT_DELAYED");
|
|
declare_integer("IMPORT_STANDARD");
|
|
declare_integer("IMPORT_ANY");
|
|
|
|
declare_integer("is_pe");
|
|
declare_integer("machine");
|
|
declare_integer("number_of_sections");
|
|
declare_integer("timestamp");
|
|
declare_integer("pointer_to_symbol_table");
|
|
declare_integer("number_of_symbols");
|
|
declare_integer("size_of_optional_header");
|
|
declare_integer("characteristics");
|
|
|
|
declare_integer("entry_point");
|
|
declare_integer("entry_point_raw");
|
|
declare_integer("image_base");
|
|
declare_integer("number_of_rva_and_sizes");
|
|
declare_integer("number_of_version_infos");
|
|
|
|
declare_string_dictionary("version_info");
|
|
|
|
begin_struct_array("version_info_list")
|
|
declare_string("key");
|
|
declare_string("value");
|
|
end_struct_array("version_info_list");
|
|
|
|
declare_integer("opthdr_magic");
|
|
declare_integer("size_of_code");
|
|
declare_integer("size_of_initialized_data");
|
|
declare_integer("size_of_uninitialized_data");
|
|
declare_integer("base_of_code");
|
|
declare_integer("base_of_data");
|
|
declare_integer("section_alignment");
|
|
declare_integer("file_alignment");
|
|
|
|
begin_struct("linker_version")
|
|
declare_integer("major");
|
|
declare_integer("minor");
|
|
end_struct("linker_version");
|
|
|
|
begin_struct("os_version")
|
|
declare_integer("major");
|
|
declare_integer("minor");
|
|
end_struct("os_version");
|
|
|
|
begin_struct("image_version")
|
|
declare_integer("major");
|
|
declare_integer("minor");
|
|
end_struct("image_version");
|
|
|
|
begin_struct("subsystem_version")
|
|
declare_integer("major");
|
|
declare_integer("minor");
|
|
end_struct("subsystem_version");
|
|
|
|
declare_integer("win32_version_value");
|
|
declare_integer("size_of_image");
|
|
declare_integer("size_of_headers");
|
|
|
|
declare_integer("checksum");
|
|
declare_function("calculate_checksum", "", "i", calculate_checksum);
|
|
declare_integer("subsystem");
|
|
|
|
declare_integer("dll_characteristics");
|
|
declare_integer("size_of_stack_reserve");
|
|
declare_integer("size_of_stack_commit");
|
|
declare_integer("size_of_heap_reserve");
|
|
declare_integer("size_of_heap_commit");
|
|
declare_integer("loader_flags");
|
|
|
|
begin_struct_array("data_directories")
|
|
declare_integer("virtual_address");
|
|
declare_integer("size");
|
|
end_struct_array("data_directories");
|
|
|
|
begin_struct_array("sections")
|
|
declare_string("name");
|
|
declare_string("full_name");
|
|
declare_integer("characteristics");
|
|
declare_integer("virtual_address");
|
|
declare_integer("virtual_size");
|
|
declare_integer("raw_data_offset");
|
|
declare_integer("raw_data_size");
|
|
declare_integer("pointer_to_relocations");
|
|
declare_integer("pointer_to_line_numbers");
|
|
declare_integer("number_of_relocations");
|
|
declare_integer("number_of_line_numbers");
|
|
end_struct_array("sections");
|
|
|
|
begin_struct("overlay")
|
|
declare_integer("offset");
|
|
declare_integer("size");
|
|
end_struct("overlay");
|
|
|
|
begin_struct("rich_signature")
|
|
declare_integer("offset");
|
|
declare_integer("length");
|
|
declare_integer("key");
|
|
declare_string("raw_data");
|
|
declare_string("clear_data");
|
|
declare_string("version_data");
|
|
declare_function("version", "i", "i", rich_version);
|
|
declare_function("version", "ii", "i", rich_version_toolid);
|
|
declare_function("toolid", "i", "i", rich_toolid);
|
|
declare_function("toolid", "ii", "i", rich_toolid_version);
|
|
end_struct("rich_signature");
|
|
|
|
#if defined(HAVE_LIBCRYPTO) || defined(HAVE_WINCRYPT_H) || \
|
|
defined(HAVE_COMMONCRYPTO_COMMONCRYPTO_H)
|
|
declare_function("imphash", "", "s", imphash);
|
|
#endif
|
|
|
|
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", "r", "i", exports_regexp);
|
|
declare_function("exports", "i", "i", exports_ordinal);
|
|
declare_function("exports_index", "s", "i", exports_index_name);
|
|
declare_function("exports_index", "i", "i", exports_index_ordinal);
|
|
declare_function("exports_index", "r", "i", exports_index_regex);
|
|
declare_function("imports", "ss", "i", imports_standard);
|
|
declare_function("imports", "si", "i", imports_standard_ordinal);
|
|
declare_function("imports", "s", "i", imports_standard_dll);
|
|
declare_function("imports", "rr", "i", imports_standard_regex);
|
|
declare_function("imports", "iss", "i", imports);
|
|
declare_function("imports", "isi", "i", imports_ordinal);
|
|
declare_function("imports", "is", "i", imports_dll);
|
|
declare_function("imports", "irr", "i", imports_regex);
|
|
declare_function("import_rva", "ss", "i", import_rva);
|
|
declare_function("import_rva", "si", "i", import_rva_ordinal);
|
|
declare_function("delayed_import_rva", "ss", "i", delayed_import_rva);
|
|
declare_function("delayed_import_rva", "si", "i", delayed_import_rva_ordinal);
|
|
declare_function("locale", "i", "i", locale);
|
|
declare_function("language", "i", "i", language);
|
|
declare_function("is_dll", "", "i", is_dll);
|
|
declare_function("is_32bit", "", "i", is_32bit);
|
|
declare_function("is_64bit", "", "i", is_64bit);
|
|
|
|
declare_integer("number_of_imports");
|
|
declare_integer("number_of_imported_functions");
|
|
declare_integer("number_of_delayed_imports");
|
|
declare_integer("number_of_delayed_imported_functions");
|
|
declare_integer("number_of_exports");
|
|
|
|
declare_string("dll_name");
|
|
declare_integer("export_timestamp");
|
|
begin_struct_array("export_details")
|
|
declare_integer("offset");
|
|
declare_string("name");
|
|
declare_string("forward_name");
|
|
declare_integer("ordinal");
|
|
declare_integer("rva");
|
|
end_struct_array("export_details")
|
|
|
|
begin_struct_array("import_details")
|
|
declare_string("library_name");
|
|
declare_integer("number_of_functions");
|
|
begin_struct_array("functions")
|
|
declare_string("name");
|
|
declare_integer("ordinal");
|
|
declare_integer("rva");
|
|
end_struct_array("functions");
|
|
end_struct_array("import_details");
|
|
|
|
begin_struct_array("delayed_import_details")
|
|
declare_string("library_name");
|
|
declare_integer("number_of_functions");
|
|
begin_struct_array("functions")
|
|
declare_string("name");
|
|
declare_integer("ordinal");
|
|
declare_integer("rva");
|
|
end_struct_array("functions");
|
|
end_struct_array("delayed_import_details");
|
|
|
|
declare_integer("resource_timestamp");
|
|
|
|
begin_struct("resource_version")
|
|
declare_integer("major");
|
|
declare_integer("minor");
|
|
end_struct("resource_version")
|
|
|
|
begin_struct_array("resources")
|
|
declare_integer("rva");
|
|
declare_integer("offset");
|
|
declare_integer("length");
|
|
declare_integer("type");
|
|
declare_integer("id");
|
|
declare_integer("language");
|
|
declare_string("type_string");
|
|
declare_string("name_string");
|
|
declare_string("language_string");
|
|
end_struct_array("resources")
|
|
|
|
declare_integer("number_of_resources");
|
|
declare_string("pdb_path");
|
|
|
|
#if defined(HAVE_LIBCRYPTO) && !defined(BORINGSSL)
|
|
begin_struct_array("signatures")
|
|
declare_string("thumbprint");
|
|
declare_string("issuer");
|
|
declare_string("subject");
|
|
declare_integer("version");
|
|
declare_string("algorithm");
|
|
declare_string("algorithm_oid");
|
|
declare_string("serial");
|
|
declare_integer("not_before");
|
|
declare_integer("not_after");
|
|
|
|
declare_integer("verified");
|
|
declare_string("digest_alg");
|
|
declare_string("digest");
|
|
declare_string("file_digest");
|
|
declare_integer("number_of_certificates");
|
|
begin_struct_array("certificates")
|
|
;
|
|
declare_string("thumbprint");
|
|
declare_string("issuer");
|
|
declare_string("subject");
|
|
declare_integer("version");
|
|
declare_string("algorithm");
|
|
declare_string("algorithm_oid");
|
|
declare_string("serial");
|
|
declare_integer("not_before");
|
|
declare_integer("not_after");
|
|
end_struct_array("certificates");
|
|
|
|
begin_struct("signer_info")
|
|
;
|
|
declare_string("program_name");
|
|
declare_string("digest");
|
|
declare_string("digest_alg");
|
|
declare_integer("length_of_chain");
|
|
begin_struct_array("chain")
|
|
;
|
|
declare_string("thumbprint");
|
|
declare_string("issuer");
|
|
declare_string("subject");
|
|
declare_integer("version");
|
|
declare_string("algorithm");
|
|
declare_string("algorithm_oid");
|
|
declare_string("serial");
|
|
declare_integer("not_before");
|
|
declare_integer("not_after");
|
|
end_struct_array("chain");
|
|
end_struct("signer_info");
|
|
|
|
declare_integer("number_of_countersignatures");
|
|
begin_struct_array("countersignatures")
|
|
;
|
|
declare_integer("verified");
|
|
declare_integer("sign_time");
|
|
declare_string("digest_alg");
|
|
declare_string("digest");
|
|
declare_integer("length_of_chain");
|
|
begin_struct_array("chain")
|
|
;
|
|
declare_string("thumbprint");
|
|
declare_string("issuer");
|
|
declare_string("subject");
|
|
declare_integer("version");
|
|
declare_string("algorithm");
|
|
declare_string("algorithm_oid");
|
|
declare_string("serial");
|
|
declare_integer("not_before");
|
|
declare_integer("not_after");
|
|
end_struct_array("chain");
|
|
end_struct_array("countersignatures")
|
|
|
|
declare_function("valid_on", "i", "i", valid_on);
|
|
|
|
end_struct_array("signatures")
|
|
|
|
// If any of the signatures correctly signs the binary
|
|
declare_integer("is_signed");
|
|
declare_integer("number_of_signatures");
|
|
#endif
|
|
|
|
declare_function("rva_to_offset", "i", "i", rva_to_offset);
|
|
end_declarations
|
|
|
|
int module_initialize(YR_MODULE* module)
|
|
{
|
|
#if defined(HAVE_LIBCRYPTO) && !defined(BORINGSSL)
|
|
// Initialize OpenSSL global objects for the auth library before any
|
|
// multithreaded environment as it is not thread-safe. This can
|
|
// only be called once per process.
|
|
static bool s_initialized = false;
|
|
|
|
if (!s_initialized)
|
|
{
|
|
s_initialized = true;
|
|
initialize_authenticode_parser();
|
|
}
|
|
#endif
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
int module_finalize(YR_MODULE* module)
|
|
{
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
int module_load(
|
|
YR_SCAN_CONTEXT* context,
|
|
YR_OBJECT* module_object,
|
|
void* module_data,
|
|
size_t module_data_size)
|
|
{
|
|
YR_MEMORY_BLOCK* block;
|
|
YR_MEMORY_BLOCK_ITERATOR* iterator = context->iterator;
|
|
|
|
PIMAGE_NT_HEADERS32 pe_header;
|
|
const uint8_t* block_data = NULL;
|
|
PE* pe = NULL;
|
|
|
|
yr_set_integer(IMPORT_DELAYED, module_object, "IMPORT_DELAYED");
|
|
yr_set_integer(IMPORT_STANDARD, module_object, "IMPORT_STANDARD");
|
|
yr_set_integer(IMPORT_ANY, module_object, "IMPORT_ANY");
|
|
|
|
yr_set_integer(IMAGE_FILE_MACHINE_UNKNOWN, module_object, "MACHINE_UNKNOWN");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_AM33, module_object, "MACHINE_AM33");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_AMD64, module_object, "MACHINE_AMD64");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_ARM, module_object, "MACHINE_ARM");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_ARMNT, module_object, "MACHINE_ARMNT");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_ARM64, module_object, "MACHINE_ARM64");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_EBC, module_object, "MACHINE_EBC");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_I386, module_object, "MACHINE_I386");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_IA64, module_object, "MACHINE_IA64");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_M32R, module_object, "MACHINE_M32R");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_MIPS16, module_object, "MACHINE_MIPS16");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_MIPSFPU, module_object, "MACHINE_MIPSFPU");
|
|
yr_set_integer(
|
|
IMAGE_FILE_MACHINE_MIPSFPU16, module_object, "MACHINE_MIPSFPU16");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_POWERPC, module_object, "MACHINE_POWERPC");
|
|
yr_set_integer(
|
|
IMAGE_FILE_MACHINE_POWERPCFP, module_object, "MACHINE_POWERPCFP");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_R4000, module_object, "MACHINE_R4000");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_SH3, module_object, "MACHINE_SH3");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_SH3DSP, module_object, "MACHINE_SH3DSP");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_SH4, module_object, "MACHINE_SH4");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_SH5, module_object, "MACHINE_SH5");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_THUMB, module_object, "MACHINE_THUMB");
|
|
yr_set_integer(
|
|
IMAGE_FILE_MACHINE_WCEMIPSV2, module_object, "MACHINE_WCEMIPSV2");
|
|
yr_set_integer(
|
|
IMAGE_FILE_MACHINE_TARGET_HOST, module_object, "MACHINE_TARGET_HOST");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_R3000, module_object, "MACHINE_R3000");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_R10000, module_object, "MACHINE_R10000");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_ALPHA, module_object, "MACHINE_ALPHA");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_SH3E, module_object, "MACHINE_SH3E");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_ALPHA64, module_object, "MACHINE_ALPHA64");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_AXP64, module_object, "MACHINE_AXP64");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_TRICORE, module_object, "MACHINE_TRICORE");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_CEF, module_object, "MACHINE_CEF");
|
|
yr_set_integer(IMAGE_FILE_MACHINE_CEE, module_object, "MACHINE_CEE");
|
|
|
|
yr_set_integer(IMAGE_SUBSYSTEM_UNKNOWN, module_object, "SUBSYSTEM_UNKNOWN");
|
|
yr_set_integer(IMAGE_SUBSYSTEM_NATIVE, module_object, "SUBSYSTEM_NATIVE");
|
|
yr_set_integer(
|
|
IMAGE_SUBSYSTEM_WINDOWS_GUI, module_object, "SUBSYSTEM_WINDOWS_GUI");
|
|
yr_set_integer(
|
|
IMAGE_SUBSYSTEM_WINDOWS_CUI, module_object, "SUBSYSTEM_WINDOWS_CUI");
|
|
yr_set_integer(IMAGE_SUBSYSTEM_OS2_CUI, module_object, "SUBSYSTEM_OS2_CUI");
|
|
yr_set_integer(
|
|
IMAGE_SUBSYSTEM_POSIX_CUI, module_object, "SUBSYSTEM_POSIX_CUI");
|
|
yr_set_integer(
|
|
IMAGE_SUBSYSTEM_NATIVE_WINDOWS,
|
|
module_object,
|
|
"SUBSYSTEM_NATIVE_WINDOWS");
|
|
yr_set_integer(
|
|
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI,
|
|
module_object,
|
|
"SUBSYSTEM_WINDOWS_CE_GUI");
|
|
yr_set_integer(
|
|
IMAGE_SUBSYSTEM_EFI_APPLICATION,
|
|
module_object,
|
|
"SUBSYSTEM_EFI_APPLICATION");
|
|
yr_set_integer(
|
|
IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER,
|
|
module_object,
|
|
"SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER");
|
|
yr_set_integer(
|
|
IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER,
|
|
module_object,
|
|
"SUBSYSTEM_EFI_RUNTIME_DRIVER");
|
|
yr_set_integer(
|
|
IMAGE_SUBSYSTEM_EFI_ROM_IMAGE, module_object, "SUBSYSTEM_EFI_ROM_IMAGE");
|
|
yr_set_integer(IMAGE_SUBSYSTEM_XBOX, module_object, "SUBSYSTEM_XBOX");
|
|
yr_set_integer(
|
|
IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION,
|
|
module_object,
|
|
"SUBSYSTEM_WINDOWS_BOOT_APPLICATION");
|
|
|
|
yr_set_integer(
|
|
IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA,
|
|
module_object,
|
|
"HIGH_ENTROPY_VA");
|
|
yr_set_integer(
|
|
IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE, module_object, "DYNAMIC_BASE");
|
|
yr_set_integer(
|
|
IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY,
|
|
module_object,
|
|
"FORCE_INTEGRITY");
|
|
yr_set_integer(
|
|
IMAGE_DLLCHARACTERISTICS_NX_COMPAT, module_object, "NX_COMPAT");
|
|
yr_set_integer(
|
|
IMAGE_DLLCHARACTERISTICS_NO_ISOLATION, module_object, "NO_ISOLATION");
|
|
yr_set_integer(IMAGE_DLLCHARACTERISTICS_NO_SEH, module_object, "NO_SEH");
|
|
yr_set_integer(IMAGE_DLLCHARACTERISTICS_NO_BIND, module_object, "NO_BIND");
|
|
yr_set_integer(
|
|
IMAGE_DLLCHARACTERISTICS_APPCONTAINER, module_object, "APPCONTAINER");
|
|
yr_set_integer(
|
|
IMAGE_DLLCHARACTERISTICS_WDM_DRIVER, module_object, "WDM_DRIVER");
|
|
yr_set_integer(IMAGE_DLLCHARACTERISTICS_GUARD_CF, module_object, "GUARD_CF");
|
|
yr_set_integer(
|
|
IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE,
|
|
module_object,
|
|
"TERMINAL_SERVER_AWARE");
|
|
|
|
yr_set_integer(IMAGE_FILE_RELOCS_STRIPPED, module_object, "RELOCS_STRIPPED");
|
|
yr_set_integer(
|
|
IMAGE_FILE_EXECUTABLE_IMAGE, module_object, "EXECUTABLE_IMAGE");
|
|
yr_set_integer(
|
|
IMAGE_FILE_LINE_NUMS_STRIPPED, module_object, "LINE_NUMS_STRIPPED");
|
|
yr_set_integer(
|
|
IMAGE_FILE_LOCAL_SYMS_STRIPPED, module_object, "LOCAL_SYMS_STRIPPED");
|
|
yr_set_integer(
|
|
IMAGE_FILE_AGGRESIVE_WS_TRIM, module_object, "AGGRESIVE_WS_TRIM");
|
|
yr_set_integer(
|
|
IMAGE_FILE_LARGE_ADDRESS_AWARE, module_object, "LARGE_ADDRESS_AWARE");
|
|
yr_set_integer(
|
|
IMAGE_FILE_BYTES_REVERSED_LO, module_object, "BYTES_REVERSED_LO");
|
|
yr_set_integer(IMAGE_FILE_32BIT_MACHINE, module_object, "MACHINE_32BIT");
|
|
yr_set_integer(IMAGE_FILE_DEBUG_STRIPPED, module_object, "DEBUG_STRIPPED");
|
|
yr_set_integer(
|
|
IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP,
|
|
module_object,
|
|
"REMOVABLE_RUN_FROM_SWAP");
|
|
yr_set_integer(
|
|
IMAGE_FILE_NET_RUN_FROM_SWAP, module_object, "NET_RUN_FROM_SWAP");
|
|
yr_set_integer(IMAGE_FILE_SYSTEM, module_object, "SYSTEM");
|
|
yr_set_integer(IMAGE_FILE_DLL, module_object, "DLL");
|
|
yr_set_integer(IMAGE_FILE_UP_SYSTEM_ONLY, module_object, "UP_SYSTEM_ONLY");
|
|
yr_set_integer(
|
|
IMAGE_FILE_BYTES_REVERSED_HI, module_object, "BYTES_REVERSED_HI");
|
|
|
|
yr_set_integer(
|
|
IMAGE_DIRECTORY_ENTRY_EXPORT,
|
|
module_object,
|
|
"IMAGE_DIRECTORY_ENTRY_EXPORT");
|
|
yr_set_integer(
|
|
IMAGE_DIRECTORY_ENTRY_IMPORT,
|
|
module_object,
|
|
"IMAGE_DIRECTORY_ENTRY_IMPORT");
|
|
yr_set_integer(
|
|
IMAGE_DIRECTORY_ENTRY_RESOURCE,
|
|
module_object,
|
|
"IMAGE_DIRECTORY_ENTRY_RESOURCE");
|
|
yr_set_integer(
|
|
IMAGE_DIRECTORY_ENTRY_EXCEPTION,
|
|
module_object,
|
|
"IMAGE_DIRECTORY_ENTRY_EXCEPTION");
|
|
yr_set_integer(
|
|
IMAGE_DIRECTORY_ENTRY_SECURITY,
|
|
module_object,
|
|
"IMAGE_DIRECTORY_ENTRY_SECURITY");
|
|
yr_set_integer(
|
|
IMAGE_DIRECTORY_ENTRY_BASERELOC,
|
|
module_object,
|
|
"IMAGE_DIRECTORY_ENTRY_BASERELOC");
|
|
yr_set_integer(
|
|
IMAGE_DIRECTORY_ENTRY_DEBUG,
|
|
module_object,
|
|
"IMAGE_DIRECTORY_ENTRY_DEBUG");
|
|
yr_set_integer(
|
|
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE,
|
|
module_object,
|
|
"IMAGE_DIRECTORY_ENTRY_ARCHITECTURE");
|
|
yr_set_integer(
|
|
IMAGE_DIRECTORY_ENTRY_COPYRIGHT,
|
|
module_object,
|
|
"IMAGE_DIRECTORY_ENTRY_COPYRIGHT");
|
|
yr_set_integer(
|
|
IMAGE_DIRECTORY_ENTRY_GLOBALPTR,
|
|
module_object,
|
|
"IMAGE_DIRECTORY_ENTRY_GLOBALPTR");
|
|
yr_set_integer(
|
|
IMAGE_DIRECTORY_ENTRY_TLS, module_object, "IMAGE_DIRECTORY_ENTRY_TLS");
|
|
yr_set_integer(
|
|
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG,
|
|
module_object,
|
|
"IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG");
|
|
yr_set_integer(
|
|
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT,
|
|
module_object,
|
|
"IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT");
|
|
yr_set_integer(
|
|
IMAGE_DIRECTORY_ENTRY_IAT, module_object, "IMAGE_DIRECTORY_ENTRY_IAT");
|
|
yr_set_integer(
|
|
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT,
|
|
module_object,
|
|
"IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT");
|
|
yr_set_integer(
|
|
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR,
|
|
module_object,
|
|
"IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR");
|
|
|
|
yr_set_integer(
|
|
IMAGE_NT_OPTIONAL_HDR32_MAGIC,
|
|
module_object,
|
|
"IMAGE_NT_OPTIONAL_HDR32_MAGIC");
|
|
yr_set_integer(
|
|
IMAGE_NT_OPTIONAL_HDR64_MAGIC,
|
|
module_object,
|
|
"IMAGE_NT_OPTIONAL_HDR64_MAGIC");
|
|
yr_set_integer(
|
|
IMAGE_ROM_OPTIONAL_HDR_MAGIC,
|
|
module_object,
|
|
"IMAGE_ROM_OPTIONAL_HDR_MAGIC");
|
|
|
|
yr_set_integer(IMAGE_SCN_TYPE_NO_PAD, module_object, "SECTION_NO_PAD");
|
|
yr_set_integer(IMAGE_SCN_CNT_CODE, module_object, "SECTION_CNT_CODE");
|
|
yr_set_integer(
|
|
IMAGE_SCN_CNT_INITIALIZED_DATA,
|
|
module_object,
|
|
"SECTION_CNT_INITIALIZED_DATA");
|
|
yr_set_integer(
|
|
IMAGE_SCN_CNT_UNINITIALIZED_DATA,
|
|
module_object,
|
|
"SECTION_CNT_UNINITIALIZED_DATA");
|
|
yr_set_integer(IMAGE_SCN_LNK_OTHER, module_object, "SECTION_LNK_OTHER");
|
|
yr_set_integer(IMAGE_SCN_LNK_INFO, module_object, "SECTION_LNK_INFO");
|
|
yr_set_integer(IMAGE_SCN_LNK_REMOVE, module_object, "SECTION_LNK_REMOVE");
|
|
yr_set_integer(IMAGE_SCN_LNK_COMDAT, module_object, "SECTION_LNK_COMDAT");
|
|
yr_set_integer(
|
|
IMAGE_SCN_NO_DEFER_SPEC_EXC, module_object, "SECTION_NO_DEFER_SPEC_EXC");
|
|
yr_set_integer(IMAGE_SCN_GPREL, module_object, "SECTION_GPREL");
|
|
yr_set_integer(IMAGE_SCN_MEM_FARDATA, module_object, "SECTION_MEM_FARDATA");
|
|
yr_set_integer(
|
|
IMAGE_SCN_MEM_PURGEABLE, module_object, "SECTION_MEM_PURGEABLE");
|
|
yr_set_integer(IMAGE_SCN_MEM_16BIT, module_object, "SECTION_MEM_16BIT");
|
|
yr_set_integer(IMAGE_SCN_MEM_LOCKED, module_object, "SECTION_MEM_LOCKED");
|
|
yr_set_integer(IMAGE_SCN_MEM_PRELOAD, module_object, "SECTION_MEM_PRELOAD");
|
|
yr_set_integer(IMAGE_SCN_ALIGN_1BYTES, module_object, "SECTION_ALIGN_1BYTES");
|
|
yr_set_integer(IMAGE_SCN_ALIGN_2BYTES, module_object, "SECTION_ALIGN_2BYTES");
|
|
yr_set_integer(IMAGE_SCN_ALIGN_4BYTES, module_object, "SECTION_ALIGN_4BYTES");
|
|
yr_set_integer(IMAGE_SCN_ALIGN_8BYTES, module_object, "SECTION_ALIGN_8BYTES");
|
|
yr_set_integer(
|
|
IMAGE_SCN_ALIGN_16BYTES, module_object, "SECTION_ALIGN_16BYTES");
|
|
yr_set_integer(
|
|
IMAGE_SCN_ALIGN_32BYTES, module_object, "SECTION_ALIGN_32BYTES");
|
|
yr_set_integer(
|
|
IMAGE_SCN_ALIGN_64BYTES, module_object, "SECTION_ALIGN_64BYTES");
|
|
yr_set_integer(
|
|
IMAGE_SCN_ALIGN_128BYTES, module_object, "SECTION_ALIGN_128BYTES");
|
|
yr_set_integer(
|
|
IMAGE_SCN_ALIGN_256BYTES, module_object, "SECTION_ALIGN_256BYTES");
|
|
yr_set_integer(
|
|
IMAGE_SCN_ALIGN_512BYTES, module_object, "SECTION_ALIGN_512BYTES");
|
|
yr_set_integer(
|
|
IMAGE_SCN_ALIGN_1024BYTES, module_object, "SECTION_ALIGN_1024BYTES");
|
|
yr_set_integer(
|
|
IMAGE_SCN_ALIGN_2048BYTES, module_object, "SECTION_ALIGN_2048BYTES");
|
|
yr_set_integer(
|
|
IMAGE_SCN_ALIGN_4096BYTES, module_object, "SECTION_ALIGN_4096BYTES");
|
|
yr_set_integer(
|
|
IMAGE_SCN_ALIGN_8192BYTES, module_object, "SECTION_ALIGN_8192BYTES");
|
|
yr_set_integer(IMAGE_SCN_ALIGN_MASK, module_object, "SECTION_ALIGN_MASK");
|
|
yr_set_integer(
|
|
IMAGE_SCN_LNK_NRELOC_OVFL, module_object, "SECTION_LNK_NRELOC_OVFL");
|
|
yr_set_integer(
|
|
IMAGE_SCN_MEM_DISCARDABLE, module_object, "SECTION_MEM_DISCARDABLE");
|
|
yr_set_integer(
|
|
IMAGE_SCN_MEM_NOT_CACHED, module_object, "SECTION_MEM_NOT_CACHED");
|
|
yr_set_integer(
|
|
IMAGE_SCN_MEM_NOT_PAGED, module_object, "SECTION_MEM_NOT_PAGED");
|
|
yr_set_integer(IMAGE_SCN_MEM_SHARED, module_object, "SECTION_MEM_SHARED");
|
|
yr_set_integer(IMAGE_SCN_MEM_EXECUTE, module_object, "SECTION_MEM_EXECUTE");
|
|
yr_set_integer(IMAGE_SCN_MEM_READ, module_object, "SECTION_MEM_READ");
|
|
yr_set_integer(IMAGE_SCN_MEM_WRITE, module_object, "SECTION_MEM_WRITE");
|
|
yr_set_integer(IMAGE_SCN_SCALE_INDEX, module_object, "SECTION_SCALE_INDEX");
|
|
|
|
yr_set_integer(RESOURCE_TYPE_CURSOR, module_object, "RESOURCE_TYPE_CURSOR");
|
|
yr_set_integer(RESOURCE_TYPE_BITMAP, module_object, "RESOURCE_TYPE_BITMAP");
|
|
yr_set_integer(RESOURCE_TYPE_ICON, module_object, "RESOURCE_TYPE_ICON");
|
|
yr_set_integer(RESOURCE_TYPE_MENU, module_object, "RESOURCE_TYPE_MENU");
|
|
yr_set_integer(RESOURCE_TYPE_DIALOG, module_object, "RESOURCE_TYPE_DIALOG");
|
|
yr_set_integer(RESOURCE_TYPE_STRING, module_object, "RESOURCE_TYPE_STRING");
|
|
yr_set_integer(RESOURCE_TYPE_FONTDIR, module_object, "RESOURCE_TYPE_FONTDIR");
|
|
yr_set_integer(RESOURCE_TYPE_FONT, module_object, "RESOURCE_TYPE_FONT");
|
|
yr_set_integer(
|
|
RESOURCE_TYPE_ACCELERATOR, module_object, "RESOURCE_TYPE_ACCELERATOR");
|
|
yr_set_integer(RESOURCE_TYPE_RCDATA, module_object, "RESOURCE_TYPE_RCDATA");
|
|
yr_set_integer(
|
|
RESOURCE_TYPE_MESSAGETABLE, module_object, "RESOURCE_TYPE_MESSAGETABLE");
|
|
yr_set_integer(
|
|
RESOURCE_TYPE_GROUP_CURSOR, module_object, "RESOURCE_TYPE_GROUP_CURSOR");
|
|
yr_set_integer(
|
|
RESOURCE_TYPE_GROUP_ICON, module_object, "RESOURCE_TYPE_GROUP_ICON");
|
|
yr_set_integer(RESOURCE_TYPE_VERSION, module_object, "RESOURCE_TYPE_VERSION");
|
|
yr_set_integer(
|
|
RESOURCE_TYPE_DLGINCLUDE, module_object, "RESOURCE_TYPE_DLGINCLUDE");
|
|
yr_set_integer(
|
|
RESOURCE_TYPE_PLUGPLAY, module_object, "RESOURCE_TYPE_PLUGPLAY");
|
|
yr_set_integer(RESOURCE_TYPE_VXD, module_object, "RESOURCE_TYPE_VXD");
|
|
yr_set_integer(
|
|
RESOURCE_TYPE_ANICURSOR, module_object, "RESOURCE_TYPE_ANICURSOR");
|
|
yr_set_integer(RESOURCE_TYPE_ANIICON, module_object, "RESOURCE_TYPE_ANIICON");
|
|
yr_set_integer(RESOURCE_TYPE_HTML, module_object, "RESOURCE_TYPE_HTML");
|
|
yr_set_integer(
|
|
RESOURCE_TYPE_MANIFEST, module_object, "RESOURCE_TYPE_MANIFEST");
|
|
|
|
yr_set_integer(
|
|
IMAGE_DEBUG_TYPE_UNKNOWN, module_object, "IMAGE_DEBUG_TYPE_UNKNOWN");
|
|
yr_set_integer(IMAGE_DEBUG_TYPE_COFF, module_object, "IMAGE_DEBUG_TYPE_COFF");
|
|
yr_set_integer(
|
|
IMAGE_DEBUG_TYPE_CODEVIEW, module_object, "IMAGE_DEBUG_TYPE_CODEVIEW");
|
|
yr_set_integer(IMAGE_DEBUG_TYPE_FPO, module_object, "IMAGE_DEBUG_TYPE_FPO");
|
|
yr_set_integer(IMAGE_DEBUG_TYPE_MISC, module_object, "IMAGE_DEBUG_TYPE_MISC");
|
|
yr_set_integer(
|
|
IMAGE_DEBUG_TYPE_EXCEPTION, module_object, "IMAGE_DEBUG_TYPE_EXCEPTION");
|
|
yr_set_integer(
|
|
IMAGE_DEBUG_TYPE_FIXUP, module_object, "IMAGE_DEBUG_TYPE_FIXUP");
|
|
yr_set_integer(
|
|
IMAGE_DEBUG_TYPE_OMAP_TO_SRC,
|
|
module_object,
|
|
"IMAGE_DEBUG_TYPE_OMAP_TO_SRC");
|
|
yr_set_integer(
|
|
IMAGE_DEBUG_TYPE_OMAP_FROM_SRC,
|
|
module_object,
|
|
"IMAGE_DEBUG_TYPE_OMAP_FROM_SRC");
|
|
yr_set_integer(
|
|
IMAGE_DEBUG_TYPE_BORLAND, module_object, "IMAGE_DEBUG_TYPE_BORLAND");
|
|
yr_set_integer(
|
|
IMAGE_DEBUG_TYPE_RESERVED10,
|
|
module_object,
|
|
"IMAGE_DEBUG_TYPE_RESERVED10");
|
|
yr_set_integer(
|
|
IMAGE_DEBUG_TYPE_CLSID, module_object, "IMAGE_DEBUG_TYPE_CLSID");
|
|
yr_set_integer(
|
|
IMAGE_DEBUG_TYPE_VC_FEATURE,
|
|
module_object,
|
|
"IMAGE_DEBUG_TYPE_VC_FEATURE");
|
|
yr_set_integer(IMAGE_DEBUG_TYPE_POGO, module_object, "IMAGE_DEBUG_TYPE_POGO");
|
|
yr_set_integer(
|
|
IMAGE_DEBUG_TYPE_ILTCG, module_object, "IMAGE_DEBUG_TYPE_ILTCG");
|
|
yr_set_integer(IMAGE_DEBUG_TYPE_MPX, module_object, "IMAGE_DEBUG_TYPE_MPX");
|
|
yr_set_integer(
|
|
IMAGE_DEBUG_TYPE_REPRO, module_object, "IMAGE_DEBUG_TYPE_REPRO");
|
|
|
|
yr_set_integer(0, module_object, "is_pe");
|
|
|
|
foreach_memory_block(iterator, block)
|
|
{
|
|
block_data = yr_fetch_block_data(block);
|
|
|
|
if (block_data == NULL)
|
|
continue;
|
|
|
|
pe_header = pe_get_header(block_data, block->size);
|
|
|
|
if (pe_header != NULL)
|
|
{
|
|
// Ignore DLLs while scanning a process
|
|
|
|
if (!(context->flags & SCAN_FLAGS_PROCESS_MEMORY) ||
|
|
!(yr_le16toh(pe_header->FileHeader.Characteristics) & IMAGE_FILE_DLL))
|
|
{
|
|
pe = (PE*) yr_malloc(sizeof(PE));
|
|
|
|
if (pe == NULL)
|
|
return ERROR_INSUFFICIENT_MEMORY;
|
|
|
|
FAIL_ON_ERROR_WITH_CLEANUP(
|
|
yr_hash_table_create(17, &pe->hash_table), yr_free(pe));
|
|
|
|
pe->data = block_data;
|
|
pe->data_size = block->size;
|
|
pe->header = pe_header;
|
|
pe->object = module_object;
|
|
pe->resources = 0;
|
|
pe->version_infos = 0;
|
|
|
|
module_object->data = pe;
|
|
|
|
pe_parse_header(pe, block->base, context->flags);
|
|
pe_parse_rich_signature(pe, block->base);
|
|
pe_parse_debug_directory(pe);
|
|
|
|
#if defined(HAVE_LIBCRYPTO) && !defined(BORINGSSL)
|
|
pe_parse_certificates(pe);
|
|
#endif
|
|
|
|
pe->imported_dlls = pe_parse_imports(pe);
|
|
pe->delay_imported_dlls = pe_parse_delayed_imports(pe);
|
|
pe_parse_exports(pe);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
void free_dlls(IMPORTED_DLL* dll)
|
|
{
|
|
IMPORTED_DLL* next_dll = NULL;
|
|
IMPORT_FUNCTION* func = NULL;
|
|
IMPORT_FUNCTION* next_func = NULL;
|
|
|
|
while (dll)
|
|
{
|
|
if (dll->name)
|
|
yr_free(dll->name);
|
|
|
|
func = dll->functions;
|
|
|
|
while (func)
|
|
{
|
|
if (func->name)
|
|
yr_free(func->name);
|
|
|
|
next_func = func->next;
|
|
yr_free(func);
|
|
func = next_func;
|
|
}
|
|
|
|
next_dll = dll->next;
|
|
yr_free(dll);
|
|
dll = next_dll;
|
|
}
|
|
}
|
|
|
|
int module_unload(YR_OBJECT* module_object)
|
|
{
|
|
PE* pe = (PE*) module_object->data;
|
|
|
|
if (pe == NULL)
|
|
return ERROR_SUCCESS;
|
|
|
|
if (pe->hash_table != NULL)
|
|
yr_hash_table_destroy(
|
|
pe->hash_table, (YR_HASH_TABLE_FREE_VALUE_FUNC) yr_free);
|
|
|
|
free_dlls(pe->imported_dlls);
|
|
free_dlls(pe->delay_imported_dlls);
|
|
|
|
yr_free(pe);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|