Beispiel #1
0
 def parse_pe32(self):
     self.pe_section_utils = PESectionUtils(self.bin_contents)
     self.section_headers_info = self.pe_section_utils.get_sections_info()
     imported_dlls = self.get_imported_dlls()
     logging.info('PE Imported DLLs #: {}'.format(len(imported_dlls)))
     logging.info('PE Imported DLLs: \n- {}'.format(
         '\n- '.join(imported_dlls)))
 def parse_pe32(self):
     self.pe_section_utils = PESectionUtils(self.bin_contents)
     self.section_headers_info = self.pe_section_utils.get_sections_info()
     imported_dlls_functions = self.get_pe_dll_imported_functions()
     logging.info('PE Imported DLLs #: {}'.format(
         len(imported_dlls_functions)))
     for imported_dll_functions in imported_dlls_functions:
         logging.info('- {}: {}'.format(
             imported_dll_functions['dll_name'],
             ', '.join(imported_dll_functions['imported_functions'])))
Beispiel #3
0
 def parse_pe32(self):
     self.pe_section_utils = PESectionUtils(self.bin_contents)
     self.section_headers_info = self.pe_section_utils.get_sections_info()
     self.pe_data_directory_utils = PEDataDirectoryUtils(self.bin_contents)
     self.data_directory_info = self.pe_data_directory_utils.get_data_directories_info()
     self.image_base_relocation_foff = self.get_image_base_relocation_rva()
     max_reloc_rva = self.get_max_rva_for_relocs()
     logging.info('End RVA for relocation information: {}'.format(max_reloc_rva))
     relocation_rvas = self.get_relocation_rvas(max_reloc_rva)
     logging.info('Virtual Addresses where relocations should be applied ({} relocations):'.format(len(relocation_rvas)))
     for reloc_rva in relocation_rvas:
         logging.info('  0x{:x}'.format(reloc_rva))
 def parse_pe32(self):
     self.pe_section_utils = PESectionUtils(self.bin_contents)
     self.section_headers_info = self.pe_section_utils.get_sections_info()
     self.pe_data_directory_utils = PEDataDirectoryUtils(self.bin_contents)
     self.data_directory_info = self.pe_data_directory_utils.get_data_directories_info()
     self.pe_optional_header_utils = PEOptionalHeaderUtils(self.bin_contents)
     self.image_tls_directory_foff = self.get_image_tls_directory_rva()
     tls_callback_vas = self.get_tls_callbacks()
     logging.info('TLS Callback addresses({} TLS callbacks):'.format(len(tls_callback_vas)))
     for callback_va in tls_callback_vas:
         callback_rva = callback_va - self.pe_optional_header_utils.get_image_base()
         callback_foff = PEGenericUtils.rva_to_file_offset(self.section_headers_info, callback_rva)
         logging.info('  VA: 0x{:x}, RVA: 0x{:x}, File Offset: {}'.format(callback_va, callback_rva, callback_foff))
Beispiel #5
0
class ParsePE32(object):
    def __init__(self):
        self.bin_contents = None
        self.section_headers_info = None
        self.data_directory_info = None
        self.pe_section_utils = None
        self.pe_data_directory_utils = None
        self.pe_optional_header_utils = None
        self.image_tls_directory_foff = None

    def execute(self, bin_path):
        self.bin_contents = FileUtils.read_file(bin_path)
        if self.bin_contents:
            self.parse_pe32()

    def parse_pe32(self):
        self.pe_section_utils = PESectionUtils(self.bin_contents)
        self.section_headers_info = self.pe_section_utils.get_sections_info()
        self.pe_data_directory_utils = PEDataDirectoryUtils(self.bin_contents)
        self.data_directory_info = self.pe_data_directory_utils.get_data_directories_info(
        )
        self.pe_optional_header_utils = PEOptionalHeaderUtils(
            self.bin_contents)
        self.image_tls_directory_foff = self.get_image_tls_directory_rva()
        tls_callback_vas = self.get_tls_callbacks()
        logging.info('TLS Callback addresses({} TLS callbacks):'.format(
            len(tls_callback_vas)))
        for callback_va in tls_callback_vas:
            callback_rva = callback_va - self.pe_optional_header_utils.get_image_base(
            )
            callback_foff = PEGenericUtils.rva_to_file_offset(
                self.section_headers_info, callback_rva)
            logging.info('  VA: 0x{:x}, RVA: 0x{:x}, File Offset: {}'.format(
                callback_va, callback_rva, callback_foff))

    def get_image_tls_directory_rva(self):
        tls_rva = self.data_directory_info[9]['rva']
        return tls_rva

    def get_tls_callbacks(self):
        tls_callback_vas = []
        callback_number = 0
        address_of_callbacks_va = -1
        while address_of_callbacks_va != 0:
            address_of_callbacks_va = self.get_address_of_callbacks_va(
                callback_number)
            if address_of_callbacks_va:
                tls_callback_vas.append(address_of_callbacks_va)
                callback_number += 1
        return tls_callback_vas

    def get_address_of_callbacks_va(self, callback_number):
        address_of_callbacks_off = 0xC + callback_number * 4  # sizeof(DWORD)
        image_tls_directory_rva = self.get_image_tls_directory_rva()
        image_tls_directory_foff = PEGenericUtils.rva_to_file_offset(
            self.section_headers_info, image_tls_directory_rva)
        address_of_callbacks_va = PEGenericUtils.unpack_dword(
            self.bin_contents,
            image_tls_directory_foff + address_of_callbacks_off)
        return address_of_callbacks_va
Beispiel #6
0
 def parse_pe32(self):
     self.pe_section_utils = PESectionUtils(self.bin_contents)
     self.section_headers_info = self.pe_section_utils.get_sections_info()
     self.pe_data_directory_utils = PEDataDirectoryUtils(self.bin_contents)
     self.data_directory_info = self.pe_data_directory_utils.get_data_directories_info(
     )
     self.export_directory_foff = self.get_export_descriptor_directory_foff(
     )
     if self.has_exports():
         number_of_functions = self.get_number_of_functions()
         logging.info('Number of Functions: {}'.format(number_of_functions))
         number_of_names = self.get_number_of_names()
         logging.info('Number of Names: {}'.format(number_of_names))
         exported_functions_by_name = self.get_pe_dll_exported_functions()
         logging.info('PE Exported Functions By Name: {}'.format(
             ', '.join(exported_functions_by_name)))
     else:
         logging.info('This DLL does not have exported functions')
class ParsePE32(object):

    def __init__(self):
        self.bin_contents = None
        self.section_headers_info = None
        self.data_directory_info = None
        self.pe_section_utils = None
        self.pe_data_directory_utils = None
        self.pe_optional_header_utils = None
        self.image_tls_directory_foff = None

    def execute(self, bin_path):
        self.bin_contents = FileUtils.read_file(bin_path)
        if self.bin_contents:
            self.parse_pe32()

    def parse_pe32(self):
        self.pe_section_utils = PESectionUtils(self.bin_contents)
        self.section_headers_info = self.pe_section_utils.get_sections_info()
        self.pe_data_directory_utils = PEDataDirectoryUtils(self.bin_contents)
        self.data_directory_info = self.pe_data_directory_utils.get_data_directories_info()
        self.pe_optional_header_utils = PEOptionalHeaderUtils(self.bin_contents)
        self.image_tls_directory_foff = self.get_image_tls_directory_rva()
        tls_callback_vas = self.get_tls_callbacks()
        logging.info('TLS Callback addresses({} TLS callbacks):'.format(len(tls_callback_vas)))
        for callback_va in tls_callback_vas:
            callback_rva = callback_va - self.pe_optional_header_utils.get_image_base()
            callback_foff = PEGenericUtils.rva_to_file_offset(self.section_headers_info, callback_rva)
            logging.info('  VA: 0x{:x}, RVA: 0x{:x}, File Offset: {}'.format(callback_va, callback_rva, callback_foff))

    def get_image_tls_directory_rva(self):
        tls_rva = self.data_directory_info[9]['rva']
        return tls_rva

    def get_tls_callbacks(self):
        tls_callback_vas = []
        callback_number = 0
        has_more_callbacks = True
        while has_more_callbacks:
            address_of_callbacks_va = self.get_address_of_callbacks_va(callback_number)
            # PE32+: Looks like in PE32+ we still need to check only a DWORD and not a QWORD, so we read a QWORD
            # as we do to get the callback address, but we clean up the MSBytes. If the LSBytes are 0, end of callbacks
            has_more_callbacks = address_of_callbacks_va & 0x000000FFFFFFFF != 0x0
            if has_more_callbacks:
                tls_callback_vas.append(address_of_callbacks_va)
                callback_number += 1
        return tls_callback_vas

    def get_address_of_callbacks_va(self, callback_number):
        address_of_callbacks_off = 24 + callback_number * 8  # PE32+: fix offset and increase to sizeof(QWORD)
        image_tls_directory_rva = self.get_image_tls_directory_rva()
        image_tls_directory_foff = PEGenericUtils.rva_to_file_offset(self.section_headers_info, image_tls_directory_rva)
        address_of_callbacks_va = PEGenericUtils.unpack_qword(self.bin_contents, image_tls_directory_foff + address_of_callbacks_off)  # PE32+: Unpack QWORD
        return address_of_callbacks_va
