Parse a Portable Executable file. Loads a PE file, parsing all its structures and making them available through the instance's attributes.
(self, fname, data, fast_load)
| 2820 | return structure |
| 2821 | |
| 2822 | def __parse__(self, fname, data, fast_load): |
| 2823 | """Parse a Portable Executable file. |
| 2824 | |
| 2825 | Loads a PE file, parsing all its structures and making them available |
| 2826 | through the instance's attributes. |
| 2827 | """ |
| 2828 | |
| 2829 | if fname is not None: |
| 2830 | stat = os.stat(fname) |
| 2831 | if stat.st_size == 0: |
| 2832 | raise PEFormatError("The file is empty") |
| 2833 | fd = None |
| 2834 | try: |
| 2835 | fd = open(fname, "rb") |
| 2836 | self.fileno = fd.fileno() |
| 2837 | if hasattr(mmap, "MAP_PRIVATE"): |
| 2838 | # Unix |
| 2839 | self.__data__ = mmap.mmap(self.fileno, 0, mmap.MAP_PRIVATE) |
| 2840 | else: |
| 2841 | # Windows |
| 2842 | self.__data__ = mmap.mmap(self.fileno, 0, access=mmap.ACCESS_READ) |
| 2843 | self.__from_file = True |
| 2844 | except IOError as excp: |
| 2845 | exception_msg = "{0}".format(excp) |
| 2846 | exception_msg = exception_msg and (": %s" % exception_msg) |
| 2847 | raise Exception( |
| 2848 | "Unable to access file '{0}'{1}".format(fname, exception_msg) |
| 2849 | ) |
| 2850 | finally: |
| 2851 | if fd is not None: |
| 2852 | fd.close() |
| 2853 | elif data is not None: |
| 2854 | self.__data__ = data |
| 2855 | self.__from_file = False |
| 2856 | |
| 2857 | # Resources should not overlap each other, so they should not exceed the |
| 2858 | # file size. |
| 2859 | self.__resource_size_limit_upperbounds = len(self.__data__) |
| 2860 | self.__resource_size_limit_reached = False |
| 2861 | |
| 2862 | if not fast_load: |
| 2863 | for byte, byte_count in Counter(bytearray(self.__data__)).items(): |
| 2864 | # Only report the cases where a byte makes up for more than 50% (if |
| 2865 | # zero) or 15% (if non-zero) of the file's contents. There are |
| 2866 | # legitimate PEs where 0x00 bytes are close to 50% of the whole |
| 2867 | # file's contents. |
| 2868 | if (byte == 0 and 1.0 * byte_count / len(self.__data__) > 0.5) or ( |
| 2869 | byte != 0 and 1.0 * byte_count / len(self.__data__) > 0.15 |
| 2870 | ): |
| 2871 | self.__warnings.append( |
| 2872 | ( |
| 2873 | "Byte 0x{0:02x} makes up {1:.4f}% of the file's contents." |
| 2874 | " This may indicate truncation / malformation." |
| 2875 | ).format(byte, 100.0 * byte_count / len(self.__data__)) |
| 2876 | ) |
| 2877 | |
| 2878 | dos_header_data = self.__data__[:64] |
| 2879 | if len(dos_header_data) != 64: |
no test coverage detected