Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 86 additions & 86 deletions pefile.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,42 +38,6 @@
codecs.register_error("backslashreplace_", codecs.lookup_error("backslashreplace"))


# lru_cache with a shallow copy of the objects returned (list, dict, ...).
# We don't use copy.deepcopy as it's _really_ slow, and for the data we retrieve
# copy.copy is sufficient.
# https://stackoverflow.com/questions/54909357
def lru_cache_copy(maxsize=128, typed=False):
def decorator(f):
cached_function = lru_cache(maxsize, typed)(f)

@wraps(f)
def wrapper(*args, **kwargs):
return copy.copy(cached_function(*args, **kwargs))
return wrapper

return decorator


@lru_cache(maxsize=2048)
def cache_adjust_SectionAlignment(val, section_alignment, file_alignment):
if section_alignment < 0x1000: # page size
section_alignment = file_alignment

# 0x200 is the minimum valid FileAlignment according to the documentation
# although ntoskrnl.exe has an alignment of 0x80 in some Windows versions
#
# if section_alignment < 0x80:
# section_alignment = 0x80

if section_alignment and val % section_alignment:
return (val // section_alignment) * section_alignment
return val


def count_zeroes(data):
return data.count(0)


fast_load = False

# This will set a maximum length of a string to be retrieved from the file.
Expand Down Expand Up @@ -711,6 +675,26 @@ def set_flags(obj, flag_field, flags):
obj.__dict__[flag] = False


# lru_cache with a shallow copy of the objects returned (list, dict, ...).
# We don't use copy.deepcopy as it's _really_ slow, and for the data we retrieve
# copy.copy is sufficient.
# https://stackoverflow.com/questions/54909357
def lru_cache_copy(maxsize=128, typed=False):
def decorator(f):
cached_function = lru_cache(maxsize, typed)(f)

@wraps(f)
def wrapper(*args, **kwargs):
return copy.copy(cached_function(*args, **kwargs))
return wrapper

return decorator


def count_zeroes(data):
return data.count(0)


def power_of_two(val):
return val != 0 and (val & (val - 1)) == 0

Expand Down Expand Up @@ -867,9 +851,9 @@ def get_text(self):
"I": 4,
"l": 4,
"L": 4,
"f": 4,
"q": 8,
"Q": 8,
"f": 4,
"d": 8,
"s": 1,
}
Expand Down Expand Up @@ -2326,6 +2310,22 @@ def is_valid_function_name(
)


@lru_cache(maxsize=2048)
def cache_adjust_SectionAlignment(val, section_alignment, file_alignment):
if section_alignment < 0x1000: # page size
section_alignment = file_alignment

# 0x200 is the minimum valid FileAlignment according to the documentation
# although ntoskrnl.exe has an alignment of 0x80 in some Windows versions
#
# if section_alignment < 0x80:
# section_alignment = 0x80