class ParsePE32(object):

    def __init__(self):
        self.bin_contents = None
        self.section_headers_info = None
        self.pe_section_utils = None

    def execute(self, bin_path):
        self.bin_contents = FileUtils.read_file(bin_path)
        if self.bin_contents:
            self.parse_pe32()

    def parse_pe32(self):
        self.pe_section_utils = PESectionUtils(self.bin_contents)
        self.section_headers_info = self.pe_section_utils.get_sections_info()
        imported_dlls = self.get_imported_dlls()
        logging.info('PE Imported DLLs #: {}'.format(len(imported_dlls)))
        logging.info('PE Imported DLLs: \n- {}'.format('\n- '.join(imported_dlls)))

    def get_imported_dlls(self):
        dll_names = []
        current_import_descriptor = self.get_first_import_descriptor_foff()
        import_descriptor_size = 0x14  # self.get_import_descriptor_size()
        while True:
            name = self.get_dll_name(current_import_descriptor)
            if not name:
                break
            dll_names.append(name)
            current_import_descriptor += import_descriptor_size
        return dll_names

    def get_data_directory_array_offs(self):  # _IMAGE_DATA_DIRECTORY DataDirectory[16]; from OptionalHeader
        optional_header_offs = self.pe_section_utils.get_optional_header_offs()
        # PE32+: offsets in Ero's poster are wrong starting with the second QWORD (SizeOfStackReserve).
        data_directories_offs = optional_header_offs + 0x60 + 0x10  # The +0x10 adjusts it.
        return data_directories_offs

    def get_import_directory_offs(self):  # IMAGE_DIRECTORY_ENTRY_IMPORT (DataDirectory[1])
        data_directory_array_offs = self.get_data_directory_array_offs()
        image_data_directory_size = 0x8  # two dwords
        return data_directory_array_offs + image_data_directory_size * 1

    def get_import_descriptor_size(self):
        import_directory_offs = self.get_import_directory_offs()
        size_offs = 0x4
        import_descriptor_size = self._unpack_word(self.bin_contents, import_directory_offs + size_offs)
        return import_descriptor_size

    def get_first_import_descriptor_rva(self):
        import_directory_offs = self.get_import_directory_offs()
        virtual_address_offs = 0x0
        import_descriptor_rva = self._unpack_word(self.bin_contents, import_directory_offs + virtual_address_offs)
        return import_descriptor_rva

    def get_first_import_descriptor_foff(self):
        import_directory_offs = self.get_first_import_descriptor_rva()
        return self._rva_to_file_offset(import_directory_offs)

    def get_dll_name(self, import_descriptor_addr):
        name_offs = 0xC
        name_rva = self._unpack_dword(self.bin_contents, import_descriptor_addr + name_offs)
        name_foff = self._rva_to_file_offset(name_rva)
        name = self._read_null_terminated_ascii_string(name_foff)
        return name

    def _read_null_terminated_ascii_string(self, starting_addr):
        idx = 0
        ascii_string = ''
        while ord(self.bin_contents[starting_addr + idx]) != 0 and starting_addr + idx < len(self.bin_contents):
            ascii_string += self.bin_contents[starting_addr + idx]
            idx += 1
        return ascii_string

    def _rva_to_file_offset(self, rva):
        file_offs = -1
        for section_info in self.section_headers_info:
            if self._rva_belongs_to_section(rva, section_info):
                file_offs = rva - section_info['Virtual Address'] + section_info['Pointer to Raw Data']
                break
        return file_offs

    @staticmethod
    def _rva_belongs_to_section(rva, section_info):
        return section_info['Virtual Address'] <= rva < section_info['Virtual Address'] + section_info['Virtual Size']

    @staticmethod
    def _unpack_dword(contents, offs):
        # format string: <: little endian. L: 4 bytes (1 dword)
        return struct.unpack_from('<L', contents, offs)[0]

    @staticmethod
    def _unpack_word(contents, offs):
        # format string: <: little endian. H: 2 bytes (1 word)
        return struct.unpack_from('<H', contents, offs)[0]
