pwnlib.memleak — Helper class for leaking memory

class pwnlib.memleak.MemLeak(f, search_range=20, reraise=True)[source]

MemLeak is a caching and heuristic tool for exploiting memory leaks.

It can be used as a decorator, around functions of the form:

def some_leaker(addr):
... return data_as_string_or_None

It will cache leaked memory (which requires either non-randomized static data or a continouous session). If required, dynamic or known data can be set with the set-functions, but this is usually not required. If a byte cannot be recovered, it will try to leak nearby bytes in the hope that the byte is recovered as a side-effect.

Parameters:
  • f (function) – The leaker function.
  • search_range (int) – How many bytes to search backwards in case an address does not work.
  • reraise (bool) – Whether to reraise call pwnlib.log.warning() in case the leaker function throws an exception.

Example

>>> import pwnlib
>>> binsh = pwnlib.util.misc.read('/bin/sh')
>>> @pwnlib.memleak.MemLeak
... def leaker(addr):
...     print "leaking 0x%x" % addr
...     return binsh[addr:addr+4]
>>> leaker.s(0)[:4]
leaking 0x0
leaking 0x4
'\x7fELF'
>>> leaker[:4]
'\x7fELF'
>>> hex(leaker.d(0))
'0x464c457f'
>>> hex(leaker.clearb(1))
'0x45'
>>> hex(leaker.d(0))
leaking 0x1
'0x464c457f'
>>> @pwnlib.memleak.MemLeak
... def leaker_nonulls(addr):
...     print "leaking 0x%x" % addr
...     if addr & 0xff == 0:
...         return None
...     return binsh[addr:addr+4]
>>> leaker_nonulls.d(0) == None
leaking 0x0
True
>>> leaker_nonulls[0x100:0x104] == binsh[0x100:0x104]
leaking 0x100
leaking 0xff
leaking 0x103
True
static NoNewlines(function)[source]

Wrapper for leak functions such that addresses which contain newline bytes are not leaked.

This is useful if the address which is used for the leak is provided by e.g. fgets().

static NoNulls(function)[source]

Wrapper for leak functions such that addresses which contain NULL bytes are not leaked.

This is useful if the address which is used for the leak is read in via a string-reading function like scanf("%s") or smilar.

static NoWhitespace(function)[source]

Wrapper for leak functions such that addresses which contain whitespace bytes are not leaked.

This is useful if the address which is used for the leak is read in via e.g. scanf().

static String(function)[source]

Wrapper for leak functions which leak strings, such that a NULL terminator is automaticall added.

This is useful if the data leaked is printed out as a NULL-terminated string, via e.g. printf().

b(addr, ndx = 0) → int[source]

Leak byte at ((uint8_t*) addr)[ndx]

Examples

>>> import string
>>> data = string.ascii_lowercase
>>> l = MemLeak(lambda a: data[a:a+2], reraise=False)
>>> l.b(0) == ord('a')
True
>>> l.b(25) == ord('z')
True
>>> l.b(26) is None
True
clearb(addr, ndx = 0) → int[source]

Clears byte at ((uint8_t*)addr)[ndx] from the cache and returns the removed value or None if the address was not completely set.

Examples

>>> l = MemLeak(lambda a: None)
>>> l.cache = {0:'a'}
>>> l.n(0,1) == 'a'
True
>>> l.clearb(0) == unpack('a', 8)
True
>>> l.cache
{}
>>> l.clearb(0) is None
True
cleard(addr, ndx = 0) → int[source]

Clears dword at ((uint32_t*)addr)[ndx] from the cache and returns the removed value or None if the address was not completely set.

Examples

>>> l = MemLeak(lambda a: None)
>>> l.cache = {0:'a', 1: 'b', 2: 'c', 3: 'd'}
>>> l.n(0, 4) == 'abcd'
True
>>> l.cleard(0) == unpack('abcd', 32)
True
>>> l.cache
{}
clearq(addr, ndx = 0) → int[source]

Clears qword at ((uint64_t*)addr)[ndx] from the cache and returns the removed value or None if the address was not completely set.

Examples

>>> c = MemLeak(lambda addr: '')
>>> c.cache = {x:'x' for x in range(0x100, 0x108)}
>>> c.clearq(0x100) == unpack('xxxxxxxx', 64)
True
>>> c.cache == {}
True
clearw(addr, ndx = 0) → int[source]

Clears word at ((uint16_t*)addr)[ndx] from the cache and returns the removed value or None if the address was not completely set.

Examples

>>> l = MemLeak(lambda a: None)
>>> l.cache = {0:'a', 1: 'b'}
>>> l.n(0, 2) == 'ab'
True
>>> l.clearw(0) == unpack('ab', 16)
True
>>> l.cache
{}
d(addr, ndx = 0) → int[source]