if section_alignment and val % section_alignment:
return (val // section_alignment) * section_alignment
return val


class PE:
"""A Portable Executable representation.

Expand Down Expand Up @@ -7274,108 +7274,108 @@ def get_physical_by_rva(self, rva):
return None

##
# Double-Word get / set
# Word get / set
##

@staticmethod
def get_data_from_dword(dword):
"""Return a four byte string representing the double word value (little endian)."""
return struct.pack("<L", dword & 0xFFFFFFFF)
def get_data_from_word(word):
"""Return a two byte string representing the word value (little endian)."""
return struct.pack("<H", word)

@staticmethod
def get_dword_from_data(data, offset):
"""Convert four bytes of data to a double word (little endian)
def get_word_from_data(data, offset):
"""Convert two bytes of data to a word (little endian)

'offset' is assumed to index into a dword array. So setting it to
N will return a dword out of the data starting at offset N*4.
'offset' is assumed to index into a word array. So setting it to
N will return a dword out of the data starting at offset N*2.

Returns None if the data can't be turned into a double word.
Returns None if the data can't be turned into a word.
"""

if (offset + 1) * 4 > len(data):
if (offset + 1) * 2 > len(data):
return None

return struct.unpack("<I", data[offset * 4 : (offset + 1) * 4])[0]
return struct.unpack("<H", data[offset * 2 : (offset + 1) * 2])[0]

def get_dword_at_rva(self, rva):
"""Return the double word value at the given RVA.
def get_word_at_rva(self, rva):
"""Return the word value at the given RVA.

Returns None if the value can't be read, i.e. the RVA can't be mapped
to a file offset.
"""

try:
return self.get_dword_from_data(self.get_data(rva, 4), 0)
return self.get_word_from_data(self.get_data(rva)[:2], 0)
except PEFormatError:
return None

def get_dword_from_offset(self, offset):
"""Return the double word value at the given file offset. (little endian)"""
def get_word_from_offset(self, offset):
"""Return the word value at the given file offset (little endian)"""

if offset + 4 > len(self.__data__):
if offset + 2 > len(self.__data__):
return None

return self.get_dword_from_data(self.__data__[offset : offset + 4], 0)
return self.get_word_from_data(self.__data__[offset : offset + 2], 0)

def set_dword_at_rva(self, rva, dword):
"""Set the double word value at the file offset corresponding to the given RVA."""
return self.set_bytes_at_rva(rva, self.get_data_from_dword(dword))
def set_word_at_rva(self, rva, word):
"""Set the word value at the file offset corresponding to the given RVA."""
return self.set_bytes_at_rva(rva, self.get_data_from_word(word))

def set_dword_at_offset(self, offset, dword):
"""Set the double word value at the given file offset."""
return self.set_bytes_at_offset(offset, self.get_data_from_dword(dword))
def set_word_at_offset(self, offset, word):
"""Set the word value at the given file offset."""
return self.set_bytes_at_offset(offset, self.get_data_from_word(word))

##
# Word get / set
# Double-Word get / set
##

@staticmethod
def get_data_from_word(word):
"""Return a two byte string representing the word value. (little endian)."""
return struct.pack("<H", word)
def get_data_from_dword(dword):
"""Return a four byte string representing the double word value (little endian)."""
return struct.pack("<L", dword & 0xFFFFFFFF)

@staticmethod
def get_word_from_data(data, offset):
"""Convert two bytes of data to a word (little endian)
def get_dword_from_data(data, offset):
"""Convert four bytes of data to a double word (little endian)

'offset' is assumed to index into a word array. So setting it to
N will return a dword out of the data starting at offset N*2.
'offset' is assumed to index into a dword array. So setting it to
N will return a dword out of the data starting at offset N*4.

Returns None if the data can't be turned into a word.
Returns None if the data can't be turned into a double word.
"""

if (offset + 1) * 2 > len(data):
if (offset + 1) * 4 > len(data):
return None

return struct.unpack("<H", data[offset * 2 : (offset + 1) * 2])[0]
return struct.unpack("<I", data[offset * 4 : (offset + 1) * 4])[0]

def get_word_at_rva(self, rva):
"""Return the word value at the given RVA.
def get_dword_at_rva(self, rva):
"""Return the double word value at the given RVA.

Returns None if the value can't be read, i.e. the RVA can't be mapped
to a file offset.
"""

try:
return self.get_word_from_data(self.get_data(rva)[:2], 0)
return self.get_dword_from_data(self.get_data(rva, 4), 0)
except PEFormatError:
return None

def get_word_from_offset(self, offset):
"""Return the word value at the given file offset. (little endian)"""
def get_dword_from_offset(self, offset):
"""Return the double word value at the given file offset (little endian)."""

if offset + 2 > len(self.__data__):
if offset + 4 > len(self.__data__):
return None

return self.get_word_from_data(self.__data__[offset : offset + 2], 0)
return self.get_dword_from_data(self.__data__[offset : offset + 4], 0)

def set_word_at_rva(self, rva, word):
"""Set the word value at the file offset corresponding to the given RVA."""
return self.set_bytes_at_rva(rva, self.get_data_from_word(word))
def set_dword_at_rva(self, rva, dword):
"""Set the double word value at the file offset corresponding to the given RVA."""
return self.set_bytes_at_rva(rva, self.get_data_from_dword(dword))

def set_word_at_offset(self, offset, word):
"""Set the word value at the given file offset."""
return self.set_bytes_at_offset(offset, self.get_data_from_word(word))
def set_dword_at_offset(self, offset, dword):
"""Set the double word value at the given file offset."""
return self.set_bytes_at_offset(offset, self.get_data_from_dword(dword))

##
# Quad-Word get / set
Expand Down Expand Up @@ -7414,7 +7414,7 @@ def get_qword_at_rva(self, rva):
return None

def get_qword_from_offset(self, offset):
"""Return the quad-word value at the given file offset. (little endian)"""
"""Return the quad-word value at the given file offset (little endian)."""

if offset + 8 > len(self.__data__):
return None
Expand Down