class ParsePE32(object):
    def __init__(self):
        self.bin_contents = None
        self.section_headers_info = None
        self.data_directory_info = None
        self.pe_section_utils = None
        self.pe_data_directory_utils = None

    def execute(self, bin_path):
        self.bin_contents = FileUtils.read_file(bin_path)
        if self.bin_contents:
            self.parse_pe32()

    def parse_pe32(self):
        self.pe_section_utils = PESectionUtils(self.bin_contents)
        self.section_headers_info = self.pe_section_utils.get_sections_info()
        self.pe_data_directory_utils = PEDataDirectoryUtils(self.bin_contents)
        self.data_directory_info = self.pe_data_directory_utils.get_data_directories_info(
        )
        imported_dlls_functions = self.get_pe_dll_imported_functions()
        logging.info('PE Imported DLLs #: {}'.format(
            len(imported_dlls_functions)))
        for imported_dll_functions in imported_dlls_functions:
            logging.info('- {}: {}'.format(
                imported_dll_functions['dll_name'],
                ', '.join(imported_dll_functions['imported_functions'])))

    def get_pe_dll_imported_functions(self):
        imported_dll_functions = []
        current_import_descriptor = self.get_first_import_descriptor_foff()
        import_descriptor_size = 0x14
        while True:
            dll_name = self.get_dll_name(current_import_descriptor)
            if dll_name:
                imported_functions = self.get_dll_imported_functions(
                    current_import_descriptor)
                imported_dll_functions.append({
                    'dll_name':
                    dll_name,
                    'imported_functions':
                    imported_functions
                })
                current_import_descriptor += import_descriptor_size
            else:
                break
        return imported_dll_functions

    def get_first_import_descriptor_foff(self):
        import_directory_rva = self.data_directory_info[1]['rva']
        return self._rva_to_file_offset(import_directory_rva)

    def get_dll_name(self, import_descriptor_addr):
        name_offs = 0xC
        name_rva = self._unpack_dword(self.bin_contents,
                                      import_descriptor_addr + name_offs)
        name_foff = self._rva_to_file_offset(name_rva)
        name = self._read_null_terminated_ascii_string(name_foff)
        return name

    def get_dll_imported_functions(self, import_descriptor_addr):
        function_names = []
        original_first_thunk_foff = self.get_original_first_thunk_foff(
            import_descriptor_addr)
        while True:
            function_name = self.get_imported_function_name(
                original_first_thunk_foff)
            if function_name:
                function_names.append(function_name)
                # PE32+: FirstThunk and OriginalFirstThunk point to QWORD in PE32+ instead of DWORD
                original_first_thunk_foff += 0x8
            else:
                break
        return function_names

    def get_original_first_thunk_foff(self, import_descriptor_addr):
        original_first_thunk_off = 0x0
        original_first_thunk_rva = self._unpack_dword(
            self.bin_contents,
            import_descriptor_addr + original_first_thunk_off)
        original_first_thunk_foff = self._rva_to_file_offset(
            original_first_thunk_rva)
        return original_first_thunk_foff

    def get_imported_function_name(self, first_thunk_foff):
        name = None
        name_off = 0x2
        image_import_by_name_foff = self.get_import_by_name_foff(
            first_thunk_foff)
        if image_import_by_name_foff != -1:
            name = self._read_null_terminated_ascii_string(
                image_import_by_name_foff + name_off)
        return name

    def get_import_by_name_foff(self, first_thunk_foff):
        address_of_data_off = 0
        # PE32+: FirstThunk and OriginalFirstThunk point to QWORD in PE32+ instead of DWORD
        import_by_name_rva = self._unpack_qword(
            self.bin_contents, first_thunk_foff + address_of_data_off)
        import_by_name_foff = self._rva_to_file_offset(import_by_name_rva)
        return import_by_name_foff

    def _read_null_terminated_ascii_string(self, starting_addr):
        idx = 0
        ascii_string = ''
        while ord(self.bin_contents[starting_addr +
                                    idx]) != 0 and starting_addr + idx < len(
                                        self.bin_contents):
            ascii_string += self.bin_contents[starting_addr + idx]
            idx += 1
        return ascii_string

    def _rva_to_file_offset(self, rva):
        file_offs = -1
        for section_info in self.section_headers_info:
            if self._rva_belongs_to_section(rva, section_info):
                file_offs = rva - section_info[
                    'Virtual Address'] + section_info['Pointer to Raw Data']
                break
        return file_offs

    @staticmethod
    def _rva_belongs_to_section(rva, section_info):
        return section_info['Virtual Address'] <= rva < section_info[
            'Virtual Address'] + section_info['Virtual Size']

    @staticmethod
    def _unpack_qword(contents, offs):
        # format string: <: little endian. Q: 8 bytes (2 dword)
        return struct.unpack_from('<Q', contents, offs)[0]

    @staticmethod
    def _unpack_dword(contents, offs):
        # format string: <: little endian. L: 4 bytes (1 dword)
        return struct.unpack_from('<L', contents, offs)[0]
