Add parsing of .NET user types from .NET metadata stream (#1605)

* Add simple_str string implementation

* Add ability to parse .NET user defined types to 'dotnet' module

* Add copyright information

* Unify comment style, define iter variable within for loop

* Fix invalid handling of corrupted parameters

* Remove duplicate test_dotnet in BUILD.bazel

* Fix unaligned memory access. (#1)

* Fix unaligned memory access.

* Add missing comma in Bazel file.

Co-authored-by: Victor M. Alvarez <[email protected]>
This commit is contained in:
HoundThe
2022-06-23 12:36:22 +02:00
committed by GitHub
co-authored by Victor M. Alvarez
parent 77a57a1ba3
commit 15ec337e04
7 changed files with 2153 additions and 131 deletions
+105
View File
@@ -77,6 +77,111 @@ Reference
*Example: dotnet.guids[0] == "99c08ffd-f378-a891-10ab-c02fe11be6ef"*
.. c:type:: number_of_classes
The number of classes in the file.
.. c:type:: classes
An array of .NET classes stored in the metadata. Individual classes can be accessed
using the [] operator. Each class object contains the following attributes:
.. c:member:: fullname
Class full name.
.. c:member:: name
Class name.
.. c:member:: namespace
Class namespace.
.. c:member:: visibility
Class visibility specifier, options are:
``private``
``public``
``protected``
``internal``
``private protected``
``protected internal``
.. c:member:: type
Type of the object, options are:
``class``
``interface``
.. c:member:: abstract
Boolean representing if class is abstract.
.. c:member:: sealed
Boolean representing if class is sealed.
.. c:member:: number_of_generic_parameters
Number of generic parameters.
.. c:member:: generic_parameters
A zero-based array of generic parameters name. Individual parameters can be accessed using the [] operator.
.. c:member:: number_of_base_types
Number of the base types.
.. c:member:: base_types
A zero-based array of base types name. Individual base types can be accessed using the [] operator.
.. c:member:: number_of_methods
Number of the methods.
.. c:member:: methods
A zero-based array of method objects. Individual methods can be accessed by
using the [] operator. Each object contains following attributes:
.. c:member:: name
Method name.
.. c:member:: visibility
Method visibility specifier, options are:
``private``
``public``
``protected``
``internal``
``private protected``
``protected internal``
.. c:member:: static
Boolean representing if method is static.
.. c:member:: virtual
Boolean representing if method is virtual.
.. c:member:: final
Boolean representing if method is final.
.. c:member:: abstract
Boolean representing if method is abstract.
.. c:member:: return_type
Method return type name.
.. c:member:: number_of_parameters
Number of the method parameters.
.. c:member:: parameters
A zero-based array of method parameters. Individual parameters can be accessed by using the [] operator.
.. c:member:: name
Parameter name.
.. c:member:: type
Parameter type.
.. c:member:: number_of_generic_parameters
Number of the method generic parameters.
.. c:member:: generic_parameters
A zero-based array of method generic parameters. Individual parameters can be accessed by using the [] operator.
*Example: dotnet.classes[0].fullname == "Launcher.Program"*
.. c:type:: number_of_resources
The number of resources in the .NET file. These are different from normal PE
+206 -8
View File
@@ -3,6 +3,7 @@
#include <yara/pe.h>
#include <yara/pe_utils.h>
#include <yara/types.h>
#pragma pack(push, 1)
@@ -72,6 +73,10 @@ typedef struct _TILDE_HEADER
ULONGLONG Sorted;
} TILDE_HEADER, *PTILDE_HEADER;
// flag in HeapSizes that denotes extra 4 bytes after Rows
#define HEAP_EXTRA_DATA 0x40
#define SIG_FLAG_GENERIC 0x10
// These are the bit positions in Valid which will be set if the table
// exists.
#define BIT_MODULE 0x00
@@ -129,16 +134,86 @@ typedef struct _TILDE_HEADER
//#define BIT_IMPORTSCOPE 0x35
//#define BIT_STATEMACHINEMETHOD 0x36
//
// Element types. Note this is not a complete list as we aren't parsing all of
// them. This only includes the ones we care about.
// ECMA-335 Section II.23.1.16
//
#define ELEMENT_TYPE_STRING 0x0E
// The string length of a typelib attribute is at most 0xFF.
#define MAX_TYPELIB_SIZE 0xFF
// Flags and Masks for .NET tables
#define TYPE_ATTR_CLASS_SEMANTIC_MASK 0x20
#define TYPE_ATTR_CLASS 0x0
#define TYPE_ATTR_INTERFACE 0x20
#define TYPE_ATTR_VISIBILITY_MASK 0x7
#define TYPE_ATTR_NOT_PUBLIC 0x0
#define TYPE_ATTR_PUBLIC 0x1
#define TYPE_ATTR_NESTED_PUBLIC 0x2
#define TYPE_ATTR_NESTED_PRIVATE 0x3
#define TYPE_ATTR_NESTED_FAMILY 0x4
#define TYPE_ATTR_NESTED_ASSEMBLY 0x5
#define TYPE_ATTR_NESTED_FAM_AND_ASSEM 0x6
#define TYPE_ATTR_NESTED_FAM_OR_ASSEM 0x7
#define TYPE_ATTR_ABSTRACT 0x80
#define TYPE_ATTR_SEALED 0x100
#define METHOD_ATTR_ACCESS_MASK 0x7
#define METHOD_ATTR_PRIVATE 0x1
#define METHOD_ATTR_FAM_AND_ASSEM 0x2
#define METHOD_ATTR_ASSEM 0x3
#define METHOD_ATTR_FAMILY 0x4
#define METHOD_ATTR_FAM_OR_ASSEM 0x5
#define METHOD_ATTR_PUBLIC 0x6
#define METHOD_ATTR_STATIC 0x10
#define METHOD_ATTR_FINAL 0x20
#define METHOD_ATTR_VIRTUAL 0x40
#define METHOD_ATTR_ABSTRACT 0x400
// Element types ECMA-335 Section II.23.1.16
#define TYPE_END 0x0
#define TYPE_VOID 0x1
#define TYPE_BOOL 0x2
#define TYPE_CHAR 0x3
#define TYPE_I1 0x4
#define TYPE_U1 0x5
#define TYPE_I2 0x6
#define TYPE_U2 0x7
#define TYPE_I4 0x8
#define TYPE_U4 0x9
#define TYPE_I8 0xa
#define TYPE_U8 0xb
#define TYPE_R4 0xc
#define TYPE_R8 0xd
#define TYPE_STRING 0xe
#define TYPE_PTR 0xf
#define TYPE_BYREF 0x10
#define TYPE_VALUETYPE 0x11
#define TYPE_CLASS 0x12
#define TYPE_VAR 0x13
#define TYPE_ARRAY 0x14
#define TYPE_GENERICINST 0x15
#define TYPE_TYPEDREF 0x16
#define TYPE_I 0x18
#define TYPE_U 0x19
#define TYPE_FNPTR 0x1b
#define TYPE_OBJECT 0x1c
#define TYPE_SZARRAY 0x1d
#define TYPE_MVAR 0x1e
#define TYPE_CMOD_REQD 0x1f
#define TYPE_CMOD_OPT 0x20
#define TYPE_INTERNAL 0x21
#define TYPE_MODIFIER 0x40
#define TYPE_SENTINEL 0x41
#define TYPE_PINNED 0x45
// Sane boundaries for invalid files
#define MAX_ARRAY_RANK 50
#define MAX_PARAM_COUNT 2000
#define MAX_GEN_PARAM_COUNT 1000
#define MAX_METHOD_COUNT 20000
#define MAX_STRING_LENGTH 10000
// Sanity check for loops in type parser
#define MAX_TYPE_DEPTH 0x10
//
// Module table
// ECMA-335 Section II.22.30
@@ -306,9 +381,84 @@ typedef struct _CONSTANT_TABLE
} Value;
} CONSTANT_TABLE, *PCONSTANT_TABLE;
// ECMA 335 - II.22.37
typedef struct _TYPEDEF_ROW
{
uint32_t Flags;
uint32_t Name;
uint32_t Namespace;
uint32_t Extends;
uint32_t Field;
uint32_t Method;
} TYPEDEF_ROW, *PTYPEDEF_ROW;
// ECMA 335 - II.22.38
typedef struct _TYPEREF_ROW
{
uint32_t ResolutionScope;
uint32_t Name;
uint32_t Namespace;
} TYPEREF_ROW, *PTYPEREF_ROW;
// ECMA 335 - II.22.39
typedef struct _TYPESPEC_ROW
{
uint32_t Signature;
} TYPESPEC_ROW, *PTYPESPEC_ROW;
// ECMA 335 - II.22.23
typedef struct _INTERFACEIMPL_ROW
{
uint32_t Class;
uint32_t Interface;
} INTERFACEIMPL_ROW, *PINTERFACEIMPL_ROW;
// ECMA 335 II.22.26
typedef struct _METHODDEF_ROW
{
uint32_t Rva;
uint16_t ImplFlags;
uint16_t Flags;
uint32_t Name;
uint32_t Signature;
uint32_t ParamList;
} METHODDEF_ROW, *PMETHODDEF_ROW;
// ECMA 335 II.22.33
typedef struct _PARAM_ROW
{
uint16_t Flags;
uint16_t Sequence;
uint32_t Name;
} PARAM_ROW, *PPARAM_ROW;
// ECMA 335 II.22.20
typedef struct _GENERICPARAM_ROW
{
uint16_t Number;
uint16_t Flags;
uint32_t Owner;
uint32_t Name;
} GENERICPARAM_ROW, *PGENERICPARAM_ROW;
// ECMA 335 II.22.32
typedef struct _NESTEDCLASS_ROW
{
uint32_t NestedClass;
uint32_t EnclosingClass;
} NESTEDCLASS_ROW, *PNESTEDCLASS_ROW;
// For easy passing of gen param collection
typedef struct _GENERIC_PARAMETERS
{
char **names;
uint32_t len;
} GENERIC_PARAMETERS, *PGENERIC_PARAMETERS;
// Used to return offsets to the various headers.
typedef struct _STREAMS
{
int64_t metadata_root; // base from which are stream offsets relative
PSTREAM_HEADER guid;
PSTREAM_HEADER tilde;
PSTREAM_HEADER string;
@@ -321,9 +471,33 @@ typedef struct _STREAMS
typedef struct _BLOB_PARSE_RESULT
{
uint8_t size; // Number of bytes parsed. This is the new offset.
DWORD length; // Value of the bytes parsed. This is the blob length.
uint32_t length; // Value of the bytes parsed. This is the blob length.
} BLOB_PARSE_RESULT, *PBLOB_PARSE_RESULT;
typedef struct _TABLE_INFO
{
uint8_t *Offset;
uint32_t RowCount;
uint32_t RowSize;
} TABLE_INFO, *PTABLE_INFO;
// Structure that stores table information for parsing
typedef struct _TABLES
{
TABLE_INFO typedef_;
TABLE_INFO typespec;
TABLE_INFO typeref;
TABLE_INFO methoddef;
TABLE_INFO param;
TABLE_INFO module;
TABLE_INFO moduleref;
TABLE_INFO assembly;
TABLE_INFO assemblyref;
TABLE_INFO intefaceimpl;
TABLE_INFO genericparam;
TABLE_INFO nestedclass;
} TABLES, *PTABLES;
// Used to store the number of rows of each table.
typedef struct _ROWS
{
@@ -359,16 +533,40 @@ typedef struct _INDEX_SIZES
uint8_t blob;
uint8_t field;
uint8_t methoddef;
uint8_t methodspec;
uint8_t memberref;
uint8_t param;
uint8_t event;
uint8_t typedef_;
uint8_t typeref;
uint8_t typespec;
uint8_t interfaceimpl;
uint8_t property;
uint8_t moduleref;
uint8_t module;
uint8_t assemblyrefprocessor;
uint8_t assemblyref;
uint8_t assembly;
uint8_t genericparam;
} INDEX_SIZES, *PINDEX_SIZES;
typedef struct _CLASS_CONTEXT
{
PE* pe;
TABLES* tables;
INDEX_SIZES* index_sizes;
const uint8_t* str_heap;
uint32_t str_size;
const uint8_t* blob_heap;
uint32_t blob_size;
} CLASS_CONTEXT, *PCLASS_CONTEXT;
typedef struct _PARAMETERS
{
char* name;
char* type;
bool alloc; // if name is allocated and needs to be free
} PARAMETERS, *PPARAMETERS;
#pragma pack(pop)
#endif
File diff suppressed because it is too large Load Diff
+29
View File
@@ -1,3 +1,32 @@
/*
Copyright (c) 2022. 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 <stdarg.h>
#include <string.h>
#include <yara/mem.h>
+4 -3
View File
@@ -114,6 +114,7 @@ cc_test(
"data/079a472d22290a94ebb212aa8015cdc8dd28a968c6b4d3b88acdd58ce2d3b885",
"data/079a472d22290a94ebb212aa8015cdc8dd28a968c6b4d3b88acdd58ce2d3b885.upx",
"data/3b8b90159fa9b6048cc5410c5d53f116943564e4d05b04a843f9b3d0540d0c1c",
"data/ChipTune.efi",
"data/ca21e1c32065352d352be6cde97f89c141d7737ea92434831f998080783d5386",
"data/mtxex.dll",
"data/mtxex_modified_rsrc_rva.dll",
@@ -122,10 +123,9 @@ cc_test(
"data/tiny",
"data/tiny-idata-51ff",
"data/tiny-idata-5200",
"data/tiny-macho",
"data/tiny-overlay",
"data/weird_rich",
"data/ChipTune.efi",
"data/tiny-macho"
],
linkstatic = True,
deps = [
@@ -239,9 +239,10 @@ cc_test(
srcs = ["test-dotnet.c"],
copts = COPTS,
data = [
"data/tiny",
"data/0ca09bde7602769120fadc4f7a4147347a7a97271370583586c9e587fd396171",
"data/756684f4017ba7e931a26724ae61606b16b5f8cc84ed38a260a34e50c5016f59",
"data/bad_dotnet_pe",
"data/tiny",
],
linkstatic = True,
deps = [
+48
View File
@@ -84,6 +84,54 @@ int main(int argc, char** argv)
"tests/data/"
"0ca09bde7602769120fadc4f7a4147347a7a97271370583586c9e587fd396171");
assert_true_rule_file(
"import \"dotnet\" \
rule test { \
condition: \
dotnet.classes[0].fullname == \"Autodesk.AutoCAD.Internal.Windows.MenuServices\" and \
dotnet.classes[0].name == \"MenuServices\" and \
dotnet.classes[0].namespace == \"Autodesk.AutoCAD.Internal.Windows\" and \
dotnet.classes[0].visibility == \"public\" and \
dotnet.classes[0].type == \"class\" and \
dotnet.classes[0].abstract and \
dotnet.classes[0].sealed and \
dotnet.classes[0].number_of_generic_parameters == 0 and \
dotnet.classes[0].number_of_base_types == 1 and \
dotnet.classes[0].base_types[0] == \"System.Object\" and \
dotnet.classes[0].number_of_methods == 20 and \
dotnet.classes[0].methods[0].name == \"Initialize\" and \
dotnet.classes[0].methods[0].visibility == \"public\" and \
dotnet.classes[0].methods[0].static and \
not dotnet.classes[0].methods[0].virtual and \
not dotnet.classes[0].methods[0].final and \
dotnet.classes[0].methods[0].return_type == \"void\" and \
dotnet.classes[0].methods[0].parameters[0].name == \"productName\" and \
dotnet.classes[0].methods[0].parameters[0].type == \"string\" and \
dotnet.classes[0].methods[0].parameters[1].name == \"maxRecentFiles\" and \
dotnet.classes[0].methods[0].parameters[1].type == \"int\" and \
dotnet.classes[0].methods[0].number_of_generic_parameters == 0 and \
dotnet.classes[156].fullname == \"Autodesk.AutoCAD.Ribbon.Point3dDoubleToStringConverter\" and \
dotnet.classes[156].name == \"Point3dDoubleToStringConverter\" and \
dotnet.classes[156].namespace == \"Autodesk.AutoCAD.Ribbon\" and \
dotnet.classes[156].number_of_base_types == 2 and \
dotnet.classes[156].base_types[0] == \"System.Object\" and \
dotnet.classes[156].base_types[1] == \"System.Windows.Data.IValueConverter\" and \
dotnet.classes[156].number_of_methods == 3 and \
dotnet.classes[156].methods[0].name == \"Convert\" and \
dotnet.classes[156].methods[0].return_type == \"object\" and \
dotnet.classes[156].methods[0].number_of_parameters == 4 and \
dotnet.classes[156].methods[0].parameters[0].name == \"value\" and \
dotnet.classes[156].methods[0].parameters[0].type == \"object\" and \
dotnet.classes[156].methods[0].parameters[1].name == \"targetType\" and \
dotnet.classes[156].methods[0].parameters[1].type == \"System.Type\" and \
dotnet.classes[156].methods[0].parameters[2].name == \"parameter\" and \
dotnet.classes[156].methods[0].parameters[2].type == \"object\" and \
dotnet.classes[156].methods[0].parameters[3].name == \"culture\" and \
dotnet.classes[156].methods[0].parameters[3].type == \"System.Globalization.CultureInfo\" \
}",
"tests/data/"
"756684f4017ba7e931a26724ae61606b16b5f8cc84ed38a260a34e50c5016f59");
assert_false_rule(
"import \"dotnet\" \
rule test { \