Skip to content

Commit

Permalink
Add parsing of '/N' section names (VirusTotal#1560)
Browse files Browse the repository at this point in the history
  • Loading branch information
xbabka01 authored Sep 17, 2021
1 parent e718016 commit 5cc28d2
Show file tree
Hide file tree
Showing 6 changed files with 277 additions and 0 deletions.
10 changes: 10 additions & 0 deletions docs/modules/pe.rst
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,16 @@ Reference
Section name.

.. c:member:: full_name
If the name in the section table contains a slash (/) followed by
a representation of the decimal number in ASCII format, then this field
contains a string from the specified offset in the string table.
Otherwise, this field contains the same value as a name field.

Even though it's not a standard, MinGW and Cygwin compilers use this
feature to store section names which are longer than 8 characters.

.. c:member:: characteristics
Section characteristics.
Expand Down
169 changes: 169 additions & 0 deletions libyara/include/yara/pe.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
typedef uint8_t BYTE;
typedef uint16_t WORD;
typedef uint16_t WCHAR;
typedef int16_t SHORT;
typedef uint32_t DWORD;
typedef int32_t LONG;
typedef uint32_t ULONG;
Expand Down Expand Up @@ -604,6 +605,174 @@ typedef struct _IMAGE_DEBUG_DIRECTORY

#pragma pack(pop)

//
// Symbol format.
//

typedef struct _IMAGE_SYMBOL
{
union
{
BYTE ShortName[8];
struct
{
DWORD Short; // if 0, use LongName
DWORD Long; // offset into string table
} Name;
DWORD LongName[2]; // PBYTE [2]
} N;
DWORD Value;
SHORT SectionNumber;
WORD Type;
BYTE StorageClass;
BYTE NumberOfAuxSymbols;
} IMAGE_SYMBOL, *PIMAGE_SYMBOL;

#define IMAGE_SIZEOF_SYMBOL 18

typedef struct _IMAGE_SYMBOL_EX
{
union
{
BYTE ShortName[8];
struct
{
DWORD Short; // if 0, use LongName
DWORD Long; // offset into string table
} Name;
DWORD LongName[2]; // PBYTE [2]
} N;
DWORD Value;
LONG SectionNumber;
WORD Type;
BYTE StorageClass;
BYTE NumberOfAuxSymbols;
} IMAGE_SYMBOL_EX, *PIMAGE_SYMBOL_EX;

//
// Section values.
//
// Symbols have a section number of the section in which they are
// defined. Otherwise, section numbers have the following meanings:
//

#define IMAGE_SYM_UNDEFINED (SHORT) 0 // Symbol is undefined or is common.
#define IMAGE_SYM_ABSOLUTE (SHORT) - 1 // Symbol is an absolute value.
#define IMAGE_SYM_DEBUG (SHORT) - 2 // Symbol is a special debug item.
#define IMAGE_SYM_SECTION_MAX 0xFEFF // Values 0xFF00-0xFFFF are special
#define IMAGE_SYM_SECTION_MAX_EX MAXLONG

//
// Type (fundamental) values.
//

#define IMAGE_SYM_TYPE_NULL 0x0000 // no type.
#define IMAGE_SYM_TYPE_VOID 0x0001 //
#define IMAGE_SYM_TYPE_CHAR 0x0002 // type character.
#define IMAGE_SYM_TYPE_SHORT 0x0003 // type short integer.
#define IMAGE_SYM_TYPE_INT 0x0004 //
#define IMAGE_SYM_TYPE_LONG 0x0005 //
#define IMAGE_SYM_TYPE_FLOAT 0x0006 //
#define IMAGE_SYM_TYPE_DOUBLE 0x0007 //
#define IMAGE_SYM_TYPE_STRUCT 0x0008 //
#define IMAGE_SYM_TYPE_UNION 0x0009 //
#define IMAGE_SYM_TYPE_ENUM 0x000A // enumeration.
#define IMAGE_SYM_TYPE_MOE 0x000B // member of enumeration.
#define IMAGE_SYM_TYPE_BYTE 0x000C //
#define IMAGE_SYM_TYPE_WORD 0x000D //
#define IMAGE_SYM_TYPE_UINT 0x000E //
#define IMAGE_SYM_TYPE_DWORD 0x000F //
#define IMAGE_SYM_TYPE_PCODE 0x8000 //
//
// Type (derived) values.
//

#define IMAGE_SYM_DTYPE_NULL 0 // no derived type.
#define IMAGE_SYM_DTYPE_POINTER 1 // pointer.
#define IMAGE_SYM_DTYPE_FUNCTION 2 // function.
#define IMAGE_SYM_DTYPE_ARRAY 3 // array.

//
// Storage classes.
//
#define IMAGE_SYM_CLASS_END_OF_FUNCTION (BYTE) - 1
#define IMAGE_SYM_CLASS_NULL 0x0000
#define IMAGE_SYM_CLASS_AUTOMATIC 0x0001
#define IMAGE_SYM_CLASS_EXTERNAL 0x0002
#define IMAGE_SYM_CLASS_STATIC 0x0003
#define IMAGE_SYM_CLASS_REGISTER 0x0004
#define IMAGE_SYM_CLASS_EXTERNAL_DEF 0x0005
#define IMAGE_SYM_CLASS_LABEL 0x0006
#define IMAGE_SYM_CLASS_UNDEFINED_LABEL 0x0007
#define IMAGE_SYM_CLASS_MEMBER_OF_STRUCT 0x0008
#define IMAGE_SYM_CLASS_ARGUMENT 0x0009
#define IMAGE_SYM_CLASS_STRUCT_TAG 0x000A
#define IMAGE_SYM_CLASS_MEMBER_OF_UNION 0x000B
#define IMAGE_SYM_CLASS_UNION_TAG 0x000C
#define IMAGE_SYM_CLASS_TYPE_DEFINITION 0x000D
#define IMAGE_SYM_CLASS_UNDEFINED_STATIC 0x000E
#define IMAGE_SYM_CLASS_ENUM_TAG 0x000F
#define IMAGE_SYM_CLASS_MEMBER_OF_ENUM 0x0010
#define IMAGE_SYM_CLASS_REGISTER_PARAM 0x0011
#define IMAGE_SYM_CLASS_BIT_FIELD 0x0012

#define IMAGE_SYM_CLASS_FAR_EXTERNAL 0x0044 //

#define IMAGE_SYM_CLASS_BLOCK 0x0064
#define IMAGE_SYM_CLASS_FUNCTION 0x0065
#define IMAGE_SYM_CLASS_END_OF_STRUCT 0x0066
#define IMAGE_SYM_CLASS_FILE 0x0067
// new
#define IMAGE_SYM_CLASS_SECTION 0x0068
#define IMAGE_SYM_CLASS_WEAK_EXTERNAL 0x0069

#define IMAGE_SYM_CLASS_CLR_TOKEN 0x006B

// type packing constants

#define N_BTMASK 0x000F
#define N_TMASK 0x0030
#define N_TMASK1 0x00C0
#define N_TMASK2 0x00F0
#define N_BTSHFT 4
#define N_TSHIFT 2
// MACROS

// Basic Type of x
#define BTYPE(x) ((x) &N_BTMASK)

// Is x a pointer?
#ifndef ISPTR
#define ISPTR(x) (((x) &N_TMASK) == (IMAGE_SYM_DTYPE_POINTER << N_BTSHFT))
#endif

// Is x a function?
#ifndef ISFCN
#define ISFCN(x) (((x) &N_TMASK) == (IMAGE_SYM_DTYPE_FUNCTION << N_BTSHFT))
#endif

// Is x an array?

#ifndef ISARY
#define ISARY(x) (((x) &N_TMASK) == (IMAGE_SYM_DTYPE_ARRAY << N_BTSHFT))
#endif

// Is x a structure, union, or enumeration TAG?
#ifndef ISTAG
#define ISTAG(x) \
((x) == IMAGE_SYM_CLASS_STRUCT_TAG || (x) == IMAGE_SYM_CLASS_UNION_TAG || \
(x) == IMAGE_SYM_CLASS_ENUM_TAG)
#endif

#ifndef INCREF
#define INCREF(x) \
((((x) & ~N_BTMASK) << N_TSHIFT) | (IMAGE_SYM_DTYPE_POINTER << N_BTSHFT) | \
((x) &N_BTMASK))
#endif
#ifndef DECREF
#define DECREF(x) ((((x) >> N_TSHIFT) & ~N_BTMASK) | ((x) &N_BTMASK))
#endif

#endif // _WIN32

#define CVINFO_PDB70_CVSIGNATURE 0x53445352 // "RSDS"
Expand Down
76 changes: 76 additions & 0 deletions libyara/modules/pe/pe.c
Original file line number Diff line number Diff line change
Expand Up @@ -1895,6 +1895,70 @@ static void pe_parse_certificates(PE* pe)

#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++)
{
// Valid string
if (string[len] == 0)
{
*section_full_name_length = len;
return string;
}

// string contain unprintable character
if (!isprint(string[len]))
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;
Expand Down Expand Up @@ -2147,13 +2211,24 @@ static void pe_parse_header(PE* pe, uint64_t base_address, int flags)
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, &sect_full_name_length);

set_sized_string(
(char*) section_name,
sect_name_length + 1,
pe->object,
"sections[%i].name",
i);

set_sized_string(
full_section_name,
sect_full_name_length,
pe->object,
"sections[%i].full_name",
i);

set_integer(
yr_le32toh(section->Characteristics),
pe->object,
Expand Down Expand Up @@ -3373,6 +3448,7 @@ begin_declarations

begin_struct_array("sections")
declare_string("name");
declare_string("full_name");
declare_integer("characteristics");
declare_integer("virtual_address");
declare_integer("virtual_size");
Expand Down
1 change: 1 addition & 0 deletions tests/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ cc_test(
"data/mtxex.dll",
"data/mtxex_modified_rsrc_rva.dll",
"data/pe_imports",
"data/pe_mingw",
"data/tiny",
"data/tiny-idata-51ff",
"data/tiny-idata-5200",
Expand Down
Binary file added tests/data/pe_mingw
Binary file not shown.
21 changes: 21 additions & 0 deletions tests/test-pe.c
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,27 @@ int main(int argc, char** argv)
"tests/data/ca21e1c32065352d352be6cde97f89c141d7737ea92434831f998080783d5386");


assert_true_rule_file(
"import \"pe\" \
rule section_name_comparison { \
condition: \
for all section in pe.sections : ( \
section.name == section.full_name \
)\
}",
"tests/data/tiny");

assert_true_rule_file(
"import \"pe\" \
rule section_name_comparison { \
condition: \
for any section in pe.sections : ( \
section.name == \"/4\" and\
section.full_name == \".debug_aranges\" \
)\
}",
"tests/data/pe_mingw");

yr_finalize();

YR_DEBUG_FPRINTF(
Expand Down

0 comments on commit 5cc28d2

Please sign in to comment.