class ParsePE32(object):
    def __init__(self):
        self.bin_contents = None
        self.section_headers_info = None
        self.pe_section_utils = None

    def execute(self, bin_path):
        self.bin_contents = FileUtils.read_file(bin_path)
        if self.bin_contents:
            self.parse_pe32()

    def parse_pe32(self):
        self.pe_section_utils = PESectionUtils(self.bin_contents)
        self.section_headers_info = self.pe_section_utils.get_sections_info()
        imported_dlls_functions = self.get_pe_dll_imported_functions()
        logging.info('PE Imported DLLs #: {}'.format(
            len(imported_dlls_functions)))
        for imported_dll_functions in imported_dlls_functions:
            logging.info('- {}: {}'.format(
                imported_dll_functions['dll_name'],
                ', '.join(imported_dll_functions['imported_functions'])))

    def get_pe_dll_imported_functions(self):
        imported_dll_functions = []
        current_import_descriptor = self.get_first_import_descriptor_foff()
        import_descriptor_size = 0x14
        while True:
            dll_name = self.get_dll_name(current_import_descriptor)
            if dll_name:
                imported_functions = self.get_dll_imported_functions(
                    current_import_descriptor)
                imported_dll_functions.append({
                    'dll_name':
                    dll_name,
                    'imported_functions':
                    imported_functions
                })
                current_import_descriptor += import_descriptor_size
            else:
                break
        return imported_dll_functions

    def get_first_import_descriptor_foff(self):
        import_directory_offs = self.get_first_import_descriptor_rva()
        return self._rva_to_file_offset(import_directory_offs)

    def get_first_import_descriptor_rva(self):
        import_directory_offs = self.get_import_directory_offs()
        virtual_address_offs = 0x0
        import_descriptor_rva = self._unpack_word(
            self.bin_contents, import_directory_offs + virtual_address_offs)
        return import_descriptor_rva

    def get_import_directory_offs(
            self):  # IMAGE_DIRECTORY_ENTRY_IMPORT (DataDirectory[1])
        data_directory_array_offs = self.get_data_directory_array_offs()
        image_data_directory_size = 0x8  # two dwords
        return data_directory_array_offs + image_data_directory_size * 1

    def get_data_directory_array_offs(
            self
    ):  # _IMAGE_DATA_DIRECTORY DataDirectory[16]; from OptionalHeader
        optional_header_offs = self.pe_section_utils.get_optional_header_offs()
        # PE32+: offsets in Ero's poster are wrong starting with the second QWORD (SizeOfStackReserve).
        data_directories_offs = optional_header_offs + 0x60 + 0x10  # The +0x10 adjusts it.
        return data_directories_offs

    def get_dll_name(self, import_descriptor_addr):
        name_offs = 0xC
        name_rva = self._unpack_dword(self.bin_contents,
                                      import_descriptor_addr + name_offs)
        name_foff = self._rva_to_file_offset(name_rva)
        name = self._read_null_terminated_ascii_string(name_foff)
        return name

    def get_dll_imported_functions(self, import_descriptor_addr):
        function_names = []
        first_thunk_foff = self.get_first_thunk_foff(import_descriptor_addr)
        while True:
            function_name = self.get_imported_function_name(first_thunk_foff)
            if function_name:
                function_names.append(function_name)
                # PE32+: FirstThunk and OriginalFirstThunk point to QWORD in PE32+ instead of DWORD
                first_thunk_foff += 0x8
            else:
                break
        return function_names

    def get_first_thunk_foff(self, import_descriptor_addr):
        first_thunk_off = 0x10
        first_thunk_rva = self._unpack_dword(
            self.bin_contents, import_descriptor_addr + first_thunk_off)
        first_thunk_foff = self._rva_to_file_offset(first_thunk_rva)
        return first_thunk_foff

    def get_imported_function_name(self, first_thunk_foff):
        name = None
        name_off = 0x2
        image_import_by_name_foff = self.get_import_by_name_foff(
            first_thunk_foff)
        if image_import_by_name_foff != -1:
            name = self._read_null_terminated_ascii_string(
                image_import_by_name_foff + name_off)
        return name

    def get_import_by_name_foff(self, first_thunk_foff):
        address_of_data_off = 0
        # PE32+: FirstThunk and OriginalFirstThunk point to QWORD in PE32+ instead of DWORD
        import_by_name_rva = self._unpack_qword(
            self.bin_contents, first_thunk_foff + address_of_data_off)
        import_by_name_foff = self._rva_to_file_offset(import_by_name_rva)
        return import_by_name_foff

    def _read_null_terminated_ascii_string(self, starting_addr):
        idx = 0
        ascii_string = ''
        while ord(self.bin_contents[starting_addr +
                                    idx]) != 0 and starting_addr + idx < len(
                                        self.bin_contents):
            ascii_string += self.bin_contents[starting_addr + idx]
            idx += 1
        return ascii_string

    def _rva_to_file_offset(self, rva):
        file_offs = -1
        for section_info in self.section_headers_info:
            if self._rva_belongs_to_section(rva, section_info):
                file_offs = rva - section_info[
                    'Virtual Address'] + section_info['Pointer to Raw Data']
                break
        return file_offs

    @staticmethod
    def _rva_belongs_to_section(rva, section_info):
        return section_info['Virtual Address'] <= rva < section_info[
            'Virtual Address'] + section_info['Virtual Size']

    @staticmethod
    def _unpack_qword(contents, offs):
        # format string: <: little endian. Q: 8 bytes (2 dword)
        return struct.unpack_from('<Q', contents, offs)[0]

    @staticmethod
    def _unpack_dword(contents, offs):
        # format string: <: little endian. L: 4 bytes (1 dword)
        return struct.unpack_from('<L', contents, offs)[0]

    @staticmethod
    def _unpack_word(contents, offs):
        # format string: <: little endian. H: 2 bytes (1 word)
        return struct.unpack_from('<H', contents, offs)[0]
