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'
>>> hex(leaker.d(0))
'0x464c457f'
>>> hex(leaker.clearb(1))
'0x45'
>>> hex(leaker.d(0))
leaking 0x1
'0x464c457f'
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]

call(address, field) => int or str

Leak an entire structure, or structure field.

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