Leak dword at ((uint32_t*) addr)[ndx]

Examples

>>> import string
>>> data = string.ascii_lowercase
>>> l = MemLeak(lambda a: data[a:a+8], reraise=False)
>>> l.d(0) == unpack('abcd', 32)
True
>>> l.d(22) == unpack('wxyz', 32)
True
>>> l.d(23) is None
True
field(address, obj)[source]

field(address, field) => a structure field.

Leak a field from a structure.

Parameters:
  • address (int) – Base address to calculate offsets from
  • field (obj) – Instance of a ctypes field
Return Value:
The type of the return value will be dictated by the type of field.
field_compare(address, obj, expected)[source]

field_compare(address, field, expected) ==> bool

Leak a field from a structure, with an expected value. As soon as any mismatch is found, stop leaking the structure.

Parameters:
  • address (int) – Base address to calculate offsets from
  • field (obj) – Instance of a ctypes field
  • expected (int,str) – Expected value
Return Value:
The type of the return value will be dictated by the type of field.
n(addr, ndx = 0) → str[source]

Leak numb bytes at addr.

Returns:A string with the leaked bytes, will return None if any are missing

Examples

>>> import string
>>> data = string.ascii_lowercase
>>> l = MemLeak(lambda a: data[a:a+4], reraise=False)
>>> l.n(0,1) == 'a'
True
>>> l.n(0,26) == data
True
>>> len(l.n(0,26)) == 26
True
>>> l.n(0,27) is None
True
p(addr, ndx = 0) → int[source]

Leak a pointer-width value at ((void**) addr)[ndx]

q(addr, ndx = 0) → int[source]

Leak qword at ((uint64_t*) addr)[ndx]

Examples

>>> import string
>>> data = string.ascii_lowercase
>>> l = MemLeak(lambda a: data[a:a+16], reraise=False)
>>> l.q(0) == unpack('abcdefgh', 64)
True
>>> l.q(18) == unpack('stuvwxyz', 64)
True
>>> l.q(19) is None
True
raw(addr, numb) → list[source]

Leak numb bytes at addr

s(addr) → str[source]

Leak bytes at addr until failure or a nullbyte is found

Returns:A string, without a NULL terminator. The returned string will be empty if the first byte is a NULL terminator, or if the first byte could not be retrieved.

Examples

>>> data = "Hello\x00World"
>>> l = MemLeak(lambda a: data[a:a+4], reraise=False)
>>> l.s(0) == "Hello"
True
>>> l.s(5) == ""
True
>>> l.s(6) == "World"
True
>>> l.s(999) == ""
True
setb(addr, val, ndx=0)[source]

Sets byte at ((uint8_t*)addr)[ndx] to val in the cache.

Examples

>>> l = MemLeak(lambda x: '')
>>> l.cache == {}
True
>>> l.setb(33, 0x41)
>>> l.cache == {33: 'A'}
True
setd(addr, val, ndx=0)[source]

Sets dword at ((uint32_t*)addr)[ndx] to val in the cache.

Examples

See setw().

setq(addr, val, ndx=0)[source]

Sets qword at ((uint64_t*)addr)[ndx] to val in the cache.

Examples

See setw().

sets(addr, val, null_terminate=True)[source]

Set known string at addr, which will be optionally be null-terminated

Note that this method is a bit dumb about how it handles the data. It will null-terminate the data, but it will not stop at the first null.

Examples

>>> l = MemLeak(lambda x: '')
>>> l.cache == {}
True
>>> l.sets(0, 'H\x00ello')
>>> l.cache == {0: 'H', 1: '\x00', 2: 'e', 3: 'l', 4: 'l', 5: 'o', 6: '\x00'}
True
setw(addr, val, ndx=0)[source]

Sets word at ((uint16_t*)addr)[ndx] to val in the cache.

Examples

>>> l = MemLeak(lambda x: '')
>>> l.cache == {}
True
>>> l.setw(33, 0x41)
>>> l.cache == {33: 'A', 34: '\x00'}
True
struct(address, struct)[source]

struct(address, struct) => structure object Leak an entire structure. :param address: Addess of structure in memory :type address: int :param struct: A ctypes structure to be instantiated with leaked data :type struct: class

Return Value:
An instance of the provided struct class, with the leaked data decoded
w(addr, ndx = 0) → int[source]

Leak word at ((uint16_t*) addr)[ndx]

Examples

>>> import string
>>> data = string.ascii_lowercase
>>> l = MemLeak(lambda a: data[a:a+4], reraise=False)
>>> l.w(0) == unpack('ab', 16)
True
>>> l.w(24) == unpack('yz', 16)
True
>>> l.w(25) is None
True