-Improved the checks being done when parsing the exports and the bound imports directories. The potential data ranges to consider have been constrained further and only strings with certain characteristics will be allowed as module names in the bound forwarders.


git-svn-id: http://pefile.googlecode.com/svn/trunk@118 8842bc4e-7134-0410-8230-5dc5194fb5c1
diff --git a/pefile.py b/pefile.py
index cd74f26..583c271 100644
--- a/pefile.py
+++ b/pefile.py
@@ -2320,20 +2320,20 @@
                 
         
     
-    def parse_directory_bound_imports(self, rva, size):
+    def parse_directory_bound_imports(self, file_offset, size):
         """"""
         
         bnd_descr = Structure(self.__IMAGE_BOUND_IMPORT_DESCRIPTOR_format__)
         bnd_descr_size = bnd_descr.sizeof()
-        start = rva
+        start = file_offset
         
         bound_imports = []
         while True:
             
             bnd_descr = self.__unpack_data__(
                 self.__IMAGE_BOUND_IMPORT_DESCRIPTOR_format__,
-                   self.__data__[rva:rva+bnd_descr_size],
-                   file_offset = rva)
+                   self.__data__[file_offset:file_offset+bnd_descr_size],
+                   file_offset = file_offset)
             if bnd_descr is None:
                 # If can't parse directory then silently return.
                 # This directory does not necessarily have to be valid to
@@ -2347,28 +2347,54 @@
             if bnd_descr.all_zeroes():
                 break
             
-            rva += bnd_descr.sizeof()
+            file_offset += bnd_descr.sizeof()
             
+            section = self.get_section_by_offset(file_offset)
+            if section is None:
+                # Find the first section starting at a later offset than that specified by 'file_offset'
+                first_section_after_offset = min([section.PointerToRawData for section in self.sections
+                                                if section.PointerToRawData > file_offset])
+                section = self.get_section_by_offset(first_section_after_offset)
+                safety_boundary = section.PointerToRawData - file_offset
+            else:
+                safety_boundary = section.PointerToRawData + len(section.get_data()) - file_offset
+
+            if not section:
+                self.__warnings.append(
+                    'RVA of IMAGE_BOUND_IMPORT_DESCRIPTOR points to an invalid address: %x' %
+                    file_offset)
+                return
+                
+
             forwarder_refs = []
-            for idx in xrange(bnd_descr.NumberOfModuleForwarderRefs):
+            # 8 is the size of __IMAGE_BOUND_IMPORT_DESCRIPTOR_format__
+            for idx in xrange( min( bnd_descr.NumberOfModuleForwarderRefs, safety_boundary/8) ):
                 # Both structures IMAGE_BOUND_IMPORT_DESCRIPTOR and
                 # IMAGE_BOUND_FORWARDER_REF have the same size.
                 bnd_frwd_ref = self.__unpack_data__(
                     self.__IMAGE_BOUND_FORWARDER_REF_format__,
-                    self.__data__[rva:rva+bnd_descr_size],
-                    file_offset = rva)
+                    self.__data__[file_offset:file_offset+bnd_descr_size],
+                    file_offset = file_offset)
                 # OC Patch:
                 if not bnd_frwd_ref:
                     raise PEFormatError(
                         "IMAGE_BOUND_FORWARDER_REF cannot be read")
-                rva += bnd_frwd_ref.sizeof()
+                file_offset += bnd_frwd_ref.sizeof()
                 
                 offset = start+bnd_frwd_ref.OffsetModuleName
                 name_str =  self.get_string_from_data(
                     0, self.__data__[offset : offset + MAX_STRING_LENGTH])
-                
-                if not name_str:
-                    break
+
+                # OffsetModuleName points to a DLL name. These shouldn't be too long.
+                # Anything longer than a safety length of 128 will be taken to indicate
+                # a corrupt entry and abort the processing of these entries.
+                # Names shorted than 4 characters will be taken as invalid as well.
+
+                if name_str:
+                    invalid_chars = [c for c in name_str if c not in string.printable]
+                    if len(name_str) > 256 or len(name_str) < 4 or invalid_chars:
+                        break
+
                 forwarder_refs.append(BoundImportRefData(
                     struct = bnd_frwd_ref,
                     name = name_str))
@@ -2377,6 +2403,11 @@
             name_str = self.get_string_from_data(
                 0, self.__data__[offset : offset + MAX_STRING_LENGTH])
             
+            if name_str:
+                invalid_chars = [c for c in name_str if c not in string.printable]
+                if len(name_str) > 256 or len(name_str) < 4 or invalid_chars:
+                    break
+
             if not name_str:
                 break
             bound_imports.append(
@@ -3228,8 +3259,16 @@
         
         max_failed_entries_before_giving_up = 10
         
-        for i in xrange( min( export_dir.NumberOfNames, length_until_eof(export_dir.AddressOfNames)/4) ):
+        section = self.get_section_by_rva(export_dir.AddressOfNames)
+        if not section:
+            self.__warnings.append(
+                'RVA AddressOfNames in the export directory points to an invalid address: %x' %
+                export_dir.AddressOfNames)
+            return
+        else:
+            safety_boundary = section.VirtualAddress + len(section.get_data()) - export_dir.AddressOfNames
             
+        for i in xrange( min( export_dir.NumberOfNames, safety_boundary/4) ):
             symbol_name_address = self.get_dword_from_data(address_of_names, i)
 
             if symbol_name_address is None:
@@ -3290,8 +3329,19 @@
         ordinals = [exp.ordinal for exp in exports]
         
         max_failed_entries_before_giving_up = 10
+
+        section = self.get_section_by_rva(export_dir.AddressOfFunctions)
+        if not section:
+            self.__warnings.append(
+                'RVA AddressOfFunctions in the export directory points to an invalid address: %x' %
+                export_dir.AddressOfFunctions)
+            return
+        else:
+            safety_boundary = section.VirtualAddress + len(section.get_data()) - export_dir.AddressOfFunctions
+            
+        safety_boundary = section.VirtualAddress + len(section.get_data()) - export_dir.AddressOfFunctions
         
-        for idx in xrange( min(export_dir.NumberOfFunctions, length_until_eof(export_dir.AddressOfFunctions)/4) ):
+        for idx in xrange( min(export_dir.NumberOfFunctions, safety_boundary/4) ):
             
             if not idx+export_dir.Base in ordinals:
                 try: