import datetime import pytest import fixtures import dnfile from dnfile.mdtable import TypeRefRow, AssemblyRefRow def test_metadata(): path = fixtures.get_data_path_by_name("hello-world.exe") dn = dnfile.dnPE(path) assert dn.net is not None assert dn.net.metadata is not None assert dn.net.metadata.struct.Signature == 0x424A5342 assert dn.net.metadata.struct.MajorVersion == 1 assert dn.net.metadata.struct.MinorVersion == 1 assert dn.net.metadata.struct.Version.rstrip(b"\x00") == b"v4.0.30319" assert dn.net.metadata.struct.Flags == 0x0 assert dn.net.metadata.struct.NumberOfStreams == 5 def test_streams(): path = fixtures.get_data_path_by_name("hello-world.exe") dn = dnfile.dnPE(path) assert dn.net is not None assert dn.net.metadata is not None assert hasattr(dn.net.metadata, "streams") assert b"#~" in dn.net.metadata.streams assert dn.net.metadata.streams[b"#~"].get_file_offset() == 0x2d4 # strings used by #~ assert b"#Strings" in dn.net.metadata.streams assert hasattr(dn.net, "strings") assert dn.net.strings.get_file_offset() == 0x3d8 # "user strings" assert b"#US" in dn.net.metadata.streams assert hasattr(dn.net, "user_strings") assert dn.net.user_strings.get_file_offset() == 0x4dc assert b"#GUID" in dn.net.metadata.streams assert hasattr(dn.net, "guids") assert dn.net.guids.get_file_offset() == 0x4f8 assert b"#Blob" in dn.net.metadata.streams assert hasattr(dn.net, "blobs") assert dn.net.blobs.get_file_offset() == 0x508 assert b"#Foo" not in dn.net.metadata.streams assert not hasattr(dn.net, "foo") def test_guids(): path = fixtures.get_data_path_by_name("hello-world.exe") dn = dnfile.dnPE(path) assert dn.net is not None assert b"#GUID" in dn.net.metadata.streams assert hasattr(dn.net, "guids") assert dn.net.guids.get(0) is None assert isinstance(dn.net.guids.get(1), dnfile.stream.HeapItemGuid) assert dn.net.guids.get(1).value == b"\x8c\x8b\xc5\x48\xff\x24\x91\x45\x9e\xc8\x94\xbf\xea\xbd\x9f\x3e" def test_guids_as_sequence(): path = fixtures.get_data_path_by_name("hello-world.exe") dn = dnfile.dnPE(path) assert dn.net is not None assert b"#GUID" in dn.net.metadata.streams assert hasattr(dn.net, "guids") assert len(dn.net.guids) == 1 assert dn.net.guids[0] is not None assert hasattr(dn.net.guids[0], "value") assert dn.net.guids[0].value == b"\x8c\x8b\xc5\x48\xff\x24\x91\x45\x9e\xc8\x94\xbf\xea\xbd\x9f\x3e" def test_tables(): path = fixtures.get_data_path_by_name("hello-world.exe") dn = dnfile.dnPE(path) assert dn.net is not None assert len(dn.net.mdtables.tables_list) == 9 for table in ["Module", "TypeRef", "TypeDef", "MethodDef", "Param", "MemberRef", "CustomAttribute", "Assembly", "AssemblyRef"]: assert hasattr(dn.net.mdtables, table) assert not hasattr(dn.net.mdtables, "foo") tables = dict() tables["Module"] = { "RVA": 0x2110, "TableName": "Module", "TableNumber": 0, "IsSorted": False, "NumRows": 1, "RowSize": 10, "file_offset": 0x310, } tables["TypeRef"] = { "RVA": 0x211a, "TableName": "TypeRef", "TableNumber": 1, "IsSorted": False, "NumRows": 6, "RowSize": 6, "file_offset": 0x31a, } tables["TypeDef"] = { "RVA": 0x213e, "TableName": "TypeDef", "TableNumber": 2, "IsSorted": False, "NumRows": 2, "RowSize": 14, "file_offset": 0x33e, } tables["MethodDef"] = { "RVA": 0x215a, "TableName": "MethodDef", "TableNumber": 6, "IsSorted": False, "NumRows": 2, "RowSize": 14, "file_offset": 0x35a, } tables["Param"] = { "RVA": 0x2176, "TableName": "Param", "TableNumber": 8, "IsSorted": False, "NumRows": 1, "RowSize": 6, "file_offset": 0x376, } tables["MemberRef"] = { "RVA": 0x217c, "TableName": "MemberRef", "TableNumber": 10, "IsSorted": False, "NumRows": 5, "RowSize": 6, "file_offset": 0x37c, } tables["CustomAttribute"] = { "RVA": 0x219a, "TableName": "CustomAttribute", "TableNumber": 12, "IsSorted": True, "NumRows": 3, "RowSize": 6, "file_offset": 0x39a, } tables["Assembly"] = { "RVA": 0x21ac, "TableName": "Assembly", "TableNumber": 32, "IsSorted": False, "NumRows": 1, "RowSize": 22, "file_offset": 0x3ac, } tables["AssemblyRef"] = { "RVA": 0x21c2, "TableName": "AssemblyRef", "TableNumber": 35, "IsSorted": False, "NumRows": 1, "RowSize": 20, "file_offset": 0x3c2, } for table in dn.net.mdtables.tables_list: table: dnfile.base.ClrMetaDataTable ref_table = tables[table.name] assert table.name in tables assert table.rva == ref_table.get("RVA", None) assert table.name == ref_table.get("TableName", None) assert table.number == ref_table.get("TableNumber", None) assert table.is_sorted == ref_table.get("IsSorted", None) assert table.num_rows == ref_table.get("NumRows", None) assert table.row_size == ref_table.get("RowSize", None) assert table.file_offset == ref_table.get("file_offset") assert getattr(dn.net.mdtables, table.name) is table assert dn.net.mdtables.tables[table.number] is table assert dn.net.mdtables.Module[0].Name == "1-hello-world.exe" assert dn.net.mdtables.TypeDef[0].TypeName == "" assert dn.net.mdtables.TypeDef[1].TypeName == "HelloWorld" extends = dn.net.mdtables.TypeDef[1].Extends assert extends.table is not None assert extends.table.name == "TypeRef" assert extends.row_index == 5 assert extends.row.TypeNamespace == "System" assert extends.row.TypeName == "Object" assert dn.net.mdtables.AssemblyRef[0].Name == "mscorlib" def test_module(): path = fixtures.get_data_path_by_name("hello-world.exe") dn = dnfile.dnPE(path) assert dn.net is not None assert dn.net.mdtables.Module[0].Name == "1-hello-world.exe" def test_typedef_extends(): path = fixtures.get_data_path_by_name("hello-world.exe") dn = dnfile.dnPE(path) assert dn.net is not None typedefs = dn.net.mdtables.TypeDef assert typedefs[0].TypeName == "" assert typedefs[1].TypeName == "HelloWorld" # .class public auto ansi beforefieldinit HelloWorld # extends [mscorlib]System.Object extends = typedefs[1].Extends assert extends.table is not None assert extends.table.name == "TypeRef" assert extends.row_index == 5 superclass = extends.row assert isinstance(superclass, TypeRefRow) assert superclass.TypeNamespace == "System" assert superclass.TypeName == "Object" assert superclass.ResolutionScope.table is not None assert superclass.ResolutionScope.table.name == "AssemblyRef" assembly = superclass.ResolutionScope.row assert isinstance(assembly, AssemblyRefRow) assert assembly.Name == "mscorlib" def test_typedef_members(): path = fixtures.get_data_path_by_name("ModuleCode_x86.exe") dn = dnfile.dnPE(path) assert dn.net is not None typedefs = dn.net.mdtables.TypeDef assert typedefs[0].TypeName == "" assert typedefs[5].TypeName == "ModuleLoadException" assert len(typedefs[0].FieldList) == 52 assert len(typedefs[5].FieldList) == 1 assert len(typedefs[0].MethodList) == 76 assert len(typedefs[5].MethodList) == 3 assert typedefs[0].MethodList[0].row.Name == "rc4_init" assert typedefs[5].MethodList[0].row.Name == ".ctor" def test_typedef_methodlist(): path = fixtures.get_data_path_by_name("EmptyClass_x86.exe") dn = dnfile.dnPE(path) assert dn.net is not None typedefs = dn.net.mdtables.TypeDef assert typedefs[0].TypeName == "" assert typedefs[0].MethodList is not None assert len(typedefs[0].MethodList) == 1 assert typedefs[0].MethodList[0].row.Name == "_mainCRTStartup" def test_method_params(): path = fixtures.get_data_path_by_name("hello-world.exe") dn = dnfile.dnPE(path) assert dn.net is not None methods = dn.net.mdtables.MethodDef assert methods[0].Name == "Main" assert methods[1].Name == ".ctor" # default void Main (string[] args) cil managed assert len(methods[0].ParamList) == 1 # instance default void '.ctor' () cil managed assert len(methods[1].ParamList) == 0 assert methods[0].ParamList[0].row is not None assert methods[0].ParamList[0].row.Name == "args" def test_ignore_NumberOfRvaAndSizes(): # .NET loaders ignores NumberOfRvaAndSizes, so attempt to parse anyways path = fixtures.DATA / "1d41308bf4148b4c138f9307abc696a6e4c05a5a89ddeb8926317685abb1c241" if not path.exists(): raise pytest.xfail("test file 1d41308bf41... (DANGER: malware) not found in test fixtures") dn = dnfile.dnPE(path) assert hasattr(dn, "net") and dn.net is not None assert hasattr(dn.net, "metadata") and dn.net.metadata is not None def test_flags(): path = fixtures.get_data_path_by_name("hello-world.exe") dn = dnfile.dnPE(path) assert dn.net is not None # class HelloWorld cls = dn.net.mdtables.TypeDef.get_with_row_index(2) # these are enums from CorTypeSemantics assert cls.Flags.tdClass is True assert cls.Flags.tdInterface is False # these are flags from CorTypeAttrFlags assert cls.Flags.tdBeforeFieldInit is True assert cls.Flags.tdAbstract is False def test_heap_items(): # HeapItem buf = b"1234567890" item = dnfile.base.HeapItem(b"1234567890") assert item.raw_data == b"1234567890" assert item.value_bytes() == buf assert item == b"1234567890" assert item.raw_size == len(b"1234567890") assert item.rva is None assert item.value is None item = dnfile.base.HeapItem(b"1234567890", rva=0x400) assert item.rva == 0x400 # HeapItemString buf = b"1234567890" buf_decoded = buf.decode("utf-8") str_item = dnfile.stream.HeapItemString(buf) assert str_item.raw_data == buf assert str_item.encoding == "utf-8" assert str_item.value == buf_decoded assert str_item.value_bytes() == buf assert str_item == buf_decoded # HeapItemBinary buf_with_compressed_int_len = b"\x0a1234567890" buf = b"1234567890" bin_item = dnfile.stream.HeapItemBinary(buf_with_compressed_int_len) assert bin_item.raw_data == buf_with_compressed_int_len assert bin_item.raw_size == len(buf_with_compressed_int_len) assert bin_item.item_size == len(buf) assert bin_item.value_bytes() == buf assert bin_item == buf # UserString buf_with_flag = b"\x151\x002\x003\x004\x005\x006\x007\x008\x009\x000\x00\x00" us_str = "1234567890" us_item = dnfile.stream.UserString(buf_with_flag) assert us_item.raw_data == buf_with_flag assert us_item.value_bytes() == buf_with_flag[1:-1] assert us_item.value == us_str assert us_item == us_str # GUID guid_bytes = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" guid_item = dnfile.stream.HeapItemGuid(guid_bytes) assert guid_item is not None assert guid_item.raw_data == guid_bytes assert guid_item.value_bytes() == guid_bytes assert guid_item.value == guid_bytes assert str(guid_item) == "03020100-0504-0706-0809-0a0b0c0d0e0f" def test_datetime(): # DateTime buf = b"\xa8\xf6\x06\xbd\xd2\x51\xe3\x08" item = dnfile.base.DateTime(buf) item.parse() assert isinstance(item.struct, dnfile.base.DateTimeStruct) assert item.struct.Kind == 0 assert item.struct.Ticks == 0x08e351d2bd06f6a8 assert isinstance(item.kind, dnfile.enums.DateTimeKind) assert item.kind == dnfile.enums.DateTimeKind.Unspecified assert item.seconds == 64044553757.307459 assert isinstance(item.value, datetime.datetime)