pwnlib.libc.glibc — Convenient Functions for Glibc

Some glibc related convenient functions.

class pwnlib.libc.glibc.ExitDtorList(func: int, guard: int, obj: int, lmap: int, nextl: int)[source]

Craft a struct dtor_list object. glibc will invoke functions in it if it’s not null when exits. Note that to avoid program aborting, the ExitDtorList pointer must be free-able. Check struct dtor_list definitions.

Parameters:
  • func (int) – A pointer to function to execute.

  • guard (int) – Process POINTER_GUARD.

  • obj (int) – Argument passed to func.

  • lmap (int) – A valid pointer to a link_map if you want program not to abort. (func is executed first.)

  • nextl (int) – Next ExitDtorList on chain. 0 means end of chain.

Examples

>>> context.clear(arch='amd64')
>>> dtor = glibc.ExitDtorList(0x402c0, 0xdeadbeef, 0, 0, 0)
>>> dtor
ExitDtorList(func=0x402c0 ^ 0xdeadbeef, obj=0x0, map=0x0, next=0x0)
>>> bytes(dtor).hex()
'00005e7853bd0100000000000000000000000000000000000000000000000000'
static from_bytes(data: bytes, guard: int) ExitDtorList[source]

Construct an ExitDtorList from bytes object.

Parameters:
  • data (bytes) – The bytes object to convert to ExitDtorList, whose length should exactly be 4 * sizeof(size_t).

  • guard (int) – Process POINTER_GUARD to demangle pointers.

Examples

>>> context.clear(arch='amd64')
>>> guard = 0xd36a59fe4e9853d8
>>> blob = bytes.fromhex('d4a611029a375619000000000000000010915555555500000000000000000000')
>>> glibc.ExitDtorList.from_bytes(blob, guard)
ExitDtorList(func=0x5555555552d0 ^ 0xd36a59fe4e9853d8, obj=0x0, map=0x555555559110, next=0x0)
__init__(func: int, guard: int, obj: int, lmap: int, nextl: int)[source]
__repr__() str[source]

Return repr(self).

__weakref__[source]

list of weak references to the object

class pwnlib.libc.glibc.ExitFlavor(*values)[source]

Enum adapted from glibc exit.h. Check enum definitions.

Original enums are: ef_free, ef_us, ef_on, ef_at and ef_cxa.

__format__(format_spec, /)[source]

Convert to a string according to format_spec.

__new__(value)[source]
class pwnlib.libc.glibc.ExitFunc(flavor: ExitFlavor, func: int, guard: int, arg: int | None = None, dso: int | None = None)[source]

Craft a struct exit_function object. If user has arbitrary write to libc area and knows pointer guard used in PTR_MANGLE, then the user is able to hijack control flow when process exits. Check struct exit_function definitions.

Parameters:
  • flavor (ExitFlavor) – Which flavor of exit func is registered.

  • func (int) – A pointer to function to execute.

  • guard (int) – Process POINTER_GUARD.

  • arg (int) – Optional. onexit and cxa_exit require it.

  • dso (int) – Optional. cxa_exit require it. (dso_handle)

Examples

>>> context.clear(arch='amd64')
>>> glibc.ExitFunc(glibc.ExitFlavor.FREE, 0, 0)
ExitFunc(FREE)
>>> glibc.ExitFunc(glibc.ExitFlavor.CXA, 0x401f0, 0x13371337deadbeef, 0x238900000680, 0x44008)
ExitFunc(CXA, fn=0x401f0 ^ 0x13371337deadbeef, arg=0x238900000680, dso_handle=0x44008)
>>> exit_func = glibc.ExitFunc(glibc.ExitFlavor.AT, 0x401f0, 0x13371337deadbeef)
>>> exit_func
ExitFunc(AT, fn=0x401f0 ^ 0x13371337deadbeef)
>>> bytes(exit_func).hex()
'03000000000000006e263e7e53bd6f26'
static from_bytes(data: bytes, guard: int) ExitFunc[source]

Construct an ExitFunc from bytes object.

Parameters:
  • data (bytes) – The bytes object to convert to ExitFunc. Must be aligned to context.arch word boundry.

  • guard (int) – Process POINTER_GUARD to demangle pointers.

Examples

>>> context.clear(arch='amd64')
>>> guard = 0x2f21c4a298024bcd
>>> blob = bytes.fromhex('0300000000000000435ea835ae9aef23')
>>> glibc.ExitFunc.from_bytes(blob, guard)
ExitFunc(AT, fn=0x555555555119 ^ 0x2f21c4a298024bcd)
>>> blob = bytes.fromhex('0200000000000000435ea835ae9aef230000371337130000')
>>> glibc.ExitFunc.from_bytes(blob, guard)
ExitFunc(ON, fn=0x555555555119 ^ 0x2f21c4a298024bcd, arg=0x133713370000)
>>> blob = bytes.fromhex('0000000000000000')
>>> glibc.ExitFunc.from_bytes(blob, guard)
ExitFunc(FREE)
>>> blob = bytes.fromhex('0100000000000000')
>>> glibc.ExitFunc.from_bytes(blob, guard)
ExitFunc(USED)
__init__(flavor: ExitFlavor, func: int, guard: int, arg: int | None = None, dso: int | None = None)[source]
__repr__() str[source]