class ParsePE32(object):
    def __init__(self):
        self.bin_contents = None
        self.section_headers_info = None
        self.data_directory_info = None
        self.pe_section_utils = None
        self.pe_data_directory_utils = None
        self.export_directory_foff = None

    def execute(self, bin_path):
        self.bin_contents = FileUtils.read_file(bin_path)
        if self.bin_contents:
            self.parse_pe32()

    def parse_pe32(self):
        """
        Same as x86, the only thing that changes is pe_data_directory_utils!
        """
        self.pe_section_utils = PESectionUtils(self.bin_contents)
        self.section_headers_info = self.pe_section_utils.get_sections_info()
        self.pe_data_directory_utils = PEDataDirectoryUtils(self.bin_contents)
        self.data_directory_info = self.pe_data_directory_utils.get_data_directories_info(
        )
        self.export_directory_foff = self.get_export_descriptor_directory_foff(
        )
        if self.has_exports():
            number_of_functions = self.get_number_of_functions()
            logging.info('Number of Functions: {}'.format(number_of_functions))
            number_of_names = self.get_number_of_names()
            logging.info('Number of Names: {}'.format(number_of_names))
            exported_functions_by_name = self.get_pe_dll_exported_functions()
            logging.info('PE Exported Functions By Name: {}'.format(
                ', '.join(exported_functions_by_name)))
        else:
            logging.info('This DLL does not have exported functions')

    def get_pe_dll_exported_functions(self):
        exported_functions_by_name = self.get_exported_functions_by_name()
        return exported_functions_by_name

    def get_export_descriptor_directory_foff(self):
        if self.export_directory_foff:
            return self.export_directory_foff
        export_directory_rva = self.data_directory_info[0]['rva']
        return self._rva_to_file_offset(export_directory_rva)

    def has_exports(self):
        return self.data_directory_info[0]['rva'] != 0

    def get_number_of_functions(self):
        number_of_functions_off = 0x14
        export_directory_foff = self.get_export_descriptor_directory_foff()
        number_of_functions = self._unpack_dword(
            self.bin_contents, export_directory_foff + number_of_functions_off)
        return number_of_functions

    def get_number_of_names(self):
        number_of_names_off = 0x18
        export_directory_foff = self.get_export_descriptor_directory_foff()
        number_of_names = self._unpack_dword(
            self.bin_contents, export_directory_foff + number_of_names_off)
        return number_of_names

    def get_exported_functions_by_name(self):
        exported_functions = []
        addr_of_names_foff = self.get_addr_of_names_foff()
        number_of_names = self.get_number_of_names()
        if number_of_names:
            for idx in range(number_of_names):
                exported_name_rva = self._unpack_dword(self.bin_contents,
                                                       addr_of_names_foff +
                                                       idx * 4)  # 4 = DWORD
                exported_name_foff = self._rva_to_file_offset(
                    exported_name_rva)
                exported_name_str = self._read_null_terminated_ascii_string(
                    exported_name_foff)
                exported_functions.append(exported_name_str)
        return exported_functions

    def get_addr_of_names_foff(self):
        addr_of_names_offs = 0x20
        export_directory_foff = self.get_export_descriptor_directory_foff()
        addr_of_names_rva = self._unpack_dword(
            self.bin_contents, export_directory_foff + addr_of_names_offs)
        addr_of_names_foff = self._rva_to_file_offset(addr_of_names_rva)
        return addr_of_names_foff

    def _read_null_terminated_ascii_string(self, starting_addr):
        idx = 0
        ascii_string = ''
        while ord(self.bin_contents[starting_addr +
                                    idx]) != 0 and starting_addr + idx < len(
                                        self.bin_contents):
            ascii_string += self.bin_contents[starting_addr + idx]
            idx += 1
        return ascii_string

    def _rva_to_file_offset(self, rva):
        file_offs = -1
        for section_info in self.section_headers_info:
            if self._rva_belongs_to_section(rva, section_info):
                file_offs = rva - section_info[
                    'Virtual Address'] + section_info['Pointer to Raw Data']
                break
        return file_offs

    @staticmethod
    def _rva_belongs_to_section(rva, section_info):
        return section_info['Virtual Address'] <= rva < section_info[
            'Virtual Address'] + section_info['Virtual Size']

    @staticmethod
    def _unpack_dword(contents, offs):
        # format string: <: little endian. L: 4 bytes (1 dword)
        return struct.unpack_from('<L', contents, offs)[0]
