pwnlib.elf.elf — ELF 文件

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)
'ELF'
>>> e.asm(e.address, 'ret')
>>> e.save('/tmp/quiet-cat')
>>> disasm(file('/tmp/quiet-cat','rb').read(1))
'   0:   c3                      ret'

Module Members

class pwnlib.elf.elf.ELF(path, checksec=True)[源代码]

Bases: elftools.elf.elffile.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
asm(address, assembly)[源代码]

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

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

bss(offset=0) → int[源代码]
返回:Address of the .bss section, plus the specified offset.
checksec(banner=True)[源代码]

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

参数:banner (bool) – Whether to print the path to the ELF binary.
debug(argv=[], *a, **kw) → tube[源代码]

Debug the ELF with gdb.debug().

参数:
  • argv (list) – List of arguments to the binary
  • *args – Extra arguments to gdb.debug()
  • **kwargs – Extra arguments to gdb.debug()
返回:

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[源代码]
参数:tag (str) – Named DT_XXX tag (e.g. 'DT_STRTAB').
返回:elftools.elf.dynamic.DynamicTag
dynamic_string(offset) → bytes[源代码]

Fetches an enumerated string from the DT_STRTAB table.

参数:offset (int) – String index
返回: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()

static from_assembly(assembly) → ELF[源代码]

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

参数:
  • 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.

参数:
  • bytes (str) – Shellcode byte string
  • vma (int) – Desired base address for the ELF.

Example

>>> e = ELF.from_bytes('\x90\xcd\x80', vma=0xc000)
>>> print(e.disasm(e.entry, 3))
    c000:       90                      nop
    c001:       cd 80                   int    0x80
get_section_by_name(name)[源代码]

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

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.

参数:offset (int) – Offset to translate
返回: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].

参数:
  • argv (list) – List of arguments to the binary
  • *args – Extra arguments to process
  • **kwargs – Extra arguments to process
返回:

process

read(address, count) → bytes[源代码]

Read data from the specified virtual address

参数:
  • address (int) – Virtual address to read
  • count (int) – Number of bytes to read
返回:

A str object, or None.

Examples

The simplest example is just to read the ELF header.

>>> bash = ELF(which('bash'))
>>> bash.read(bash.address, 4)
'\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)
'\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)
'\x90\x00'
>>> e.read(e.symbols.A, 7)
'\x90\x00\x00\x00\x00\x00\xcc'
>>> e.read(e.symbols.A, 10)
'\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)
'\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 = file('/tmp/bash_copy')
>>> bash = file(which('bash'))
>>> bash.read() == copy.read()
True
search(needle, writable = 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.

参数:
  • needle (str) – String to search for.
  • writable (bool) – Search only writable 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('ELF'))
True

We can also search for string the binary.

>>> len(list(bash.search('GNU bash'))) > 0
True
section(name) → bytes[源代码]

Gets data for the named section

参数:name (str) – Name of the section
返回:str – String containing the bytes for that section
string(address) → str[源代码]

Reads a null-terminated string from the specified address

返回: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

参数:address (int) – Virtual address to translate
返回: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

参数:
  • address (int) – Virtual address to write
  • data (str) – Bytes to write

注解

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)
'ELF'
>>> bash.write(bash.address, "HELO")
>>> bash.read(bash.address, 4)
'HELO'
address[源代码]

int – Address of the lowest segment loaded in the ELF.

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

However, the following fields are NOT updated:

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
arch = None[源代码]

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

See: ContextType.arch

asan[源代码]

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

aslr[源代码]

bool – Whether the current binary is position-independent.

bits = 32[源代码]

int – Bit-ness of the file

build = None[源代码]

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

buildid[源代码]

str – GNU Build ID embedded into the binary

bytes = 4[源代码]

int – Pointer width, in bytes

canary[源代码]

bool – Whether the current binary uses stack canaries.

config = None[源代码]

dict – Linux kernel configuration, if this is a Linux kernel image

