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', mode='rb')
>>> @pwnlib.memleak.MemLeak
... def leaker(addr):
...     print("leaking 0x%x" % addr)
...     return binsh[addr:addr+4]
>>> leaker.s(0)[:4]
leaking 0x0
leaking 0x4
b'\x7fELF'
>>> leaker[:4]
b'\x7fELF'
>>> hex(leaker.d(0))
'0x464c457f'
>>> hex(leaker.clearb(1))
'0x45'
>>> hex(leaker.d(0))
leaking 0x1
'0x464c457f'
>>> @pwnlib.memleak.MemLeak
... def leaker(addr):
...     if addr & 0xff == 0:
...         print("leaker failed 0x%x" % addr)
...         return
...     print("leaking 0x%x" % addr)
...     return binsh[addr:addr+4]
>>> leaker.d(0)
leaker failed 0x0
>>> leaker.d(0x100) == pwnlib.util.packing.u32(binsh[0x100:0x104])
leaker failed 0x100
leaking 0xff
leaking 0x103
True
>>> leaker[0xf0:0x110] == binsh[0xf0:0x110] == leaker.n(0xf0, 0x20)
leaking 0xf0
leaking 0xf4
leaking 0xf8
leaking 0xfc
leaking 0x107
leaking 0x10b
leaking 0x10f
True
>>> import ctypes
>>> class MyStruct(ctypes.Structure):
...     _pack_ = True
...     _fields_ = [("a", ctypes.c_char),
...                 ("b", ctypes.c_uint32),]
>>> leaker.field(0x101, MyStruct.b) == leaker.d(0x102)
True
b(addr, ndx=0) → int[source]

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

Examples

>>> import string
>>> data = string.ascii_lowercase.encode('utf8')
>>> 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: b'a'}
>>> l.n(0, 1) == b'a'
True
>>> l.clearb(0) == unpack(b'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: b'a', 1: b'b', 2: b'c', 3: b'd'}
>>> l.n(0, 4) == b'abcd'
True
>>> l.cleard(0) == unpack(b'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: b'')
>>> c.cache = {x: b'x' for x in range(0x100, 0x108)}
>>> c.clearq(0x100) == unpack(b'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: b'a', 1: b'b'}
>>> l.n(0, 2) == b'ab'
True
>>> l.clearw(0) == unpack(b'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.encode('utf8')
>>> l = MemLeak(lambda a: data[a:a+8], reraise=False)
>>> l.d(0) == unpack(b'abcd', 32)
True
>>> l.d(22) == unpack(b'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.
n(addr, ndx = 0) → bytes[source]

Leak numb bytes at addr.

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

Examples

>>> import string
>>> data = string.ascii_lowercase.encode('ascii')
>>> l = MemLeak(lambda a: data[a:a+4], reraise=False)
>>> l.n(0, 1) == b'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.encode('utf8')
>>> l = MemLeak(lambda a: data[a:a+16], reraise=False)
>>> l.q(0) == unpack(b'abcdefgh', 64)
True
>>> l.q(18) == unpack(b'stuvwxyz', 64)
True
>>> l.q(19) is None
True
raw(addr, numb) → list[source]

Return a list of numb leaked bytes at addr. Bytes that could not be leaked are replaced by None.

rawb(addr)[source]

raw(addr) -> bytes or None

Returns the byte at addr or None if it could not be leaked.

s(addr) → bytes[source]

Leak bytes at addr until failure or a nullbyte is found

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

Examples

>>> data = b"Hello\x00World"
>>> l = MemLeak(lambda a: data[a:a+4], reraise=False)
>>> l.s(0) == b"Hello"
True
>>> l.s(5) == b""
True
>>> l.s(6) == b"World"
True
>>> l.s(999) == b""
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: b'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, b'H\x00ello')
>>> l.cache == {0: b'H', 1: b'\x00', 2: b'e', 3: b'l', 4: b'l', 5: b'o', 6: b'\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: b'')
>>> l.cache == {}
True
>>> l.setw(33, 0x41)
>>> l.cache == {33: b'A', 34: b'\x00'}
True
struct(address, struct)[source]

struct(address, struct) => structure object

Leak an entire structure.

Parameters:
  • address (int) – Address of structure in memory
  • struct (class) – A ctypes structure to be instantiated with leaked data
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.encode('utf8')
>>> l = MemLeak(lambda a: data[a:a+4], reraise=False)
>>> l.w(0) == unpack(b'ab', 16)
True
>>> l.w(24) == unpack(b'yz', 16)
True
>>> l.w(25) is None
True