Beispiel #12
0
class ParsePE32(object):

    def __init__(self):
        self.bin_contents = None
        self.section_headers_info = None
        self.data_directory_info = None
        self.pe_section_utils = None
        self.pe_data_directory_utils = None
        self.image_base_relocation_foff = None

    def execute(self, bin_path):
        self.bin_contents = FileUtils.read_file(bin_path)
        if self.bin_contents:
            self.parse_pe32()

    def parse_pe32(self):
        self.pe_section_utils = PESectionUtils(self.bin_contents)
        self.section_headers_info = self.pe_section_utils.get_sections_info()
        self.pe_data_directory_utils = PEDataDirectoryUtils(self.bin_contents)
        self.data_directory_info = self.pe_data_directory_utils.get_data_directories_info()
        self.image_base_relocation_foff = self.get_image_base_relocation_rva()
        max_reloc_rva = self.get_max_rva_for_relocs()
        logging.info('End RVA for relocation information: {}'.format(max_reloc_rva))
        relocation_rvas = self.get_relocation_rvas(max_reloc_rva)
        logging.info('Virtual Addresses where relocations should be applied ({} relocations):'.format(len(relocation_rvas)))
        for reloc_rva in relocation_rvas:
            logging.info('  0x{:x}'.format(reloc_rva))

    def get_image_base_relocation_rva(self):
        if self.image_base_relocation_foff:
            return self.image_base_relocation_foff
        image_base_relocation_rva = self.data_directory_info[5]['rva']
        return image_base_relocation_rva

    def get_max_rva_for_relocs(self):
        return self.get_image_base_relocation_rva() + self.data_directory_info[5]['size']

    def get_relocation_rvas(self, max_reloc_rva):
        relocation_rvas = []
        block_rva = self.get_image_base_relocation_rva()
        while block_rva < max_reloc_rva:
            reloc_rvas_for_block = self.get_relocations_for_block(block_rva)
            relocation_rvas += reloc_rvas_for_block
            block_rva += self.get_block_size(block_rva)
        return relocation_rvas

    def get_relocations_for_block(self, block_rva):
        block_relocs = []
        block_foff = PEGenericUtils.rva_to_file_offset(self.section_headers_info, block_rva)
        block_virtualaddress = self.get_block_virtualaddress(block_rva)
        size_of_block = self.get_block_size(block_rva)
        relocation_block_foff, relocation_foff = block_foff, block_foff + 8
        while relocation_foff < relocation_block_foff + size_of_block:
            reloc_value = self.parse_reloc(relocation_foff)
            if reloc_value:
                block_relocs.append(block_virtualaddress + reloc_value)
            relocation_foff += 2
        return block_relocs

    def get_block_virtualaddress(self, block_rva):
        virtualaddress_off = 0x0
        block_foff = PEGenericUtils.rva_to_file_offset(self.section_headers_info, block_rva)
        size_of_block = PEGenericUtils.unpack_dword(self.bin_contents, block_foff + virtualaddress_off)
        return size_of_block

    def get_block_size(self, block_rva):
        size_of_block_off = 0x4
        block_foff = PEGenericUtils.rva_to_file_offset(self.section_headers_info, block_rva)
        size_of_block = PEGenericUtils.unpack_dword(self.bin_contents, block_foff + size_of_block_off)
        return size_of_block

    def parse_reloc(self, reloc_foff):
        reloc_type_mask = 0b1111000000000000
        reloc_value_mask = 0b0000111111111111
        reloc_word = PEGenericUtils.unpack_word(self.bin_contents, reloc_foff)
        reloc_type = (reloc_word & reloc_type_mask) >> 12
        reloc_value = reloc_word & reloc_value_mask if reloc_type == 0x3 else None
        return reloc_value