# `pwnlib.elf.elf` — ELF Files

Exposes functionality for manipulating ELF files

Stop hard-coding things! Look them up at runtime with `pwnlib.elf`.

## Example Usage

    >>> e = ELF('/bin/cat')
    >>> print(hex(e.address))
    0x400000
    >>> print(hex(e.symbols['write']))
    0x401680
    >>> print(hex(e.got['write']))
    0x60b070
    >>> print(hex(e.plt['write']))
    0x401680

You can even patch and save the files.

    >>> e = ELF('/bin/cat')
    >>> e.read(e.address+1, 3)
    b'ELF'
    >>> e.asm(e.address, 'ret')
    >>> e.save('/tmp/quiet-cat')
    >>> disasm(open('/tmp/quiet-cat','rb').read(1))
    '   0:   c3                      ret'

## Module Members

class pwnlib.elf.elf.ELF(path, checksec=True)

  Bases: `ELFFile`

  Encapsulates information about an ELF file.

  Example

      >>> bash = ELF(which('bash'))
      >>> hex(bash.symbols['read'])
      0x41dac0
      >>> hex(bash.plt['read'])
      0x41dac0
      >>> u32(bash.read(bash.got['read'], 4))
      0x41dac6
      >>> print(bash.disasm(bash.plt.read, 16))
      0:   ff 25 1a 18 2d 00       jmp    QWORD PTR [rip+0x2d181a]        # 0x2d1820
      6:   68 59 00 00 00          push   0x59
      b:   e9 50 fa ff ff          jmp    0xfffffffffffffa60

  classmethod load_from_path(path)

    Takes a path to a file on the local filesystem, and returns an ELFFile from it, setting up a correct stream_loader relative to the original file.

  static _decompress_dwarf_section(section)

    Returns the uncompressed contents of the provided DWARF section.

  static from_assembly(assembly) → ELF

    Given an assembly listing, return a fully loaded ELF object which contains that assembly at its entry point.

    Parameters:

      - assembly (str) – Assembly language listing

      - vma (int) – Address of the entry point and the module’s base address.

    Example

        >>> e = ELF.from_assembly('nop; foo: int 0x80', vma = 0x400000)
        >>> e.symbols['foo'] = 0x400001
        >>> e.disasm(e.entry, 1)
        '  400000:       90                      nop'
        >>> e.disasm(e.symbols['foo'], 2)
        '  400001:       cd 80                   int    0x80'

  static from_bytes(bytes) → ELF

    Given a sequence of bytes, return a fully loaded ELF object which contains those bytes at its entry point.

    Parameters:

      - bytes (str) – Shellcode byte string

      - vma (int) – Desired base address for the ELF.

    Example

        >>> e = ELF.from_bytes(b'\x90\xcd\x80', vma=0xc000)
        >>> print(e.disasm(e.entry, 3))
            c000:       90                      nop
            c001:       cd 80                   int    0x80

  static make_relative_loader(base_path)

    Return a function that takes a potentially relative path, resolves it against base_path (bytes or str), and opens a file at that.

    ELFFile uses functions like that for resolving DWARF links.

  static patch_custom_libraries(exe_path, custom_library_path, create_copy=True, suffix='_remotelibc') → ELF

    Looks for the interpreter binary in the given path and patches the binary to use it if available. Also patches the RUNPATH to the given path using the patchelf utility.

    Parameters:

      - exe_path (str) – Path to the binary to patch.

      - custom_library_path (str) – Path to a folder containing the libraries.

      - create_copy (bool) – Create a copy of the binary and apply the patches to the copy.

      - suffix (str) – Suffix to append to the filename when creating the copy to patch.

    Returns:

      A new ELF instance is returned after patching the binary with the external `patchelf` tool.

    Example

        >>> tmpdir = tempfile.mkdtemp()
        >>> linker_path = os.path.join(tmpdir, 'ld-mock.so')
        >>> write(linker_path, b'loader')
        >>> ls_path = os.path.join(tmpdir, 'ls')
        >>> _ = shutil.copy(which('ls'), ls_path)
        >>> e = ELF.patch_custom_libraries(ls_path, tmpdir)
        >>> e.runpath.decode() == tmpdir
        True
        >>> e.linker.decode() == linker_path
        True

  static set_interpreter(exepath, interpreter_path) → ELF

    Patches the interpreter of the ELF to the given binary using the patchelf utility.

    When running the binary, the new interpreter will be used to load the ELF.

    Parameters:

      - exepath (str) – Path to the binary to patch.

      - interpreter_path (str) – Path to the ld.so dynamic loader.

    Returns:

      A new ELF instance is returned after patching the binary with the external `patchelf` tool.

    Example

        >>> tmpdir = tempfile.mkdtemp()
        >>> ls_path = os.path.join(tmpdir, 'ls')
        >>> _ = shutil.copy(which('ls'), ls_path)
        >>> e = ELF.set_interpreter(ls_path, '/tmp/correct_ld.so')
        >>> e.linker == b'/tmp/correct_ld.so'
        True

  static set_runpath(exepath, runpath) → ELF

    Patches the RUNPATH of the ELF to the given path using the patchelf utility.

    The dynamic loader will look for any needed shared libraries in the given path first, before trying the system library paths. This is useful to run a binary with a different libc binary.

    Parameters:

      - exepath (str) – Path to the binary to patch.

      - runpath (str) – Path containing the needed libraries.

    Returns:

      A new ELF instance is returned after patching the binary with the external `patchelf` tool.

    Example

        >>> tmpdir = tempfile.mkdtemp()
        >>> ls_path = os.path.join(tmpdir, 'ls')
        >>> _ = shutil.copy(which('ls'), ls_path)
        >>> e = ELF.set_runpath(ls_path, './libs')
        >>> e.runpath == b'./libs'
        True

  __getitem__(name)

    Implement dict-like access to header entries

  __init__(path, checksec=True)
  __repr__()

    Return repr(self).

  _get_linked_strtab_section(n)

    Get the section at index #n from the file, throws if it’s not a STRTAB. Used for resolving section links with target type validation.

  _get_linked_symtab_section(n)

    Get the section at index #n from the file, throws if it’s not a SYMTAB/DYNTAB. Used for resolving section links with target type validation.

  _get_section_header(n)

    Find the header of section #n, parse it and return the struct

  _get_section_header_stringtable()

    Get the string table section corresponding to the section header table.

  _get_section_name(section_header)

    Given a section header, find this section’s name in the file’s string table

  _get_segment_header(n)

    Find the header of segment #n, parse it and return the struct

  _identify_file()

    Verify the ELF file and identify its class and endianness.

  _make_gnu_verdef_section(section_header, name)

    Create a GNUVerDefSection

  _make_gnu_verneed_section(section_header, name)

    Create a GNUVerNeedSection

  _make_gnu_versym_section(section_header, name)

    Create a GNUVerSymSection

  _make_section(section_header)

    Create a section object of the appropriate type

  _make_segment(segment_header)

    Create a Segment object of the appropriate type

  _make_sunwsyminfo_table_section(section_header, name)

    Create a SUNWSyminfoTableSection

  _make_symbol_table_index_section(section_header, name)

    Create a SymbolTableIndexSection object

  _make_symbol_table_section(section_header, name)

    Create a SymbolTableSection

  _parse_elf_header()

    Parses the ELF file header and assigns the result to attributes of this object.

  _patch_elf_and_read_maps()

    patch_elf_and_read_maps(self) -> dict

    Read `/proc/self/maps` as if the ELF were executing.

    This is done by replacing the code at the entry point with shellcode which dumps `/proc/self/maps` and exits, and actually executing the binary.

    Returns:

      A `dict` mapping file paths to the lowest address they appear at. Does not do any translation for e.g. QEMU emulation, the raw results are returned.

      If there is not enough space to inject the shellcode in the segment which contains the entry point, returns `{}`.

    Doctests:

    > These tests are just to ensure that our shellcode is correct.

    >     >>> for arch in CAT_PROC_MAPS_EXIT:
    >     ...   context.clear()
    >     ...   with context.local(arch=arch):
    >     ...     sc = shellcraft.cat2("/proc/self/maps")
    >     ...     sc += shellcraft.exit()
    >     ...     sc = asm(sc)
    >     ...     sc = enhex(sc)
    >     ...     assert sc == CAT_PROC_MAPS_EXIT[arch], (arch, sc)

  _populate_functions()

    Builds a dict of ‘functions’ (i.e. symbols of type ‘STT_FUNC’) by function name that map to a tuple consisting of the func address and size in bytes.

  _populate_got()

    Loads the symbols for all relocations.

        >>> libc = ELF(which('bash')).libc
        >>> assert 'strchrnul' in libc.got
        >>> assert 'memcpy' in libc.got
        >>> assert libc.got.strchrnul != libc.got.memcpy

  _populate_libraries()

        >>> from os.path import exists
        >>> bash = ELF(which('bash'))
        >>> all(map(exists, bash.libs.keys()))
        True
        >>> any(map(lambda x: 'libc' in x, bash.libs.keys()))
        True

  _populate_plt()

    Loads the PLT symbols

        >>> path = pwnlib.data.elf.path
        >>> for test in glob(os.path.join(path, 'test-*')):
        ...     test = ELF(test)
        ...     assert '__stack_chk_fail' in test.got, test
        ...     if test.arch != 'ppc':
        ...         assert '__stack_chk_fail' in test.plt, test

  _populate_symbols()

        >>> bash = ELF(which('bash'))
        >>> bash.symbols['_start'] == bash.entry
        True

  _populate_synthetic_symbols()

    Adds symbols from the GOT and PLT to the symbols dictionary.

    Does not overwrite any existing symbols, and prefers PLT symbols.

    Synthetic plt.xxx and got.xxx symbols are added for each PLT and GOT entry, respectively.

    Example:bash.

        >>> bash = ELF(which('bash'))
        >>> bash.symbols.wcscmp == bash.plt.wcscmp
        True
        >>> bash.symbols.wcscmp == bash.symbols.plt.wcscmp
        True
        >>> bash.symbols.stdin  == bash.got.stdin
        True
        >>> bash.symbols.stdin  == bash.symbols.got.stdin
        True

  _read_dwarf_section(section, relocate_dwarf_sections)

    Read the contents of a DWARF section from the stream and return a DebugSectionDescriptor. Apply relocations if asked to.

  _section_offset(n)

    Compute the offset of section #n in the file

  _segment_offset(n)

    Compute the offset of segment #n in the file

  asm(address, assembly)

    Assembles the specified instructions and inserts them into the ELF at the specified address.

    This modifies the ELF in-place. The resulting binary can be saved with `ELF.save()`

  bss(offset=0) → int
    Returns:

      Address of the `.bss` section, plus the specified offset.

  checksec(banner=True, color=True)

    Prints out information in the binary, similar to `checksec.sh`.

    Parameters:

      - banner (bool) – Whether to print the path to the ELF binary.

      - color (bool) – Whether to use colored output.

  close() → None

    Close the ELF file and release all resources associated with it.

  debug(argv=[], *a, **kw) → tube

    Debug the ELF with `gdb.debug()`.

    Parameters:

      - argv (list) – List of arguments to the binary

      - *args – Extra arguments to `gdb.debug()`

      - **kwargs – Extra arguments to `gdb.debug()`

    Returns:

      `tube` – See `gdb.debug()`

  disable_nx()

    Disables NX for the ELF.

    Zeroes out the `PT_GNU_STACK` program header `p_type` field.

  disasm(address, n_bytes) → str

    Returns a string of disassembled instructions at the specified virtual memory address

  dynamic_by_tag(tag) → tag
    Parameters:

      tag (str) – Named `DT_XXX` tag (e.g. `'DT_STRTAB'`).

    Returns:

      `elftools.elf.dynamic.DynamicTag`

  dynamic_string(offset) → bytes

    Fetches an enumerated string from the `DT_STRTAB` table.

    Parameters:

      offset (int) – String index

    Returns:

      `str` – String from the table as raw bytes.

  dynamic_value_by_tag(tag) → int

    Retrieve the value from a dynamic tag a la `DT_XXX`.

    If the tag is missing, returns `None`.

  fit(address, *a, **kw)

    Writes fitted data into the specified address.

    See: `packing.fit()`

  flat(address, *a, **kw)

    Writes a full array of values to the specified address.

    See: `packing.flat()`

  get_dwarf_link()

    Read the .gnu_debuglink section, return an object with filename (as bytes) and checksum (as number) in it.

  get_ehabi_infos()

    Generally, shared library and executable contain 1 .ARM.exidx section. Object file contains many .ARM.exidx sections. So we must traverse every section and filter sections whose type is SHT_ARM_EXIDX.

  get_machine_arch()

    Return the machine architecture, as detected from the ELF header.

  get_section_by_name(name)

    Get a section from the file, by name. Return None if no such section exists.

  get_section_index(section_name)

    Gets the index of the section by name. Return None if no such section name exists.

  get_segment_for_address(address, size=1) → Segment

    Given a virtual address described by a `PT_LOAD` segment, return the first segment which describes the virtual address. An optional `size` may be provided to ensure the entire range falls into the same segment.

    Parameters:

      - address (int) – Virtual address to find

      - size (int) – Number of bytes which must be available after `address` in both the file-backed data for the segment, and the memory region which is reserved for the data.

    Returns:

      Either returns a `segments.Segment` object, or `None`.

  get_shstrndx()

    Find the string table section index for the section header table

  get_supplementary_dwarfinfo(dwarfinfo)

    Read supplementary dwarfinfo, from either the standared .debug_sup section, the GNU proprietary .gnu_debugaltlink, or .gnu_debuglink.

  has_dwarf_link()

    Whether the binary’s debug info is in an external file. Use get_dwarf_link to retrieve the path to it.

  has_ehabi_info()

    Check whether this file appears to have arm exception handler index table.

  has_phantom_bytes()

    The XC16 compiler for the PIC microcontrollers emits DWARF where all odd bytes in all DWARF sections are to be discarded (“phantom”).

    > We don’t know where does the phantom byte discarding fit into the usual chain of section content transforms. There are no XC16/PIC binaries in the corpus with relocations against DWARF, and the DWARF section compression seems to be unsupported by XC16.

  has_section(section_name)

    Section existence check by name, without the overhead of parsing if found.

  iter_notes()
    Yields:

      All the notes in the PT_NOTE segments. Each result is a dictionary- like object with `n_name`, `n_type`, and `n_desc` fields, amongst others.

  iter_properties()
    Yields:

      All the GNU properties in the PT_NOTE segments. Each result is a dictionary- like object with `pr_type`, `pr_datasz`, and `pr_data` fields.

  iter_segments_by_type(t)
    Yields:

      Segments matching the specified type.

  num_sections()

    Number of sections in the file

  num_segments()

    Number of segments in the file

  offset_to_vaddr(offset) → int

    Translates the specified offset to a virtual address.

    Parameters:

      offset (int) – Offset to translate

    Returns:

      int – Virtual address which corresponds to the file offset, or `None`.

    Examples

    This example shows that regardless of changes to the virtual address layout by modifying `ELF.address`, the offset for any given address doesn’t change.

        >>> bash = ELF('/bin/bash')
        >>> bash.address == bash.offset_to_vaddr(0)
        True
        >>> bash.address += 0x123456
        >>> bash.address == bash.offset_to_vaddr(0)
        True

  p16(address, data, *a, **kw)

    Writes a 16-bit integer `data` to the specified `address`

  p32(address, data, *a, **kw)

    Writes a 32-bit integer `data` to the specified `address`

  p64(address, data, *a, **kw)

    Writes a 64-bit integer `data` to the specified `address`

  p8(address, data, *a, **kw)

    Writes a 8-bit integer `data` to the specified `address`

  pack(address, data, *a, **kw)

    Writes a packed integer `data` to the specified `address`

  process(argv=[], *a, **kw) → process

    Execute the binary with `process`. Note that `argv` is a list of arguments, and should not include `argv[0]`.

    Parameters:

      - argv (list) – List of arguments to the binary

      - *args – Extra arguments to `process`

      - **kwargs – Extra arguments to `process`

    Returns:

      `process`

  read(address, count) → bytes

    Read data from the specified virtual address

    Parameters:

      - address (int) – Virtual address to read

      - count (int) – Number of bytes to read

    Returns:

      A `bytes` object, or `None`.

    Examples

    The simplest example is just to read the ELF header.

        >>> bash = ELF(which('bash'))
        >>> bash.read(bash.address, 4)
        b'\x7fELF'

    ELF segments do not have to contain all of the data on-disk that gets loaded into memory.

    First, let’s create an ELF file has some code in two sections.

        >>> assembly = '''
        ... .section .A,"awx"
        ... .global A
        ... A: nop
        ... .section .B,"awx"
        ... .global B
        ... B: int3
        ... '''
        >>> e = ELF.from_assembly(assembly, vma=False)

    By default, these come right after eachother in memory.

        >>> e.read(e.symbols.A, 2)
        b'\x90\xcc'
        >>> e.symbols.B - e.symbols.A
        1

    Let’s move the sections so that B is a little bit further away.

        >>> objcopy = pwnlib.asm._objcopy()
        >>> objcopy += [
        ...     '--change-section-vma', '.B+5',
        ...     '--change-section-lma', '.B+5',
        ...     e.path
        ... ]
        >>> subprocess.check_call(objcopy)
        0

    Now let’s re-load the ELF, and check again

        >>> e = ELF(e.path)
        >>> e.symbols.B - e.symbols.A
        6
        >>> e.read(e.symbols.A, 2)
        b'\x90\x00'
        >>> e.read(e.symbols.A, 7)
        b'\x90\x00\x00\x00\x00\x00\xcc'
        >>> e.read(e.symbols.A, 10)
        b'\x90\x00\x00\x00\x00\x00\xcc\x00\x00\x00'

    Everything is relative to the user-selected base address, so moving things around keeps everything working.

        >>> e.address += 0x1000
        >>> e.read(e.symbols.A, 10)
        b'\x90\x00\x00\x00\x00\x00\xcc\x00\x00\x00'

  save(path=None)

    Save the ELF to a file

        >>> bash = ELF(which('bash'))
        >>> bash.save('/tmp/bash_copy')
        >>> copy = open('/tmp/bash_copy', 'rb')
        >>> bash = open(which('bash'), 'rb')
        >>> bash.read() == copy.read()
        True

  search(needle, writable=False, executable=False) → generator

    Search the ELF’s virtual address space for the specified string.

    Notes

    Does not search empty space between segments, or uninitialized data. This will only return data that actually exists in the ELF file. Searching for a long string of NULL bytes probably won’t work.

    Parameters:

      - needle (bytes) – String to search for.

      - writable (bool) – Search only writable sections.

      - executable (bool) – Search only executable sections.

    Yields:

      An iterator for each virtual address that matches.

    Examples

    An ELF header starts with the bytes `\x7fELF`, so we sould be able to find it easily.

        >>> bash = ELF('/bin/bash')
        >>> bash.address + 1 == next(bash.search(b'ELF'))
        True

    We can also search for string the binary.

        >>> len(list(bash.search(b'GNU bash'))) > 0
        True

    It is also possible to search for instructions in executable sections.

        >>> binary = ELF.from_assembly('nop; mov eax, 0; jmp esp; ret')
        >>> jmp_addr = next(binary.search(asm('jmp esp'), executable = True))
        >>> binary.read(jmp_addr, 2) == asm('jmp esp')
        True

  section(name) → bytes

    Gets data for the named section

    Parameters:

      name (str) – Name of the section

    Returns:

      `str` – String containing the bytes for that section

  string(address) → str

    Reads a null-terminated string from the specified `address`

    Returns:

      A `str` with the string contents (NUL terminator is omitted), or an empty string if no NUL terminator could be found.

  u16(address, *a, **kw)

    Unpacks an integer from the specified `address`.

  u32(address, *a, **kw)

    Unpacks an integer from the specified `address`.

  u64(address, *a, **kw)

    Unpacks an integer from the specified `address`.

  u8(address, *a, **kw)

    Unpacks an integer from the specified `address`.

  unpack(address, *a, **kw)

    Unpacks an integer from the specified `address`.

  vaddr_to_offset(address) → int

    Translates the specified virtual address to a file offset

    Parameters:

      address (int) – Virtual address to translate

    Returns:

      int – Offset within the ELF file which corresponds to the address, or `None`.

    Examples

        >>> bash = ELF(which('bash'))
        >>> bash.vaddr_to_offset(bash.address)
        0
        >>> bash.address += 0x123456
        >>> bash.vaddr_to_offset(bash.address)
        0
        >>> bash.vaddr_to_offset(0) is None
        True

  write(address, data)

    Writes data to the specified virtual address

    Parameters:

      - address (int) – Virtual address to write

      - data (str) – Bytes to write

    Note

    This routine does not check the bounds on the write to ensure that it stays in the same segment.

    Examples

        >>> bash = ELF(which('bash'))
        >>> bash.read(bash.address+1, 3)
        b'ELF'
        >>> bash.write(bash.address, b"HELO")
        >>> bash.read(bash.address, 4)
        b'HELO'

  __weakref__

    list of weak references to the object

  property address

    Address of the lowest segment loaded in the ELF.

    When updated, the addresses of the following fields are also updated:

    - `symbols`

    - `got`

    - `plt`

    - `functions`

    However, the following fields are NOT updated:

    - `segments`

    - `sections`

    Example

        >>> bash = ELF('/bin/bash')
        >>> read = bash.symbols['read']
        >>> text = bash.get_section_by_name('.text').header.sh_addr
        >>> bash.address += 0x1000
        >>> read + 0x1000 == bash.symbols['read']
        True
        >>> text == bash.get_section_by_name('.text').header.sh_addr
        True

    Type:

      `int`

  arch

    Architecture of the file (e.g. `'i386'`, `'arm'`).

    See: `ContextType.arch`

    Type:

      `str`

  property asan

    Whether the current binary was built with Address Sanitizer (`ASAN`).

    Type:

      `bool`

  property aslr

    Whether the current binary is position-independent.

    Type:

      `bool`

  bits = 32

    Bit-ness of the file

    Type:

      `int`

  build

    Linux kernel build commit, if this is a Linux kernel image

    Type:

      `str`

  property buildid

    GNU Build ID embedded into the binary

    Type:

      `bytes`

  bytes = 4

    Pointer width, in bytes

    Type:

      `int`

  property canary

    Whether the current binary uses stack canaries.

    Type:

      `bool`

  config

    Linux kernel configuration, if this is a Linux kernel image

    Type:

      `dict`

  property data

    Raw data of the ELF file.

    See:

      `get_data()`

    Type:

      `bytes`

  property debuginfo

    Whether the current binary has debug information

    Type:

      `bool`

  property dwarf

    DWARF info for the elf

  property elftype

    ELF type (`EXEC`, `DYN`, etc)

    Type:

      `str`

  endian = 'little'

    Endianness of the file (e.g. `'big'`, `'little'`)

    Type:

      `str`

  property entry

    Address of the entry point for the ELF

    Type:

      `int`

  property entrypoint

    Address of the entry point for the ELF

    Type:

      `int`

  property execstack

    Whether dynamically loading the current binary will make the stack executable.

    This is based on the presence of a program header `PT_GNU_STACK`, its setting, and the default stack permissions for the architecture.

    If `PT_GNU_STACK` is persent, the stack permissions are set according to it:

        case PT_GNU_STACK:
          stack_flags = ph->p_flags;
          break;

    Else, the stack permissions are set according to the architecture defaults as defined by `DEFAULT_STACK_PERMS`:

        /* On most platforms presume that PT_GNU_STACK is absent and the stack is
         * executable.  Other platforms default to a nonexecutable stack and don't
         * need PT_GNU_STACK to do so.  */
        uint_fast16_t stack_flags = DEFAULT_STACK_PERMS;

    By searching the source for `DEFAULT_STACK_PERMS`, we can see which architectures have which settings.

        $ git grep '#define DEFAULT_STACK_PERMS' | grep -v PF_X
        sysdeps/aarch64/stackinfo.h:    #define DEFAULT_STACK_PERMS (PF_R|PF_W)
        sysdeps/arc/stackinfo.h:        #define DEFAULT_STACK_PERMS (PF_R|PF_W)
        sysdeps/csky/stackinfo.h:       #define DEFAULT_STACK_PERMS (PF_R|PF_W)
        sysdeps/ia64/stackinfo.h:       #define DEFAULT_STACK_PERMS (PF_R|PF_W)
        sysdeps/loongarch/stackinfo.h:  #define DEFAULT_STACK_PERMS (PF_R | PF_W)
        sysdeps/nios2/stackinfo.h:      #define DEFAULT_STACK_PERMS (PF_R|PF_W)
        sysdeps/riscv/stackinfo.h:      #define DEFAULT_STACK_PERMS (PF_R | PF_W)

    Type:

      `bool`

  executable

    `True` if the ELF is an executable

  property executable_segments

    List of all segments which are executable.

    See:

      `ELF.segments`

    Type:

      `list`

  file

    Open handle to the ELF file on disk

    Type:

      `file`

  property fortify

    Whether the current binary was built with Fortify Source (`-DFORTIFY`).

    Type:

      `bool`

  functions = {}

    `dotdict` of `name` to `Function` for each function in the ELF

  got = {}

    `dotdict` of `name` to `address` for all Global Offset Table (GOT) entries

  property ibt

    Whether the current binary was built with Indirect Branch Tracking (`IBT`)

    Type:

      `bool`

  property libc

    If this `ELF` imports any libraries which contain `/libc[.-]`, and we can determine the appropriate path to it on the local system, returns a new `ELF` object pertaining to that library. Prints the checksec output of the library if it was printed for the original ELF too.

    If not found, the value will be `None`.

    Warning

    Getting this attribute actually runs the executable. Make sure that you trust the binary you are exploiting (see `ELF.libs`).

    Type:

      `ELF`

  property libc_start_main_return

    Address of the return address into __libc_start_main from main.

        >>> bash = ELF(which('bash'))
        >>> libc = bash.libc
        >>> libc.libc_start_main_return > 0
        True

    Try to find the return address from main into __libc_start_main. The heuristic to find the call to the function pointer of main is to list all calls inside __libc_start_main, find the call to exit after the call to main and select the previous call.

    Type:

      `int`

  library

    `True` if the ELF is a shared library

  property libs

    Dictionary of `{path: address}` for every library loaded for this ELF.

    Warning

    Getting this attribute actually runs the executable. Make sure that you trust the binary you are exploiting. If it adds itself as `DT_NEEDED`, has overlapping segments, ambiguous headers, or employs text relocations, it can run arbitrary code even though you are just inspecting it. Running is the only reliable way to ensure all the libraries are loaded from the correct paths, because some of them may change loading logic.

    Exploitability first noticed at CWTE CTF 2025.

  linker = None

    Path to the linker for the ELF

  property maps

    Dictionary of `{name: address}` for every mapping in this ELF’s address space.

    Warning

    Getting this attribute actually runs the executable. Make sure that you trust the binary you are exploiting (see `ELF.libs`).

  memory

    IntervalTree which maps all of the loaded memory segments

  mmap

    Memory-mapped copy of the ELF file on disk

    Type:

      `mmap.mmap`

  property msan

    Whether the current binary was built with Memory Sanitizer (`MSAN`).

    Type:

      `bool`

  native

    Whether this ELF should be able to run natively

  property non_writable_segments

    List of all segments which are NOT writeable.

    See:

      `ELF.segments`

    Type:

      `list`

  property nx

    Whether the current binary uses NX protections.

    Specifically, we are checking for `READ_IMPLIES_EXEC` being set by the kernel, as a result of honoring `PT_GNU_STACK` in the kernel.

    `READ_IMPLIES_EXEC` is set, according to a set of architecture specific rules, that depend on the CPU features, and the presence of `PT_GNU_STACK`.

    Unfortunately, `ELF` is not context-aware, so it’s not always possible to determine whether the process of a binary that’s missing `PT_GNU_STACK` will have NX or not.

    The rules are as follows:

    > ELF arch | linux | GNU_STACK | other | NX
    > --- | --- | --- | --- | ---
    > i386 | < 5.8 [1] | non-exec |  | enabled
    > exec / missing |  | disabled
    > >= 5.8 [2] | exec / non-exec |  | enabled
    > missing |  | disabled
    > amd64 | < 5.8 [1] | non-exec |  | enabled
    > exec / missing |  | disabled
    > >= 5.8 [2] | exec / non-exec / missing |  | enabled
    > arm | < 5.8 [3] | non-exec* |  | enabled
    > exec / missing |  | disabled
    > >= 5.8 [4] | exec / non-exec* |  | enabled
    > missing |  | disabled
    > mips | < 5.18 [5] | non-exec* |  | enabled
    > exec / missing |  | disabled
    > >= 5.18 [6] | exec / non-exec* |  | enabled
    > missing |  | disabled
    > powerpc | [7] | non-exec / exec |  | enabled
    > missing |  | disabled
    > powerpc64 | [7] | exec / non-exec / missing |  | enabled
    > ia64 | [8] | non-exec |  | enabled
    > exec / missing | e_flags & EF_IA_64_LINUX_EXECUTABLE_STACK == 0 | enabled
    > e_flags & EF_IA_64_LINUX_EXECUTABLE_STACK != 0 | disabled
    > the rest | [9] | exec / non-exec / missing |  | enabled

    > * Hardware limitations are ignored.

    If `READ_IMPLIES_EXEC` is set, then all readable pages are executable.

        if (elf_read_implies_exec(loc->elf_ex, executable_stack))
            current->personality |= READ_IMPLIES_EXEC;

    [1] (1,2)

    source

        #define elf_read_implies_exec(ex, executable_stack) \
            (executable_stack != EXSTACK_DISABLE_X)

    [2] (1,2)

    source

        #define elf_read_implies_exec(ex, executable_stack) \
            (mmap_is_ia32() && executable_stack == EXSTACK_DEFAULT)

    mmap_is_ia32():

        /*
         * True on X86_32 or when emulating IA32 on X86_64
         */
        static inline int mmap_is_ia32(void)

    [3]

    source

        int arm_elf_read_implies_exec(int executable_stack)
        {
            if (executable_stack != EXSTACK_DISABLE_X)
                return 1;
            if (cpu_architecture() < CPU_ARCH_ARMv6)
                return 1;
            return 0;
        }

    [4]

    source

        int arm_elf_read_implies_exec(int executable_stack)
        {
            if (executable_stack == EXSTACK_DEFAULT)
                return 1;
            if (cpu_architecture() < CPU_ARCH_ARMv6)
                return 1;
            return 0;
        }

    [5]

    source

        int mips_elf_read_implies_exec(void *elf_ex, int exstack)
        {
            if (exstack != EXSTACK_DISABLE_X) {
                /* The binary doesn't request a non-executable stack */
                return 1;
            }
            if (!cpu_has_rixi) {
                /* The CPU doesn't support non-executable memory */
                return 1;
            }
            return 0;
        }

    [6]

    source

        int mips_elf_read_implies_exec(void *elf_ex, int exstack)
        {
            /*
             * Set READ_IMPLIES_EXEC only on non-NX systems that
             * do not request a specific state via PT_GNU_STACK.
             */
            return (!cpu_has_rixi && exstack == EXSTACK_DEFAULT);
        }

    [7] (1,2)

    source

        #ifdef __powerpc64__
        /* stripped */
        # define elf_read_implies_exec(ex, exec_stk) (is_32bit_task() ? \
                (exec_stk == EXSTACK_DEFAULT) : 0)
        #else
        # define elf_read_implies_exec(ex, exec_stk) (exec_stk == EXSTACK_DEFAULT)
        #endif /* __powerpc64__ */

    [8]

    source

        #define elf_read_implies_exec(ex, executable_stack)                                 \
            ((executable_stack!=EXSTACK_DISABLE_X) && ((ex).e_flags & EF_IA_64_LINUX_EXECUTABLE_STACK) != 0)

    EF_IA_64_LINUX_EXECUTABLE_STACK:

        #define EF_IA_64_LINUX_EXECUTABLE_STACK     0x1     /* is stack (& heap) executable by default? */

    [9]

    source

        # define elf_read_implies_exec(ex, have_pt_gnu_stack)       0

    Type:

      `bool`

  os

    Operating system of the ELF

  property packed

    Whether the current binary is packed with UPX.

    Type:

      `bool`

  path = '/path/to/the/file'

    Path to the file

    Type:

      `str`

  property pie

    Whether the current binary is position-independent.

    Type:

      `bool`

  plt = {}

    `dotdict` of `name` to `address` for all Procedure Linkage Table (PLT) entries

  property relro

    Whether the current binary uses RELRO protections.

    This requires both presence of the dynamic tag `DT_BIND_NOW`, and a `GNU_RELRO` program header.

    The ELF Specification describes how the linker should resolve symbols immediately, as soon as a binary is loaded. This can be emulated with the `LD_BIND_NOW=1` environment variable.

    > `DT_BIND_NOW`

    > If present in a shared object or executable, this entry instructs the dynamic linker to process all relocations for the object containing this entry before transferring control to the program. The presence of this entry takes precedence over a directive to use lazy binding for this object when specified through the environment or via `dlopen(BA_LIB)`.

    > (page 81)

    Separately, an extension to the GNU linker allows a binary to specify a PT_GNU_RELRO program header, which describes the region of memory which is to be made read-only after relocations are complete.

    Finally, a new-ish extension which doesn’t seem to have a canonical source of documentation is DF_BIND_NOW, which has supposedly superceded `DT_BIND_NOW`.

    > `DF_BIND_NOW`

    > If set in a shared object or executable, this flag instructs the dynamic linker to process all relocations for the object containing this entry before transferring control to the program. The presence of this entry takes precedence over a directive to use lazy binding for this object when specified through the environment or via `dlopen(BA_LIB)`.

        >>> path = pwnlib.data.elf.relro.path
        >>> for test in glob(os.path.join(path, 'test-*')):
        ...     e = ELF(test)
        ...     expected = os.path.basename(test).split('-')[2]
        ...     actual = str(e.relro).lower()
        ...     assert actual == expected

    Type:

      `bool`

  property rpath

    Whether the current binary has an `RPATH`.

    Type:

      `bool`

  property runpath

    Whether the current binary has a `RUNPATH`.

    Type:

      `bool`

  property rwx_segments

    List of all segments which are writeable and executable.

    See:

      `ELF.segments`

    Type:

      `list`

  property sections

    A list of `elftools.elf.sections.Section` objects for the segments in the ELF.

    Type:

      `list`

  property segments

    A list of `elftools.elf.segments.Segment` objects for the segments in the ELF.

    Type:

      `list`

  property shadowstack

    Whether the current binary was built with Shadow Stack (`SHSTK`)

    Type:

      `bool`

  property start

    Address of the entry point for the ELF

    Type:

      `int`

  statically_linked

    `True` if the ELF is statically linked

  property stripped

    Whether the current binary has been stripped of symbols

    Type:

      `bool`

  property sym

    Alias for `ELF.symbols`

    Type:

      `dotdict`

  symbols = {}

    `dotdict` of `name` to `address` for all symbols in the ELF

  property ubsan

    Whether the current binary was built with Undefined Behavior Sanitizer (`UBSAN`).

    Type:

      `bool`

  version

    Linux kernel version, if this is a Linux kernel image

    Type:

      `tuple`

  property writable_segments

    List of all segments which are writeable.

    See:

      `ELF.segments`

    Type:

      `list`

class pwnlib.elf.elf.Function(name, address, size, elf=None)

  Encapsulates information about a function in an `ELF` binary.

  Parameters:

    - name (str) – Name of the function

    - address (int) – Address of the function

    - size (int) – Size of the function, in bytes

    - elf (ELF) – Encapsulating ELF object

  __init__(name, address, size, elf=None)
  __repr__()

    Return repr(self).

  __weakref__

    list of weak references to the object

  address

    Address of the function in the encapsulating ELF

  elf

    Encapsulating ELF object

  name

    Name of the function

  size

    Size of the function, in bytes

class pwnlib.elf.elf.dotdict

  Wrapper to allow dotted access to dictionary elements.

  Is a real `dict` object, but also serves up keys as attributes when reading attributes.

  Supports recursive instantiation for keys which contain dots.

  Example

      >>> x = pwnlib.elf.elf.dotdict()
      >>> isinstance(x, dict)
      True
      >>> x['foo'] = 3
      >>> x.foo
      3
      >>> x['bar.baz'] = 4
      >>> x.bar.baz
      4

  __weakref__

    list of weak references to the object