Return repr(self).

__weakref__[source]

list of weak references to the object

class pwnlib.libc.glibc.ExitFuncList(nextp: int, fns: list[ExitFunc])[source]

Craft a struct exit_function_list object. glibc has a static variable initial to store most atexit objects and a pointer __exit_funcs pointing to initial. Check struct exit_function_list definitions.

Parameters:
  • nextp (int) – Next struct exit_function_list pointer on chain.

  • fns (list[ExitFunc]) – Registered exit functions

Variables:

idx (int) – Total size of registered exit funcs. (This field is automatically obtained via len(funcs))

Examples

>>> context.clear(arch='i386')
>>> fa = glibc.ExitFunc(glibc.ExitFlavor.FREE, 0, 0)
>>> fb = glibc.ExitFunc(glibc.ExitFlavor.AT, 0x401f0, 0x13371337)
>>> flist = glibc.ExitFuncList(0, [fa, fb])
>>> flist
ExitFuncList(next=0x0, idx=2, fns=[ExitFunc(FREE), ExitFunc(AT, fn=0x401f0 ^ 0x13371337)])
>>> bytes(flist).hex()
'00000000020000000000000000000000000000000000000003000000268e25660000000000000000'
static from_bytes(data: bytes, guard: int) ExitFuncList[source]

Construct an ExitFuncList from bytes object.

Parameters:
  • data (bytes) – The bytes object to convert to ExitFuncList. Should be large enough to resolve all entries specified by idx.

  • guard (int) – Process POINTER_GUARD to demangle pointers.

Examples

>>> context.clear(arch='amd64')
>>> guard = 0x2d42599562d398bb
>>> blob = bytes.fromhex('000000000000000001000000000000000400000000000000845ab66f5e2ad54c00000000000000000000000000000000')
>>> glibc.ExitFuncList.from_bytes(blob, guard)
ExitFuncList(next=0x0, idx=1, fns=[ExitFunc(CXA, fn=0x7ffff7fcaf60 ^ 0x2d42599562d398bb, arg=0x0, dso_handle=0x0)])
__init__(nextp: int, fns: list[ExitFunc])[source]
__repr__() str[source]

Return repr(self).

__weakref__[source]

list of weak references to the object

pwnlib.libc.glibc.protect_ptr(word_addr: int, value: int) int[source]

Perform PROTECT_PTR in glibc heap macros to protect pointers. REVEAL_PTR is basically PROTECT_PTR, and since we don’t know the address of the word, so use protect_ptr instead.

Parameters:
  • word_addr (int) – The address of where value is stored.

  • value (int) – The value to protect/reveal.

Returns:

Protected/Revealed value.

Examples

>>> hex(glibc.protect_ptr(0x5e5555556700, 0))
'0x5e5555556'
>>> ptr_addr = 0x555555559200
>>> ptr_value = 0x7ffff7f96058
>>> glibc.protect_ptr(ptr_addr, glibc.protect_ptr(ptr_addr, ptr_value)) == ptr_value
True
pwnlib.libc.glibc.ptr_demangle(guard: int, mangled: int) int[source]

Perform PTR_DEMANGLE in glibc to demangle protected pointer.

Parameters:
  • guard (int) – The value of POINTER_GUARD.

  • mangled (int) – The value to demangle.

Returns:

Demangled value.

Examples

>>> with context.local(arch='amd64'):
...     val = glibc.ptr_demangle(0x1f1f1f1f1f1f1f1f, 0xc03e3e3e3e3e3e3e)
...     print(hex(val))
0x7f0000000000
>>> with context.local(arch='aarch64'):
...     val = glibc.ptr_demangle(0x1f1f1f1f, 0x2e2e2e2e00000000)
...     print(hex(val))
0x2e2e2e2e1f1f1f1f
pwnlib.libc.glibc.ptr_mangle(guard: int, value: int) int[source]

Perform PTR_MANGLE in glibc to protect pointers.

Parameters:
  • guard (int) – The value of POINTER_GUARD.

  • value (int) – The value to protect.

Returns:

Mangled value.

Examples

>>> with context.local(arch='amd64'):
...     val = glibc.ptr_mangle(0x1f1f1f1f1f1f1f1f, 0x7f0000000000)
...     print(hex(val))
0xc03e3e3e3e3e3e3e
>>> with context.local(arch='arm'):
...     val = glibc.ptr_mangle(0x1f1f, 0x2e2e0000)
...     print(hex(val))
0x2e2e1f1f
pwnlib.libc.glibc.reveal_ptr_same_page(ptr_value: int) int[source]

Reveal a pointer that was mangled by protect_ptr without knowing where the pointer is stored. Only works if the leaked pointer itself is stored on the same page as its value.

Parameters:

ptr_value (int) – The mangled pointer.

Returns:

int – The original pointer.

Example

>>> context.clear(arch='amd64')
>>> ptr_addr = 0x555555559200
>>> ptr_value = 0x555555559380
>>> glibc.reveal_ptr_same_page(glibc.protect_ptr(ptr_addr, ptr_value)) == ptr_value
True
>>> ptr_addr = 0x555555559200
>>> ptr_value = 0x55555556a380
>>> glibc.reveal_ptr_same_page(glibc.protect_ptr(ptr_addr, ptr_value)) == ptr_value
False