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()
-
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
– Seegdb.debug()
-
disable_nx
()[源代码]¶ Disables NX for the ELF.
Zeroes out the
PT_GNU_STACK
program headerp_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.
参数: 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.
参数: 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.
-
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
-
process
(argv=[], *a, **kw) → process[源代码]¶ Execute the binary with
process
. Note thatargv
is a list of arguments, and should not includeargv[0]
.参数: 返回:
-
read
(address, count) → bytes[源代码]¶ Read data from the specified virtual address
参数: 返回: A
str
object, orNone
.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.
参数: 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.
-
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
参数: 注解
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
-
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_segments
[源代码]¶ list
– List of all segments which are executable.- See:
ELF.segments
-
libc
[源代码]¶ ELF
– If thisELF
imports any libraries which contain'libc[.-]
, and we can determine the appropriate path to it on the local system, returns a newELF
object pertaining to that library.If not found, the value will be
None
.
-
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 honoringPT_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;
-
relro
[源代码]¶ bool
– Whether the current binary uses RELRO protections.This requires both presence of the dynamic tag
DT_BIND_NOW
, and aGNU_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
-
rwx_segments
[源代码]¶ list
– List of all segments which are writeable and executable.- See:
ELF.segments
-
sym
[源代码]¶ dotdict
– Alias forELF.symbols
-
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.参数:
-
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