diff --git a/CHANGELOG.md b/CHANGELOG.md index 5187d79da..e253c3cf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,7 +71,9 @@ The table below shows which release corresponds to each branch, and what date th ## 4.14.0 (`dev`) +- [#2356][2356] Add local libc database provider for libcdb +[2356]: https://github.com/Gallopsled/pwntools/pull/2356 ## 4.13.0 (`beta`) diff --git a/pwnlib/args.py b/pwnlib/args.py index 6af4a34cd..f7985e7d6 100644 --- a/pwnlib/args.py +++ b/pwnlib/args.py @@ -159,6 +159,11 @@ def STDERR(v): """Sends logging to ``stderr`` by default, instead of ``stdout``""" context.log_console = sys.stderr +def LOCAL_LIBCDB(v): + """Sets path to local libc-database via ``context.local_libcdb``, e.g. + ``LOCAL_LIBCDB='/path/to/libc-databse'``""" + context.local_libcdb = v + hooks = { 'LOG_LEVEL': LOG_LEVEL, 'LOG_FILE': LOG_FILE, @@ -170,6 +175,7 @@ def STDERR(v): 'NOASLR': NOASLR, 'NOPTRACE': NOPTRACE, 'STDERR': STDERR, + 'LOCAL_LIBCDB': LOCAL_LIBCDB, } def initialize(): diff --git a/pwnlib/context/__init__.py b/pwnlib/context/__init__.py index 6b3f636b7..9d6ebe71d 100644 --- a/pwnlib/context/__init__.py +++ b/pwnlib/context/__init__.py @@ -360,6 +360,7 @@ class ContextType(object): 'endian': 'little', 'gdbinit': "", 'kernel': None, + 'local_libcdb': "/var/lib/libc-database", 'log_level': logging.INFO, 'log_file': _devnull(), 'log_console': sys.stdout, @@ -1071,6 +1072,32 @@ def log_console(self, stream): stream = open(stream, 'wt') return stream + @_validator + def local_libcdb(self, path): + """ + Sets path to local libc-database, get more information for libc-database: + https://github.com/niklasb/libc-database + + Works in :attr:`pwnlib.libcdb` when searching by local database provider. + + The default value is ``/var/lib/libc-database``. + + Sets `context.local_libcdb` to empty string or `None` will turn off local libc-database integration. + + Examples: + + >>> context.local_libcdb = pwnlib.data.elf.path + >>> context.local_libcdb = 'foobar' + Traceback (most recent call last): + ... + AttributeError: 'foobar' does not exist, please download libc-database first + """ + + if not os.path.isdir(path): + raise AttributeError("'%s' does not exist, please download libc-database first" % path) + + return path + @property def mask(self): return (1 << self.bits) - 1 diff --git a/pwnlib/libcdb.py b/pwnlib/libcdb.py index 885a0eea2..329ed187f 100644 --- a/pwnlib/libcdb.py +++ b/pwnlib/libcdb.py @@ -11,6 +11,7 @@ from pwnlib.context import context from pwnlib.elf import ELF +from pwnlib.filesystem.path import Path from pwnlib.log import getLogger from pwnlib.tubes.process import process from pwnlib.util.fiddling import enhex @@ -126,7 +127,23 @@ def provider_local_system(hex_encoded_id, hash_type): return local_libc.data return None -PROVIDERS = [provider_local_system, provider_libcdb, provider_libc_rip] +# Offline search https://github.com/niklasb/libc-database for hash type +def provider_local_database(hex_encoded_id, hash_type): + if not context.local_libcdb: + return None + + localdb = Path(context.local_libcdb) + if not localdb.is_dir(): + return None + + log.debug("Searching local libc database, %s: %s", hash_type, hex_encoded_id) + for libc_path in localdb.rglob("*.so"): + if hex_encoded_id == HASHES[hash_type](libc_path): + return read(libc_path) + + return None + +PROVIDERS = [provider_local_system, provider_local_database, provider_libcdb, provider_libc_rip] def search_by_hash(hex_encoded_id, hash_type='build_id', unstrip=True): assert hash_type in HASHES, hash_type