PE


Introduction

import lief

# Using filepath
pe: lief.PE.Binary | None = lief.PE.parse(r"C:\Users\test.exe")

# Using a Path from pathlib
pe: lief.PE.Binary | None = lief.PE.parse(pathlib.Path(r"C:\Users\test.exe"))

# Using an io object
with open(r"C:\Users\test.exe", "rb") as f:
    pe: lief.PE.Binary | None = lief.PE.parse(f)

Note

In Python, you can also use the generic lief.parse(), which returns a lief.PE.Binary object.

With the parsed PE binary, you can use the API to inspect or modify the binary itself.
pe: lief.PE.Binary

print(pe.rich_header)
print(pe.authentihash_md5.hex(":"))

for section in pe.sections:
    print(section.name, len(section.content))
pe: lief.PE.Binary

section = lief.PE.Section(".hello")
section.content = [0xCC] * 0x100
pe.add_section(section)

pe.write("new.exe")

Dump Analysis

LIEF has the support to process PE memory dump with . This function translates the file offsets referenced by the PE structures into their location inside the dump, using the base address passed as the second parameter:
# 0x7ffd21b80000 is the (absolute) address at which the dump was mapped
pe = lief.PE.parse_from_dump("module.dump", 0x7FFD21B80000)
assert isinstance(pe, lief.PE.Binary)

for imp in pe.imports:
    print(imp.name)

Note

The second parameter must be the (absolute) virtual address at which the dump was mapped. It is used to convert the RVAs found in the PE structures back into an offset within the dump.

Producing a dump with the runtime API

Such a dump can be produced from a live process thanks to the LIEF runtime and, more precisely, the Module API. captures the memory of a loaded module (from its imagebase over its virtual size):
# Find the module to dump in the current process
mod = lief.runtime.module_from_name("target.dll")
assert isinstance(mod, lief.runtime.windows.Module)

# Dump the module's memory into a file (the raw bytes are also returned) ...
data: bytes = mod.dump("module.dump")

# ... and parse it back using the same imagebase:
pe = lief.PE.parse_from_dump(data, mod.imagebase)

Advanced Parsing/Writing

Warning

parser_config = lief.PE.ParserConfig()
parser_config.parse_signature = False

pe = lief.PE.parse("some.exe", parser_config)
assert isinstance(pe, lief.PE.Binary)

builder_config = lief.PE.Builder.config_t()
builder_config.imports = True

pe.write("new.exe", builder_config)
pe: lief.PE.Binary

new_pe: bytes = pe.write_to_bytes()

PDB Support

For more details regarding PDB support, please refer to the PDB section.

Authenticode

LIEF supports PE Authenticode by providing an API for inspecting and verifying PE executable signatures.

Note

Typically, a signed PE executable contains a single signature, but the format allows for multiple signatures. Consequently, returns an iterator rather than a single signature object.
pe = lief.PE.parse("signed.exe")
assert isinstance(pe, lief.PE.Binary)

for signature in pe.signatures:
    for crt in signature.certificates:
        print(crt)

assert pe.verify_signature() == lief.PE.Signature.VERIFICATION_FLAGS.OK

You can find additional details about Authenticode support in this tutorial: PE Authenticode