data[源代码]

str – Raw data of the ELF file.

See:
get_data()
dwarf[源代码]

DWARF info for the elf

elftype[源代码]

str – ELF type (EXEC, DYN, etc)

endian = 'little'[源代码]

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

entry[源代码]

int – Address of the entry point for the ELF

entrypoint[源代码]

int – Address of the entry point for the ELF

execstack[源代码]

bool – Whether the current binary uses an executable stack.

This is based on the presence of a program header PT_GNU_STACK being present, and its setting.

PT_GNU_STACK

The p_flags member specifies the permissions on the segment containing the stack and is used to indicate wether the stack should be executable. The absense of this header indicates that the stack will be executable.

In particular, if the header is missing the stack is executable. If the header is present, it may explicitly mark that the stack is executable.

This is only somewhat accurate. When using the GNU Linker, it usees DEFAULT_STACK_PERMS to decide whether a lack of PT_GNU_STACK should mark the stack as executable:

/* 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:31:#define DEFAULT_STACK_PERMS (PF_R|PF_W)
sysdeps/nios2/stackinfo.h:31:#define DEFAULT_STACK_PERMS (PF_R|PF_W)
sysdeps/tile/stackinfo.h:31:#define DEFAULT_STACK_PERMS (PF_R|PF_W)
executable = None[源代码]

True if the ELF is an executable

executable_segments[源代码]

list – List of all segments which are executable.

See:
ELF.segments
file = None[源代码]

file – Open handle to the ELF file on disk

fortify[源代码]

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

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

libc[源代码]

ELF – 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.

If not found, the value will be None.

library = None[源代码]

True if the ELF is a shared library

memory = None[源代码]

IntervalTree which maps all of the loaded memory segments

mmap = None[源代码]

mmap.mmap – Memory-mapped copy of the ELF file on disk

msan[源代码]

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

native = None[源代码]

Whether this ELF should be able to run natively

non_writable_segments[源代码]

list – List of all segments which are NOT writeable.

See:
ELF.segments
nx[源代码]

bool – 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.

The Linux kernel directly honors PT_GNU_STACK to mark the stack as executable.

case PT_GNU_STACK:
    if (elf_ppnt->p_flags & PF_X)
        executable_stack = EXSTACK_ENABLE_X;
    else
        executable_stack = EXSTACK_DISABLE_X;
    break;

Additionally, it then sets read_implies_exec, so that all readable pages are executable.

if (elf_read_implies_exec(loc->elf_ex, executable_stack))
    current->personality |= READ_IMPLIES_EXEC;
packed[源代码]

bool – Whether the current binary is packed with UPX.

path = '/path/to/the/file'[源代码]

str – Path to the file

pie[源代码]

bool – Whether the current binary is position-independent.

plt = {}[源代码]

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

relro[源代码]

bool – 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
rpath[源代码]

bool – Whether the current binary has an RPATH.

runpath[源代码]

bool – Whether the current binary has a RUNPATH.

rwx_segments[源代码]

list – List of all segments which are writeable and executable.

See:
ELF.segments
sections[源代码]

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

segments[源代码]

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

start[源代码]

int – Address of the entry point for the ELF

statically_linked = None[源代码]

True if the ELF is a statically linked executable

sym[源代码]

dotdict – Alias for ELF.symbols

symbols = {}[源代码]

dotdict of name to address for all symbols in the ELF

ubsan[源代码]

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

version = None[源代码]

tuple – Linux kernel version, if this is a Linux kernel image

writable_segments[源代码]

list – List of all segments which are writeable.

See:
ELF.segments
class pwnlib.elf.elf.Function(name, address, size, elf=None)[源代码]

Encapsulates information about a function in an ELF binary.

参数:
  • 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
address = None[源代码]

Address of the function in the encapsulating ELF

elf = None[源代码]

Encapsulating ELF object

name = None[源代码]

Name of the function

size = None[源代码]

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