1# coding=utf-8
2# Copyright 2018 Sascha Schirra
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are met:
6#
7# 1. Redistributions of source code must retain the above copyright notice, this
8# list of conditions and the following disclaimer.
9#
10# 2. Redistributions in binary form must reproduce the above copyright notice,
11# this list of conditions and the following disclaimer in the documentation
12# and/or other materials provided with the distribution.
13#
14# 3. Neither the name of the copyright holder nor the names of its contributors
15# may be used to endorse or promote products derived from this software without
16# specific prior written permission.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" A ND
19# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29
30from .enum import Enum
31from .binary import *
32
33###################### PE General #################
34
35class IMAGE_FILE_MACHINE(Enum):
36    UKNOWN = 0
37    AM33 = 0x1d3
38    AMD64 = 0x8664
39    ARM = 0x1c0
40    ARMV = 0x1c4
41    EBC = 0xebc
42    I386 = 0x14c
43    IA64 = 0x200
44    M32R = 0x9041
45    MIPS16 = 0x266
46    MIPSFPU = 0x366
47    MIPSFPU16 = 0x466
48    POWERPC = 0x1f0
49    POWERPCFP = 0x1f1
50    THUMB = 0x1c2
51    WCEMIPSV2 = 0x169
52    ARM64 = 0xaa64
53
54class IMAGE_SCN(Enum):
55    TYPE_NO_PAD = 0x00000008
56    CNT_CODE = 0x00000020
57    CNT_INITIALIZED_DATA = 0x00000040
58    CNT_UNINITIALIZED_DATA = 0x00000080
59    LNK_OTHER = 0x00000100
60    LNK_INFO = 0x00000200
61    LNK_REMOVE = 0x00000800
62    LNK_COMDAT = 0x00001000
63    GPREL = 0x00008000
64    MEM_PURGEABLE = 0x00020000
65    MEM_LOCKED = 0x00040000
66    MEM_PRELOAD = 0x00080000
67    ALIGN_1BYTES = 0x00100000
68    ALIGN_2BYTES = 0x00200000
69    ALIGN_4BYTES = 0x00300000
70    ALIGN_8BYTES = 0x00400000
71    ALIGN_16BYTES = 0x00500000
72    ALIGN_32BYTES = 0x00600000
73    ALIGN_64BYTES = 0x00700000
74    ALIGN_128BYTES = 0x00800000
75    ALIGN_256BYTES = 0x00900000
76    ALIGN_512BYTES = 0x00A00000
77    ALIGN_1024BYTES = 0x00B00000
78    ALIGN_2048BYTES = 0x00C00000
79    ALIGN_4096BYTES = 0x00D00000
80    ALIGN_8192BYTES = 0x00E00000
81    LNK_NRELOC_OVFL = 0x01000000
82    MEM_WRITE = 0x80000000
83    MEM_READ = 0x4000000
84
85
86
87class ImageDllCharacteristics(Enum):
88    DYNAMIC_BASE = 0x0040
89    FORCE_INTEGRITY = 0x0080
90    NX_COMPAT = 0x0100
91    NO_ISOLATION = 0x0200
92    NO_SEH = 0x0400
93    NO_BIND = 0x0800
94    APP_CONTAINER = 0x1000
95    WDM_DRIVER = 0x2000
96    CONTROL_FLOW_GUARD = 0x4000
97    TERMINAL_SERVER_AWARE = 0x8000
98
99
100class ImageDirectoryEntry(Enum):
101    EXPORT = 0
102    IMPORT = 1
103    RESOURCE = 2
104    EXCEPTION = 3
105    SECURITY = 4
106    BASERELOC = 5
107    DEBUG = 6
108    COPYRIGHT = 7
109    GLOBALPTR = 8
110    TLS = 9
111    LOAD_CONFIG = 10
112    BOUND_IMPORT = 11
113    IAT = 12
114    DELAY_IMPORT = 13
115    COM_DESCRIPTOR = 14
116    NUMBER_OF_DIRECTORY_ENTRIES = 16
117
118
119class IMAGE_DOS_HEADER(Structure):
120    _fields_ = [('e_magic', c_char * 2),
121                ('e_cblp', c_ushort),
122                ('e_cp', c_ushort),
123                ('e_crlc', c_ushort),
124                ('e_cparhdr', c_ushort),
125                ('e_minalloc', c_ushort),
126                ('e_maxalloc', c_ushort),
127                ('e_ss', c_ushort),
128                ('e_sp', c_ushort),
129                ('e_csum', c_ushort),
130                ('e_ip', c_ushort),
131                ('e_cs', c_ushort),
132                ('e_lfarlc', c_ushort),
133                ('e_ovno', c_ushort),
134                ('e_res', c_ushort * 4),
135                ('e_oemid', c_ushort),
136                ('e_oeminfo', c_ushort),
137                ('e_res2', c_ushort * 10),
138                ('e_lfanew', c_uint)]       # Offset zum PE-Header
139
140
141class IMAGE_FILE_HEADER(Structure):
142    _fields_ = [('Machine', c_ushort),
143                ('NumberOfSections', c_ushort),
144                ('TimeDateStamp', c_uint),
145                ('PointerToSymbolTable', c_uint),
146                ('NumberOfSymbols', c_uint),
147                ('SizeOfOptionalHeader', c_ushort),
148                ('Characteristics', c_ushort)
149                ]
150
151
152class IMAGE_DATA_DIRECTORY(Structure):
153    _fields_ = [('VirtualAddress', c_uint),
154                ('Size', c_uint)]
155
156
157class IMAGE_SECTION_HEADER(Structure):
158    _fields_ = [('Name', c_char * 8),
159                ('PhysicalAddress_or_VirtualSize', c_uint),
160                ('VirtualAddress', c_uint),
161                ('SizeOfRawData', c_uint),
162                ('PointerToRawData', c_uint),
163                ('PointerToRelocations', c_uint),
164                ('PointerToLinenumbers', c_uint),
165                ('NumberOfRelocations', c_ushort),
166                ('NumberOfLinenumbers', c_ushort),
167                ('Characteristics', c_uint)]
168
169
170class IMAGE_IMPORT_BY_NAME(Structure):
171    _fields_ = [('Hint', c_ushort),
172                ('Name', c_char)]
173
174
175class IMAGE_THUNK_DATA(Union):
176    _fields_ = [('ForwarderString', c_uint),
177                ('Function', c_uint),
178                ('Ordinal', c_uint),
179                ('AddressOfData', c_uint)]
180
181
182class IMAGE_IMPORT_DESCRIPTOR(Structure):
183    _fields_ = [('OriginalFirstThunk', c_uint),
184                ('TimeDateStamp', c_uint),
185                ('ForwarderChain', c_uint),
186                ('Name', c_uint),
187                ('FirstThunk', c_uint)]
188
189class IMAGE_EXPORT_DIRECTORY(Structure):
190    _fields_ = [('Characteristics',c_uint),
191                ('TimeDateStamp',c_uint),
192                ('MajorVersion', c_ushort),
193                ('MinorVersion', c_ushort),
194                ('Name',c_uint),
195                ('Base',c_uint),
196                ('NumberOfFunctions',c_uint),
197                ('NumberOfNames',c_uint),
198                ('AddressOfFunctions',c_uint),
199                ('AddressOfNames',c_uint),
200                ('AddressOfNameOrdinals',c_uint)
201                ]
202
203class GUARD_CFF_ENTRY(Structure):
204    _fields_ = [('rva',c_uint),
205                ('flag', c_byte)]
206
207##################### PE32 ########################
208
209class IMAGE_OPTIONAL_HEADER(Structure):
210    _fields_ = [('Magic', c_ushort),
211                ('MajorLinkerVersion', c_byte),
212                ('MinorLinkerVersion', c_byte),
213                ('SizeOfCode', c_uint),
214                ('SizeOfInitializedData', c_uint),
215                ('SizeOfUninitializedData', c_uint),
216                ('AddressOfEntryPoint', c_uint),
217                ('BaseOfCode', c_uint),
218                ('BaseOfData', c_uint),
219                ('ImageBase', c_uint),
220                ('SectionAlignment', c_uint),
221                ('FileAlignment', c_uint),
222                ('MajorOperatingSystemVersion', c_ushort),
223                ('MinorOperatingSystemVersion', c_ushort),
224                ('MajorImageVersion', c_ushort),
225                ('MinorImageVersion', c_ushort),
226                ('MajorSubsystemVersion', c_ushort),
227                ('MinorSubsystemVersion', c_ushort),
228                ('Win32VersionValue', c_uint),
229                ('SizeOfImage', c_uint),
230                ('SizeOfHeaders', c_uint),
231                ('CheckSum', c_uint),
232                ('Subsystem', c_ushort),
233                ('DllCharacteristics', c_ushort),
234                ('SizeOfStackReserve', c_uint),
235                ('SizeOfStackCommit', c_uint),
236                ('SizeOfHeapReserve', c_uint),
237                ('SizeOfHeapCommit', c_uint),
238                ('LoaderFlags', c_uint),
239                ('NumberOfRvaAndSizes', c_uint),
240                ('DataDirectory', IMAGE_DATA_DIRECTORY * 16)]
241
242
243class PE32_IMAGE_NT_HEADERS(Structure):
244    _fields_ = [('Signature', c_char * 4),
245                ('FileHeader', IMAGE_FILE_HEADER),
246                ('OptionalHeader', IMAGE_OPTIONAL_HEADER)]
247
248class PE32(object):
249    IMAGE_NT_HEADERS = PE32_IMAGE_NT_HEADERS
250
251
252class IMAGE_LOAD_CONFIG_DIRECTORY32(Structure):
253    _fields_ = [('Size', c_uint),
254                ('TimeDateStamp', c_uint),
255                ('MajorVersion', c_ushort),
256                ('MinorVersion', c_ushort),
257                ('GlobalFlagsClear', c_uint),
258                ('GlobalFlagsSet', c_uint),
259                ('CriticalSectionDefaultTimeout', c_uint),
260                ('DeCommitFreeBLockThreshold', c_uint),
261                ('DeCommitTotalFreeThreshold', c_uint),
262                ('LockPrefixTable', c_uint),
263                ('MaximumAllocationSize', c_uint),
264                ('VirtualMemoryThreshold', c_uint),
265                ('ProcessHeapFlags', c_uint),
266                ('ProcessAffinityMask', c_uint),
267                ('CSDVersion', c_ushort),
268                ('Reserved1', c_ushort),
269                ('EditList', c_uint),
270                ('SecurityCookie', c_uint),
271                ('SEHandlerTable', c_uint),
272                ('SEHandlerCount', c_uint),
273                ('GuardCFCheckFunctionPointer', c_uint),
274                ('Reserved2', c_uint),
275                ('GuardCFFunctionTable', c_uint),
276                ('GuardCFFunctionCount', c_uint),
277                ('GuardFlags', c_uint)]
278
279
280######################### PE64 ########################
281
282class IMAGE_OPTIONAL_HEADER_PE32_PLUS(Structure):
283    _fields_ = [('Magic', c_ushort),
284                ('MajorLinkerVersion', c_ubyte),
285                ('MinorLinkerVersion', c_ubyte),
286                ('SizeOfCode', c_uint),
287                ('SizeOfInitializedData', c_uint),
288                ('SizeOfUninitializedData', c_uint),
289                ('AddressOfEntryPoint', c_uint),
290                ('BaseOfCode', c_uint),
291                ('ImageBase', c_ulonglong),
292                ('SectionAlignment', c_uint),
293                ('FileAlignment', c_uint),
294                ('MajorOperatingSystemVersion', c_ushort),
295                ('MinorOperatingSystemVersion', c_ushort),
296                ('MajorImageVersion', c_ushort),
297                ('MinorImageVersion', c_ushort),
298                ('MajorSubsystemVersion', c_ushort),
299                ('MinorSubsystemVersion', c_ushort),
300                ('Win32VersionValue', c_uint),
301                ('SizeOfImage', c_uint),
302                ('SizeOfHeaders', c_uint),
303                ('CheckSum', c_uint),
304                ('Subsystem', c_ushort),
305                ('DllCharacteristics', c_ushort),
306                ('SizeOfStackReserve', c_ulonglong),
307                ('SizeOfStackCommit', c_ulonglong),
308                ('SizeOfHeapReserve', c_ulonglong),
309                ('SizeOfHeapCommit', c_ulonglong),
310                ('LoaderFlags', c_uint),
311                ('NumberOfRvaAndSizes', c_uint),
312                ('DataDirectory', IMAGE_DATA_DIRECTORY * 16)]
313
314
315class PE64_IMAGE_NT_HEADERS(Structure):
316    _fields_ = [('Signature', c_char * 4),
317                ('FileHeader', IMAGE_FILE_HEADER),
318                ('OptionalHeader', IMAGE_OPTIONAL_HEADER_PE32_PLUS)]
319
320class PE64(object):
321    IMAGE_NT_HEADERS = PE64_IMAGE_NT_HEADERS
322
323
324class IMAGE_LOAD_CONFIG_DIRECTORY64(Structure):
325    _fields_ = [('Size', c_uint),
326                ('TimeDateStamp', c_uint),
327                ('MajorVersion', c_ushort),
328                ('MinorVersion', c_ushort),
329                ('GlobalFlagsClear', c_uint),
330                ('GlobalFlagsSet', c_uint),
331                ('CriticalSectionDefaultTimeout', c_uint),
332                ('DeCommitFreeBLockThreshold', c_ulonglong),
333                ('DeCommitTotalFreeThreshold', c_ulonglong),
334                ('LockPrefixTable', c_ulonglong),
335                ('MaximumAllocationSize', c_ulonglong),
336                ('VirtualMemoryThreshold', c_ulonglong),
337                ('ProcessAffinityMask', c_ulonglong),
338                ('ProcessHeapFlags', c_uint),
339                ('CSDVersion', c_ushort),
340                ('Reserved1', c_ushort),
341                ('EditList', c_ulonglong),
342                ('SecurityCookie', c_ulonglong),
343                ('SEHandlerTable', c_ulonglong),
344                ('SEHandlerCount', c_ulonglong),
345                ('GuardCFCheckFunctionPointer', c_ulonglong),
346                ('Reserved2', c_ulonglong),
347                ('GuardCFFunctionTable', c_ulonglong),
348                ('GuardCFFunctionCount', c_ulonglong),
349                ('GuardFlags', c_uint)]
350
351##################### Container ###################
352
353
354def to_offset(addr, section):
355    return addr - section.header.VirtualAddress
356
357def to_raw_address(addr, section):
358        """Converts the addr from a rva to a pointer to raw data in the file"""
359        return addr - section.header.VirtualAddress + section.header.PointerToRawData
360
361class ImageDosHeaderData(Container):
362    """
363    header = IMAGE_DOS_HEADER
364    """
365
366class ImageNtHeaderData(Container):
367    """
368    header = IMAGE_NT_HEADERS
369    """
370
371class SectionData(Container):
372    """
373    header = IMAGE_SECTION_HEADER
374    name = name of the section (str)
375    bytes = bytes of section (bytearray)
376    raw = bytes of section (c_ubyte_array)
377    """
378
379class DataDirectoryData(Container):
380    """
381    header = IMAGE_DATA_DIRECTORY
382    """
383
384class ImportDescriptorData(Container):
385    """
386    header = IMAGE_IMPORT_DESCRIPTOR
387    dllName = name of dll (str)
388    importNameTable = list of IMAGE_THUNK_DATA
389    importAddressTable = list of IMAGE_THUNK_DATA
390    """
391
392class ImportByNameData(Container):
393    """
394    header = IMAGE_IMPORT_BY_NAME
395    name = name of function (str)
396    """
397
398
399class ThunkData(Container):
400    """
401    header = IMAGE_THUNK_DATA
402    rva = relative virtual address of thunk
403    ordinal = None | Ordinal
404    importByName = None| ImportByNameData
405    """
406
407class ExportDirectoryData(Container):
408    """
409    header = IMAGE_EXPORT_DIRECTORY
410    name = name of dll (str)
411    functions = list of FunctionData
412    """
413
414class LoadConfigData(Container):
415    """"
416    header = IMAGE_LOAD_CONFIG_DIRECTORY32/IMAGE_LOAD_CONFIG_DIRECTORY64
417    cfGuardedFunctions = list of relative virtual addresses (RVA) of cfg allowed call/jmp targets. Empty if CFG not supported
418    """
419
420class FunctionData(Container):
421    """
422    name = name of the function (str)
423    ordinal = ordinal (int)
424    rva = relative virtual address of function (int)
425    """
426
427def checkOffset(offset, section):
428    size = len(section.raw)
429    if offset < 0 or offset > size:
430        raise BinaryError('Invalid offset: {} (data size: {})'.format(offset, size))
431
432class PE(Binary):
433
434    def __init__(self, fileName, fileContent=None, parse_header_only=False):
435        super(PE, self).__init__(fileName, fileContent)
436
437
438
439        self.__imageDosHeader = self._parseImageDosHeader(self._bytes)
440        self.__classes = self._getSuitableClasses(self._bytes, self.imageDosHeader)
441
442        if not self.__classes:
443            raise BinaryError('Bad architecture')
444
445        self.__imageNtHeaders = self._parseImageNtHeaders(self._bytes, self.imageDosHeader)
446        self.__sections = self._parseSections(self._bytes, self.imageDosHeader, self.imageNtHeaders, parse_header_only=parse_header_only)
447
448        if parse_header_only:
449            self.__dataDirectory = None
450        else:
451            self.__dataDirectory = self._parseDataDirectory(self._bytes, self.sections, self.imageNtHeaders)
452
453
454    @property
455    def _classes(self):
456        return self.__classes
457
458    @property
459    def imageDosHeader(self):
460        return self.__imageDosHeader
461
462    @property
463    def imageNtHeaders(self):
464        return self.__imageNtHeaders
465
466    @property
467    def sections(self):
468        return self.__sections
469
470    @property
471    def dataDirectory(self):
472        return self.__dataDirectory
473
474
475    @property
476    def entryPoint(self):
477        return self.imageNtHeaders.header.OptionalHeader.ImageBase + self.imageNtHeaders.header.OptionalHeader.AddressOfEntryPoint
478
479    @property
480    def imageBase(self):
481        return self.imageNtHeaders.header.OptionalHeader.ImageBase
482
483    @property
484    def type(self):
485        return 'PE'
486
487    def _getSuitableClasses(self, data, imageDosHeader):
488        """Returns the class which holds the suitable classes for the loaded file"""
489        classes = None
490        machine = IMAGE_FILE_MACHINE[c_ushort.from_buffer(data,imageDosHeader.header.e_lfanew+4).value]
491
492        if machine == IMAGE_FILE_MACHINE.I386:
493            classes = PE32
494        elif machine == IMAGE_FILE_MACHINE.AMD64:
495            classes = PE64
496
497        return classes
498
499    def _parseImageDosHeader(self, data):
500        """Returns the ImageDosHeader"""
501        ioh = IMAGE_DOS_HEADER.from_buffer(data)
502        if ioh.e_magic != b'MZ':
503            raise BinaryError('No valid PE/COFF file')
504
505        return ImageDosHeaderData(header=ioh)
506
507    def _parseImageNtHeaders(self, data, imageDosHeader):
508        """Returns the ImageNtHeaders"""
509        inth = self._classes.IMAGE_NT_HEADERS.from_buffer(data, imageDosHeader.header.e_lfanew)
510
511        if inth.Signature != b'PE':
512            raise BinaryError('No valid PE/COFF file')
513
514        return ImageNtHeaderData(header=inth)
515
516    def _parseSections(self, data, imageDosHeader, imageNtHeaders, parse_header_only=False):
517        """Parses the sections in the memory and returns a list of them"""
518        sections = []
519
520        optional_header_offset = imageDosHeader.header.e_lfanew + 4 + sizeof(IMAGE_FILE_HEADER)
521        offset = optional_header_offset + imageNtHeaders.header.FileHeader.SizeOfOptionalHeader  # start reading behind the dos- and ntheaders
522
523        image_section_header_size = sizeof(IMAGE_SECTION_HEADER)
524        IMAGE_SIZEOF_SYMBOL = 18
525        strtable_offset = imageNtHeaders.header.FileHeader.PointerToSymbolTable + IMAGE_SIZEOF_SYMBOL * imageNtHeaders.header.FileHeader.NumberOfSymbols
526
527        for sectionNo in range(imageNtHeaders.header.FileHeader.NumberOfSections):
528            ishdr = IMAGE_SECTION_HEADER.from_buffer(data, offset)
529
530            if parse_header_only:
531                raw = None
532                bytes_ = bytearray()
533            else:
534                size = ishdr.SizeOfRawData
535                raw = (c_ubyte * size).from_buffer(data, ishdr.PointerToRawData)
536                bytes_ = bytearray(raw)
537
538            secname = ishdr.Name.decode('ASCII', errors='ignore')
539            if secname.startswith('/'):
540                name_offset = int(secname[1:]) + strtable_offset
541                s = bytearray()
542                while self._bytes[name_offset] != 0:
543                    s.append(self._bytes[name_offset])
544                    name_offset += 1
545                secname = bytes(s).decode('ASCII', errors='ignore')
546
547            sections.append(SectionData(header=ishdr, name=secname, bytes=bytes_, raw=raw))
548
549            offset += image_section_header_size
550
551        return sections
552
553    def _getSectionForDataDirectoryEntry(self, data_directory_entry, sections):
554        """Returns the section which contains the data of DataDirectory"""
555        for section in sections:
556            if data_directory_entry.VirtualAddress >= section.header.VirtualAddress and \
557            data_directory_entry.VirtualAddress < section.header.VirtualAddress + section.header.SizeOfRawData :
558
559                return section
560
561    def _parseDataDirectory(self, data, sections, imageNtHeaders):
562        """Parses the entries of the DataDirectory and returns a list of the content"""
563        data_directory_data_list = [None for i in range(15)]
564
565        # parse DataDirectory[Export]
566        export_data_directory = imageNtHeaders.header.OptionalHeader.DataDirectory[ImageDirectoryEntry.EXPORT]
567        export_section = self._getSectionForDataDirectoryEntry(export_data_directory, sections)
568        export_data_directory_data = self._parseDataDirectoryExport(data, export_data_directory, export_section)
569        data_directory_data_list[ImageDirectoryEntry.EXPORT] = export_data_directory_data
570
571        # parse DataDirectory[Import]
572        import_data_directory = imageNtHeaders.header.OptionalHeader.DataDirectory[ImageDirectoryEntry.IMPORT]
573        import_section = self._getSectionForDataDirectoryEntry(import_data_directory, sections)
574        import_data_directory_data = self._parseDataDirectoryImport(import_data_directory, import_section)
575        data_directory_data_list[ImageDirectoryEntry.IMPORT] = import_data_directory_data
576
577        # parse DataDirectory[LOAD_CONFIG]
578        loadconfig_data_directory = imageNtHeaders.header.OptionalHeader.DataDirectory[ImageDirectoryEntry.LOAD_CONFIG]
579        loadconfig_section = self._getSectionForDataDirectoryEntry(loadconfig_data_directory, sections)
580        loadconfig_data = self._parseLoadConfig(loadconfig_data_directory, loadconfig_section)
581        data_directory_data_list[ImageDirectoryEntry.LOAD_CONFIG] = loadconfig_data
582
583        return data_directory_data_list
584
585    def _parseDataDirectoryExport(self, data, dataDirectoryEntry, exportSection):
586        """Parses the EmportDataDirectory and returns an instance of ExportDirectoryData"""
587        if not exportSection:
588            return
589        functions = []
590        export_directory = IMAGE_EXPORT_DIRECTORY.from_buffer(exportSection.raw, to_offset(dataDirectoryEntry.VirtualAddress, exportSection))
591        offset = to_offset(export_directory.Name, exportSection)
592
593        checkOffset(offset, exportSection)
594        name = get_str(exportSection.raw, offset)
595
596        offsetOfNames = to_offset(export_directory.AddressOfNames, exportSection)
597        offsetOfAddress = to_offset(export_directory.AddressOfFunctions, exportSection)
598        offsetOfNameOrdinals = to_offset(export_directory.AddressOfNameOrdinals, exportSection)
599        for i in range(export_directory.NumberOfNames):
600            name_address = c_uint.from_buffer(exportSection.raw, offsetOfNames).value
601            name_offset = to_offset(name_address, exportSection)
602
603            checkOffset(name_offset, exportSection)
604            func_name = get_str(exportSection.raw, name_offset)
605            ordinal = c_ushort.from_buffer(exportSection.raw, offsetOfNameOrdinals).value
606            func_addr = c_uint.from_buffer(exportSection.raw, offsetOfAddress).value
607
608            offsetOfNames += 4
609            offsetOfAddress += 4
610            offsetOfNameOrdinals += 2
611            functions.append(FunctionData(name=func_name, rva=func_addr, ordinal=ordinal))
612
613        return ExportDirectoryData(header=export_directory, name=name, functions=functions)
614
615    def _parseDataDirectoryImport(self, dataDirectoryEntry, importSection):
616        """Parses the ImportDataDirectory and returns a list of ImportDescriptorData"""
617        if not importSection:
618            return
619
620
621        raw_bytes = (c_ubyte * dataDirectoryEntry.Size).from_buffer(importSection.raw, to_offset(dataDirectoryEntry.VirtualAddress, importSection))
622        offset = 0
623        import_descriptors = []
624        while True:
625            import_descriptor = IMAGE_IMPORT_DESCRIPTOR.from_buffer(raw_bytes, offset)
626
627
628            if import_descriptor.OriginalFirstThunk == 0:
629                break
630            else:
631                nameOffset = to_offset(import_descriptor.Name, importSection)
632
633                checkOffset(nameOffset, importSection)
634                dllName = get_str(importSection.raw, nameOffset)
635
636                import_name_table =  self.__parseThunks(import_descriptor.OriginalFirstThunk, importSection)
637                import_address_table =  self.__parseThunks(import_descriptor.FirstThunk, importSection)
638
639                import_descriptors.append(ImportDescriptorData(header=import_descriptor, dllName=dllName, importNameTable=import_name_table, importAddressTable=import_address_table))
640            offset += sizeof(IMAGE_IMPORT_DESCRIPTOR)
641        return import_descriptors
642
643    def _getSectionByRVA(self, va):
644        for section in self.sections:
645            address = section.header.VirtualAddress
646            SizeOfRawData = section.header.SizeOfRawData
647            if  address <= va and va < (address + SizeOfRawData):
648                return section
649
650        return
651
652    def _parseLoadConfig(self, loadConfigEntry, loadconfigSection):
653        if not loadconfigSection:
654            return
655
656        if self._classes == PE64:
657            load_config_directory = IMAGE_LOAD_CONFIG_DIRECTORY64.from_buffer(
658                loadconfigSection.raw, to_offset(loadConfigEntry.VirtualAddress, loadconfigSection))
659
660            pass
661
662        elif self._classes == PE32:
663            load_config_directory = IMAGE_LOAD_CONFIG_DIRECTORY32.from_buffer(
664                loadconfigSection.raw, to_offset(loadConfigEntry.VirtualAddress, loadconfigSection))
665
666            pass
667        else:
668            pass
669
670        guardCFTableRVA = load_config_directory.GuardCFFunctionTable - self.imageBase
671        section = self._getSectionByRVA(guardCFTableRVA)
672        CfGuardedFunctions = set()
673        if section:
674            sectionOffset = guardCFTableRVA - section.header.VirtualAddress
675
676            # loop through the ControlFlow Guard Function table
677            for i in range(0, load_config_directory.GuardCFFunctionCount):
678                cffEntry = GUARD_CFF_ENTRY.from_buffer(section.raw, sectionOffset)
679                CfGuardedFunctions.add(cffEntry.rva)
680                sectionOffset += 5
681
682        return LoadConfigData(header=load_config_directory, cfGuardedFunctions=CfGuardedFunctions )
683
684    def __parseThunks(self, thunkRVA, importSection):
685        """Parses the thunks and returns a list"""
686        offset = to_offset(thunkRVA, importSection)
687        table_offset = 0
688        thunks = []
689        while True:
690            thunk = IMAGE_THUNK_DATA.from_buffer(importSection.raw, offset)
691            offset += sizeof(IMAGE_THUNK_DATA)
692            if thunk.Ordinal == 0:
693                break
694            thunkData = ThunkData(header=thunk, rva=table_offset+thunkRVA,ordinal=None, importByName=None)
695            if to_offset(thunk.AddressOfData, importSection) > 0 and to_offset(thunk.AddressOfData, importSection) < len(self._bytes):
696                self.__parseThunkData(thunkData, importSection)
697            thunks.append(thunkData)
698            table_offset += 4
699        return thunks
700
701    def __parseThunkData(self, thunk,importSection):
702        """Parses the data of a thunk and sets the data"""
703        offset = to_offset(thunk.header.AddressOfData, importSection)
704        if 0xf0000000 & thunk.header.AddressOfData == 0x80000000:
705            thunk.ordinal = thunk.header.AddressOfData & 0x0fffffff
706        else:
707            ibn = IMAGE_IMPORT_BY_NAME.from_buffer(importSection.raw, offset)
708
709            checkOffset(offset+2, importSection)
710            name = get_str(importSection.raw, offset+2)
711            thunk.importByName = ImportByNameData(header=ibn, hint=ibn.Hint, name=name)
712
713    @classmethod
714    def isSupportedContent(cls, fileContent):
715        """Returns if the files are valid for this filetype"""
716        return bytearray(fileContent)[:2] == b'MZ'
717