We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
pwn libcdb file fails if binary doesn't contain b'/bin/sh\x00'
Hello,
the "pwn libcdb file /[something]" is crashing depending whether or not the binary contains the string b'/bin/sh\x00'.

This works:
```
$ pwn libcdb file /bin/bash
[*] bash
    BuildID:     e1da91a3e72343eb054c8c69a8d6b4240acb8b10
    MD5:         c33ad3a4937b1c186a8a1279bb31e702
    SHA1:        d07f822b462ecf5ae31f5ccf1c6657b7505afb3f
    SHA256:      a8334e823ce220c4a375e1d5f32fabc1bd47abb6810760ea4100415b55a097e4
    Symbols:
                         dup2 = 0x302b4
                       printf = not found
                         puts = 0x30024
                         read = 0x30494
                   str_bin_sh = 0x336ca
                       system = not found
                        write = 0x30134

```

This is crashing as search returns :
```
$ pwn libcdb file /bin/ls
[*] ls
    BuildID:     e2ca832f1c2112aea9d7b9bc639e97e873a6b516
    MD5:         df0e7216034340f844de8e3b3c37d32b
    SHA1:        0c5f47f25f4379690945f6e7eaa92e1999d0755d
    SHA256:      9379a0fa9ed1e0b4302c4a2c9b1254d3cd76a9048f0ead3c9e216a5082b536bf
    Symbols:
Traceback (most recent call last):
  File "/usr/bin/pwn", line 33, in <module>
    sys.exit(load_entry_point('pwntools==4.11.1', 'console_scripts', 'pwn')())
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/pwnlib/commandline/main.py", line 58, in main
    commands[args.command](args)
  File "/usr/lib/python3.12/site-packages/pwnlib/commandline/libcdb.py", line 236, in main
    synthetic_symbols = collect_synthetic_symbols(exe)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/pwnlib/commandline/libcdb.py", line 180, in collect_synthetic_symbols
    exe.symbols['str_bin_sh'] = next(exe.search(b'/bin/sh\x00'))
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
StopIteration

```



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pwnlib/commandline/libcdb.py`
Content:
```
1 #!/usr/bin/env python
2 from __future__ import absolute_import
3 from __future__ import division
4 from __future__ import print_function
5 
6 import re
7 import shutil
8 import sys
9 
10 import pwnlib.args
11 pwnlib.args.free_form = False
12 
13 from pwn import *
14 from pwnlib.commandline import common
15 
16 parser = common.parser_commands.add_parser(
17     'libcdb',
18     help = 'Print various information about a libc binary',
19     description = 'Print various information about a libc binary'
20 )
21 
22 libc_commands = parser.add_subparsers(
23     dest = 'libc_command'
24 )
25 
26 lookup_parser = libc_commands.add_parser(
27     'lookup',
28     help = 'Lookup a libc version by function offsets',
29     description = 'Lookup a libc version by function offsets'
30 )
31 
32 lookup_parser.add_argument(
33     'symbol_offset_pairs',
34     metavar = 'symbol_offset_pairs',
35     nargs = '+',
36     help = 'Symbol and offset pairs to lookup matching libc version. Can be any number of pairs to narrow the search. Example: "read 3e0 write 520"'
37 )
38 
39 lookup_parser.add_argument(
40     '--download-libc',
41     action = 'store_true',
42     default = False,
43     help = 'Attempt to download the matching libc.so'
44 )
45 
46 lookup_parser.add_argument(
47     '--unstrip',
48     action = 'store_true',
49     default = True,
50     help = 'Attempt to unstrip the libc binary with debug symbols from a debuginfod server'
51 )
52 
53 lookup_parser.add_argument(
54     '--no-unstrip',
55     action = 'store_false',
56     dest = 'unstrip',
57     help = 'Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server'
58 )
59 
60 hash_parser = libc_commands.add_parser(
61     'hash',
62     help = 'Display information of a libc version given an unique hash',
63     description = 'Display information of a libc version given an unique hash'
64 )
65 
66 hash_parser.add_argument(
67     'hash_value',
68     metavar = 'hash_value',
69     nargs = '+',
70     help = 'Hex encoded hash value'
71 )
72 
73 hash_parser.add_argument(
74     '-t', '--hash_type',
75     nargs = '?',
76     type = str,
77     choices = ['id', 'buildid', 'md5', 'sha1', 'sha256'],
78     default = 'buildid',
79     help = 'The type of the provided hash value. Supported hashtypes: id, buildid, md5, sha1, sha256'
80 )
81 
82 hash_parser.add_argument(
83     '--download-libc',
84     action = 'store_true',
85     default = False,
86     help = 'Attempt to download the matching libc.so'
87 )
88 
89 hash_parser.add_argument(
90     '--unstrip',
91     action = 'store_true',
92     default = True,
93     help = 'Attempt to unstrip the libc binary with debug symbols from a debuginfod server'
94 )
95 
96 hash_parser.add_argument(
97     '--no-unstrip',
98     action = 'store_false',
99     dest = 'unstrip',
100     help = 'Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server'
101 )
102 
103 file_parser = libc_commands.add_parser(
104     'file',
105     help = 'Dump information about a libc binary',
106     description = 'Dump information about a libc binary'
107 )
108 
109 file_parser.add_argument(
110     'files',
111     metavar = 'files',
112     nargs = '+',
113     help = 'Libc binary to dump'
114 )
115 
116 file_parser.add_argument(
117     '-s', '--symbols',
118     metavar = 'symbols',
119     nargs = '*',
120     help = 'List of symbol offsets to dump in addition to the common ones'
121 )
122 
123 file_parser.add_argument(
124     '-o', '--offset',
125     metavar = 'offset',
126     type = str,
127     help = 'Display all offsets relative to this symbol'
128 )
129 
130 file_parser.add_argument(
131     '--unstrip',
132     action = 'store_true',
133     default = False,
134     help = 'Attempt to unstrip the libc binary inplace with debug symbols from a debuginfod server'
135 )
136 
137 common_symbols = ['dup2', 'printf', 'puts', 'read', 'system', 'write']
138 
139 def find_libc(params):
140     import requests
141     url    = "https://libc.rip/api/find"
142     result = requests.post(url, json=params, timeout=20)
143     log.debug('Request: %s', params)
144     log.debug('Result: %s', result.json())
145     if result.status_code != 200 or len(result.json()) == 0:
146         log.failure("Could not find libc for %s on libc.rip", params)
147         return []
148 
149     return result.json()
150 
151 def print_libc(libc):
152     log.info('%s', text.red(libc['id']))
153     log.indented('\t%-20s %s', text.green('BuildID:'), libc['buildid'])
154     log.indented('\t%-20s %s', text.green('MD5:'), libc['md5'])
155     log.indented('\t%-20s %s', text.green('SHA1:'), libc['sha1'])
156     log.indented('\t%-20s %s', text.green('SHA256:'), libc['sha256'])
157     log.indented('\t%s', text.green('Symbols:'))
158     for symbol in libc['symbols'].items():
159         log.indented('\t%25s = %s', symbol[0], symbol[1])
160 
161 def handle_remote_libc(args, libc):
162     print_libc(libc)
163     if args.download_libc:
164         path = libcdb.search_by_build_id(libc['buildid'], args.unstrip)
165         if path:
166             if args.unstrip:
167                 libcdb.unstrip_libc(path)
168             shutil.copy(path, './{}.so'.format(libc['id']))
169 
170 def translate_offset(offs, args, exe):
171     if args.offset:
172         if args.offset not in exe.symbols:
173             log.info_once('offset symbol %s not found. ignoring.', args.offset)
174             return offs
175         return offs - exe.symbols[args.offset]
176     return offs
177 
178 def collect_synthetic_symbols(exe):
179     available_symbols = ['str_bin_sh']
180     exe.symbols['str_bin_sh'] = next(exe.search(b'/bin/sh\x00'))
181 
182     libc_start_main_return = exe.libc_start_main_return
183     if libc_start_main_return > 0:
184         exe.symbols['__libc_start_main_ret'] = libc_start_main_return
185         available_symbols.append('__libc_start_main_ret')
186 
187     return available_symbols
188 
189 def main(args):
190     if len(sys.argv) < 3:
191         parser.print_usage()
192         sys.exit()
193 
194     if args.libc_command == 'lookup':
195         pairs = args.symbol_offset_pairs
196         if len(pairs) % 2 != 0:
197             log.failure('Uneven number of arguments. Please provide "symbol offset" pairs')
198             return
199         
200         symbols = {pairs[i]:pairs[i+1] for i in range(0, len(pairs), 2)}
201         matched_libcs = find_libc({'symbols': symbols})
202         for libc in matched_libcs:
203             handle_remote_libc(args, libc)
204 
205     elif args.libc_command == 'hash':
206         for hash_value in args.hash_value:
207             matched_libcs = find_libc({args.hash_type: hash_value})
208             for libc in matched_libcs:
209                 handle_remote_libc(args, libc)
210 
211     elif args.libc_command == 'file':
212         from hashlib import md5, sha1, sha256
213         for file in args.files:
214             if not os.path.exists(file) or not os.path.isfile(file):
215                 log.failure('File does not exist %s', args.file)
216                 continue
217             
218             if args.unstrip:
219                 libcdb.unstrip_libc(file)
220 
221             exe = ELF(file, checksec=False)
222             log.info('%s', text.red(os.path.basename(file)))
223 
224             libc_version = re.search(br'libc[ -](\d+\.\d+)', exe.data)
225             if libc_version:
226                 log.indented('%-20s %s', text.green('Version:'), libc_version.group(1).decode())
227 
228             if exe.buildid:
229                 log.indented('%-20s %s', text.green('BuildID:'), enhex(exe.buildid))
230             log.indented('%-20s %s', text.green('MD5:'), md5(exe.data).hexdigest())
231             log.indented('%-20s %s', text.green('SHA1:'), sha1(exe.data).hexdigest())
232             log.indented('%-20s %s', text.green('SHA256:'), sha256(exe.data).hexdigest())
233 
234             # Always dump the basic list of common symbols
235             log.indented('%s', text.green('Symbols:'))
236             synthetic_symbols = collect_synthetic_symbols(exe)
237 
238             symbols = common_symbols + (args.symbols or []) + synthetic_symbols
239             symbols.sort()
240             for symbol in symbols:
241                 if symbol not in exe.symbols:
242                     log.indented('%25s = %s', symbol, text.red('not found'))
243                 else:
244                     log.indented('%25s = %#x', symbol, translate_offset(exe.symbols[symbol], args, exe))
245 
246 if __name__ == '__main__':
247     pwnlib.commandline.common.main(__file__)
248
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pwnlib/commandline/libcdb.py b/pwnlib/commandline/libcdb.py
--- a/pwnlib/commandline/libcdb.py
+++ b/pwnlib/commandline/libcdb.py
@@ -176,9 +176,13 @@
     return offs
 
 def collect_synthetic_symbols(exe):
-    available_symbols = ['str_bin_sh']
-    exe.symbols['str_bin_sh'] = next(exe.search(b'/bin/sh\x00'))
-
+    available_symbols = []
+    try:
+        exe.symbols['str_bin_sh'] = next(exe.search(b'/bin/sh\x00'))
+        available_symbols.append('str_bin_sh')
+    except StopIteration:
+        pass
+        
     libc_start_main_return = exe.libc_start_main_return
     if libc_start_main_return > 0:
         exe.symbols['__libc_start_main_ret'] = libc_start_main_return

{"golden_diff": "diff --git a/pwnlib/commandline/libcdb.py b/pwnlib/commandline/libcdb.py\n--- a/pwnlib/commandline/libcdb.py\n+++ b/pwnlib/commandline/libcdb.py\n@@ -176,9 +176,13 @@\n     return offs\n \n def collect_synthetic_symbols(exe):\n-    available_symbols = ['str_bin_sh']\n-    exe.symbols['str_bin_sh'] = next(exe.search(b'/bin/sh\\x00'))\n-\n+    available_symbols = []\n+    try:\n+        exe.symbols['str_bin_sh'] = next(exe.search(b'/bin/sh\\x00'))\n+        available_symbols.append('str_bin_sh')\n+    except StopIteration:\n+        pass\n+        \n     libc_start_main_return = exe.libc_start_main_return\n     if libc_start_main_return > 0:\n         exe.symbols['__libc_start_main_ret'] = libc_start_main_return\n", "issue": "pwn libcdb file fails if binary doesn't contain b'/bin/sh\\x00'\nHello,\r\nthe \"pwn libcdb file /[something]\" is crashing depending whether or not the binary contains the string b'/bin/sh\\x00'.\r\n\r\nThis works:\r\n```\r\n$ pwn libcdb file /bin/bash\r\n[*] bash\r\n    BuildID:     e1da91a3e72343eb054c8c69a8d6b4240acb8b10\r\n    MD5:         c33ad3a4937b1c186a8a1279bb31e702\r\n    SHA1:        d07f822b462ecf5ae31f5ccf1c6657b7505afb3f\r\n    SHA256:      a8334e823ce220c4a375e1d5f32fabc1bd47abb6810760ea4100415b55a097e4\r\n    Symbols:\r\n                         dup2 = 0x302b4\r\n                       printf = not found\r\n                         puts = 0x30024\r\n                         read = 0x30494\r\n                   str_bin_sh = 0x336ca\r\n                       system = not found\r\n                        write = 0x30134\r\n\r\n```\r\n\r\nThis is crashing as search returns :\r\n```\r\n$ pwn libcdb file /bin/ls\r\n[*] ls\r\n    BuildID:     e2ca832f1c2112aea9d7b9bc639e97e873a6b516\r\n    MD5:         df0e7216034340f844de8e3b3c37d32b\r\n    SHA1:        0c5f47f25f4379690945f6e7eaa92e1999d0755d\r\n    SHA256:      9379a0fa9ed1e0b4302c4a2c9b1254d3cd76a9048f0ead3c9e216a5082b536bf\r\n    Symbols:\r\nTraceback (most recent call last):\r\n  File \"/usr/bin/pwn\", line 33, in <module>\r\n    sys.exit(load_entry_point('pwntools==4.11.1', 'console_scripts', 'pwn')())\r\n             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n  File \"/usr/lib/python3.12/site-packages/pwnlib/commandline/main.py\", line 58, in main\r\n    commands[args.command](args)\r\n  File \"/usr/lib/python3.12/site-packages/pwnlib/commandline/libcdb.py\", line 236, in main\r\n    synthetic_symbols = collect_synthetic_symbols(exe)\r\n                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n  File \"/usr/lib/python3.12/site-packages/pwnlib/commandline/libcdb.py\", line 180, in collect_synthetic_symbols\r\n    exe.symbols['str_bin_sh'] = next(exe.search(b'/bin/sh\\x00'))\r\n                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\nStopIteration\r\n\r\n```\r\n\r\n\n", "before_files": [{"content": "#!/usr/bin/env python\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport re\nimport shutil\nimport sys\n\nimport pwnlib.args\npwnlib.args.free_form = False\n\nfrom pwn import *\nfrom pwnlib.commandline import common\n\nparser = common.parser_commands.add_parser(\n    'libcdb',\n    help = 'Print various information about a libc binary',\n    description = 'Print various information about a libc binary'\n)\n\nlibc_commands = parser.add_subparsers(\n    dest = 'libc_command'\n)\n\nlookup_parser = libc_commands.add_parser(\n    'lookup',\n    help = 'Lookup a libc version by function offsets',\n    description = 'Lookup a libc version by function offsets'\n)\n\nlookup_parser.add_argument(\n    'symbol_offset_pairs',\n    metavar = 'symbol_offset_pairs',\n    nargs = '+',\n    help = 'Symbol and offset pairs to lookup matching libc version. Can be any number of pairs to narrow the search. Example: \"read 3e0 write 520\"'\n)\n\nlookup_parser.add_argument(\n    '--download-libc',\n    action = 'store_true',\n    default = False,\n    help = 'Attempt to download the matching libc.so'\n)\n\nlookup_parser.add_argument(\n    '--unstrip',\n    action = 'store_true',\n    default = True,\n    help = 'Attempt to unstrip the libc binary with debug symbols from a debuginfod server'\n)\n\nlookup_parser.add_argument(\n    '--no-unstrip',\n    action = 'store_false',\n    dest = 'unstrip',\n    help = 'Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server'\n)\n\nhash_parser = libc_commands.add_parser(\n    'hash',\n    help = 'Display information of a libc version given an unique hash',\n    description = 'Display information of a libc version given an unique hash'\n)\n\nhash_parser.add_argument(\n    'hash_value',\n    metavar = 'hash_value',\n    nargs = '+',\n    help = 'Hex encoded hash value'\n)\n\nhash_parser.add_argument(\n    '-t', '--hash_type',\n    nargs = '?',\n    type = str,\n    choices = ['id', 'buildid', 'md5', 'sha1', 'sha256'],\n    default = 'buildid',\n    help = 'The type of the provided hash value. Supported hashtypes: id, buildid, md5, sha1, sha256'\n)\n\nhash_parser.add_argument(\n    '--download-libc',\n    action = 'store_true',\n    default = False,\n    help = 'Attempt to download the matching libc.so'\n)\n\nhash_parser.add_argument(\n    '--unstrip',\n    action = 'store_true',\n    default = True,\n    help = 'Attempt to unstrip the libc binary with debug symbols from a debuginfod server'\n)\n\nhash_parser.add_argument(\n    '--no-unstrip',\n    action = 'store_false',\n    dest = 'unstrip',\n    help = 'Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server'\n)\n\nfile_parser = libc_commands.add_parser(\n    'file',\n    help = 'Dump information about a libc binary',\n    description = 'Dump information about a libc binary'\n)\n\nfile_parser.add_argument(\n    'files',\n    metavar = 'files',\n    nargs = '+',\n    help = 'Libc binary to dump'\n)\n\nfile_parser.add_argument(\n    '-s', '--symbols',\n    metavar = 'symbols',\n    nargs = '*',\n    help = 'List of symbol offsets to dump in addition to the common ones'\n)\n\nfile_parser.add_argument(\n    '-o', '--offset',\n    metavar = 'offset',\n    type = str,\n    help = 'Display all offsets relative to this symbol'\n)\n\nfile_parser.add_argument(\n    '--unstrip',\n    action = 'store_true',\n    default = False,\n    help = 'Attempt to unstrip the libc binary inplace with debug symbols from a debuginfod server'\n)\n\ncommon_symbols = ['dup2', 'printf', 'puts', 'read', 'system', 'write']\n\ndef find_libc(params):\n    import requests\n    url    = \"https://libc.rip/api/find\"\n    result = requests.post(url, json=params, timeout=20)\n    log.debug('Request: %s', params)\n    log.debug('Result: %s', result.json())\n    if result.status_code != 200 or len(result.json()) == 0:\n        log.failure(\"Could not find libc for %s on libc.rip\", params)\n        return []\n\n    return result.json()\n\ndef print_libc(libc):\n    log.info('%s', text.red(libc['id']))\n    log.indented('\\t%-20s %s', text.green('BuildID:'), libc['buildid'])\n    log.indented('\\t%-20s %s', text.green('MD5:'), libc['md5'])\n    log.indented('\\t%-20s %s', text.green('SHA1:'), libc['sha1'])\n    log.indented('\\t%-20s %s', text.green('SHA256:'), libc['sha256'])\n    log.indented('\\t%s', text.green('Symbols:'))\n    for symbol in libc['symbols'].items():\n        log.indented('\\t%25s = %s', symbol[0], symbol[1])\n\ndef handle_remote_libc(args, libc):\n    print_libc(libc)\n    if args.download_libc:\n        path = libcdb.search_by_build_id(libc['buildid'], args.unstrip)\n        if path:\n            if args.unstrip:\n                libcdb.unstrip_libc(path)\n            shutil.copy(path, './{}.so'.format(libc['id']))\n\ndef translate_offset(offs, args, exe):\n    if args.offset:\n        if args.offset not in exe.symbols:\n            log.info_once('offset symbol %s not found. ignoring.', args.offset)\n            return offs\n        return offs - exe.symbols[args.offset]\n    return offs\n\ndef collect_synthetic_symbols(exe):\n    available_symbols = ['str_bin_sh']\n    exe.symbols['str_bin_sh'] = next(exe.search(b'/bin/sh\\x00'))\n\n    libc_start_main_return = exe.libc_start_main_return\n    if libc_start_main_return > 0:\n        exe.symbols['__libc_start_main_ret'] = libc_start_main_return\n        available_symbols.append('__libc_start_main_ret')\n\n    return available_symbols\n\ndef main(args):\n    if len(sys.argv) < 3:\n        parser.print_usage()\n        sys.exit()\n\n    if args.libc_command == 'lookup':\n        pairs = args.symbol_offset_pairs\n        if len(pairs) % 2 != 0:\n            log.failure('Uneven number of arguments. Please provide \"symbol offset\" pairs')\n            return\n        \n        symbols = {pairs[i]:pairs[i+1] for i in range(0, len(pairs), 2)}\n        matched_libcs = find_libc({'symbols': symbols})\n        for libc in matched_libcs:\n            handle_remote_libc(args, libc)\n\n    elif args.libc_command == 'hash':\n        for hash_value in args.hash_value:\n            matched_libcs = find_libc({args.hash_type: hash_value})\n            for libc in matched_libcs:\n                handle_remote_libc(args, libc)\n\n    elif args.libc_command == 'file':\n        from hashlib import md5, sha1, sha256\n        for file in args.files:\n            if not os.path.exists(file) or not os.path.isfile(file):\n                log.failure('File does not exist %s', args.file)\n                continue\n            \n            if args.unstrip:\n                libcdb.unstrip_libc(file)\n\n            exe = ELF(file, checksec=False)\n            log.info('%s', text.red(os.path.basename(file)))\n\n            libc_version = re.search(br'libc[ -](\\d+\\.\\d+)', exe.data)\n            if libc_version:\n                log.indented('%-20s %s', text.green('Version:'), libc_version.group(1).decode())\n\n            if exe.buildid:\n                log.indented('%-20s %s', text.green('BuildID:'), enhex(exe.buildid))\n            log.indented('%-20s %s', text.green('MD5:'), md5(exe.data).hexdigest())\n            log.indented('%-20s %s', text.green('SHA1:'), sha1(exe.data).hexdigest())\n            log.indented('%-20s %s', text.green('SHA256:'), sha256(exe.data).hexdigest())\n\n            # Always dump the basic list of common symbols\n            log.indented('%s', text.green('Symbols:'))\n            synthetic_symbols = collect_synthetic_symbols(exe)\n\n            symbols = common_symbols + (args.symbols or []) + synthetic_symbols\n            symbols.sort()\n            for symbol in symbols:\n                if symbol not in exe.symbols:\n                    log.indented('%25s = %s', symbol, text.red('not found'))\n                else:\n                    log.indented('%25s = %#x', symbol, translate_offset(exe.symbols[symbol], args, exe))\n\nif __name__ == '__main__':\n    pwnlib.commandline.common.main(__file__)\n", "path": "pwnlib/commandline/libcdb.py"}], "after_files": [{"content": "#!/usr/bin/env python\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport re\nimport shutil\nimport sys\n\nimport pwnlib.args\npwnlib.args.free_form = False\n\nfrom pwn import *\nfrom pwnlib.commandline import common\n\nparser = common.parser_commands.add_parser(\n    'libcdb',\n    help = 'Print various information about a libc binary',\n    description = 'Print various information about a libc binary'\n)\n\nlibc_commands = parser.add_subparsers(\n    dest = 'libc_command'\n)\n\nlookup_parser = libc_commands.add_parser(\n    'lookup',\n    help = 'Lookup a libc version by function offsets',\n    description = 'Lookup a libc version by function offsets'\n)\n\nlookup_parser.add_argument(\n    'symbol_offset_pairs',\n    metavar = 'symbol_offset_pairs',\n    nargs = '+',\n    help = 'Symbol and offset pairs to lookup matching libc version. Can be any number of pairs to narrow the search. Example: \"read 3e0 write 520\"'\n)\n\nlookup_parser.add_argument(\n    '--download-libc',\n    action = 'store_true',\n    default = False,\n    help = 'Attempt to download the matching libc.so'\n)\n\nlookup_parser.add_argument(\n    '--unstrip',\n    action = 'store_true',\n    default = True,\n    help = 'Attempt to unstrip the libc binary with debug symbols from a debuginfod server'\n)\n\nlookup_parser.add_argument(\n    '--no-unstrip',\n    action = 'store_false',\n    dest = 'unstrip',\n    help = 'Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server'\n)\n\nhash_parser = libc_commands.add_parser(\n    'hash',\n    help = 'Display information of a libc version given an unique hash',\n    description = 'Display information of a libc version given an unique hash'\n)\n\nhash_parser.add_argument(\n    'hash_value',\n    metavar = 'hash_value',\n    nargs = '+',\n    help = 'Hex encoded hash value'\n)\n\nhash_parser.add_argument(\n    '-t', '--hash_type',\n    nargs = '?',\n    type = str,\n    choices = ['id', 'buildid', 'md5', 'sha1', 'sha256'],\n    default = 'buildid',\n    help = 'The type of the provided hash value. Supported hashtypes: id, buildid, md5, sha1, sha256'\n)\n\nhash_parser.add_argument(\n    '--download-libc',\n    action = 'store_true',\n    default = False,\n    help = 'Attempt to download the matching libc.so'\n)\n\nhash_parser.add_argument(\n    '--unstrip',\n    action = 'store_true',\n    default = True,\n    help = 'Attempt to unstrip the libc binary with debug symbols from a debuginfod server'\n)\n\nhash_parser.add_argument(\n    '--no-unstrip',\n    action = 'store_false',\n    dest = 'unstrip',\n    help = 'Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server'\n)\n\nfile_parser = libc_commands.add_parser(\n    'file',\n    help = 'Dump information about a libc binary',\n    description = 'Dump information about a libc binary'\n)\n\nfile_parser.add_argument(\n    'files',\n    metavar = 'files',\n    nargs = '+',\n    help = 'Libc binary to dump'\n)\n\nfile_parser.add_argument(\n    '-s', '--symbols',\n    metavar = 'symbols',\n    nargs = '*',\n    help = 'List of symbol offsets to dump in addition to the common ones'\n)\n\nfile_parser.add_argument(\n    '-o', '--offset',\n    metavar = 'offset',\n    type = str,\n    help = 'Display all offsets relative to this symbol'\n)\n\nfile_parser.add_argument(\n    '--unstrip',\n    action = 'store_true',\n    default = False,\n    help = 'Attempt to unstrip the libc binary inplace with debug symbols from a debuginfod server'\n)\n\ncommon_symbols = ['dup2', 'printf', 'puts', 'read', 'system', 'write']\n\ndef find_libc(params):\n    import requests\n    url    = \"https://libc.rip/api/find\"\n    result = requests.post(url, json=params, timeout=20)\n    log.debug('Request: %s', params)\n    log.debug('Result: %s', result.json())\n    if result.status_code != 200 or len(result.json()) == 0:\n        log.failure(\"Could not find libc for %s on libc.rip\", params)\n        return []\n\n    return result.json()\n\ndef print_libc(libc):\n    log.info('%s', text.red(libc['id']))\n    log.indented('\\t%-20s %s', text.green('BuildID:'), libc['buildid'])\n    log.indented('\\t%-20s %s', text.green('MD5:'), libc['md5'])\n    log.indented('\\t%-20s %s', text.green('SHA1:'), libc['sha1'])\n    log.indented('\\t%-20s %s', text.green('SHA256:'), libc['sha256'])\n    log.indented('\\t%s', text.green('Symbols:'))\n    for symbol in libc['symbols'].items():\n        log.indented('\\t%25s = %s', symbol[0], symbol[1])\n\ndef handle_remote_libc(args, libc):\n    print_libc(libc)\n    if args.download_libc:\n        path = libcdb.search_by_build_id(libc['buildid'], args.unstrip)\n        if path:\n            if args.unstrip:\n                libcdb.unstrip_libc(path)\n            shutil.copy(path, './{}.so'.format(libc['id']))\n\ndef translate_offset(offs, args, exe):\n    if args.offset:\n        if args.offset not in exe.symbols:\n            log.info_once('offset symbol %s not found. ignoring.', args.offset)\n            return offs\n        return offs - exe.symbols[args.offset]\n    return offs\n\ndef collect_synthetic_symbols(exe):\n    available_symbols = []\n    try:\n        exe.symbols['str_bin_sh'] = next(exe.search(b'/bin/sh\\x00'))\n        available_symbols.append('str_bin_sh')\n    except StopIteration:\n        pass\n        \n    libc_start_main_return = exe.libc_start_main_return\n    if libc_start_main_return > 0:\n        exe.symbols['__libc_start_main_ret'] = libc_start_main_return\n        available_symbols.append('__libc_start_main_ret')\n\n    return available_symbols\n\ndef main(args):\n    if len(sys.argv) < 3:\n        parser.print_usage()\n        sys.exit()\n\n    if args.libc_command == 'lookup':\n        pairs = args.symbol_offset_pairs\n        if len(pairs) % 2 != 0:\n            log.failure('Uneven number of arguments. Please provide \"symbol offset\" pairs')\n            return\n        \n        symbols = {pairs[i]:pairs[i+1] for i in range(0, len(pairs), 2)}\n        matched_libcs = find_libc({'symbols': symbols})\n        for libc in matched_libcs:\n            handle_remote_libc(args, libc)\n\n    elif args.libc_command == 'hash':\n        for hash_value in args.hash_value:\n            matched_libcs = find_libc({args.hash_type: hash_value})\n            for libc in matched_libcs:\n                handle_remote_libc(args, libc)\n\n    elif args.libc_command == 'file':\n        from hashlib import md5, sha1, sha256\n        for file in args.files:\n            if not os.path.exists(file) or not os.path.isfile(file):\n                log.failure('File does not exist %s', args.file)\n                continue\n            \n            if args.unstrip:\n                libcdb.unstrip_libc(file)\n\n            exe = ELF(file, checksec=False)\n            log.info('%s', text.red(os.path.basename(file)))\n\n            libc_version = re.search(br'libc[ -](\\d+\\.\\d+)', exe.data)\n            if libc_version:\n                log.indented('%-20s %s', text.green('Version:'), libc_version.group(1).decode())\n\n            if exe.buildid:\n                log.indented('%-20s %s', text.green('BuildID:'), enhex(exe.buildid))\n            log.indented('%-20s %s', text.green('MD5:'), md5(exe.data).hexdigest())\n            log.indented('%-20s %s', text.green('SHA1:'), sha1(exe.data).hexdigest())\n            log.indented('%-20s %s', text.green('SHA256:'), sha256(exe.data).hexdigest())\n\n            # Always dump the basic list of common symbols\n            log.indented('%s', text.green('Symbols:'))\n            synthetic_symbols = collect_synthetic_symbols(exe)\n\n            symbols = common_symbols + (args.symbols or []) + synthetic_symbols\n            symbols.sort()\n            for symbol in symbols:\n                if symbol not in exe.symbols:\n                    log.indented('%25s = %s', symbol, text.red('not found'))\n                else:\n                    log.indented('%25s = %#x', symbol, translate_offset(exe.symbols[symbol], args, exe))\n\nif __name__ == '__main__':\n    pwnlib.commandline.common.main(__file__)\n", "path": "pwnlib/commandline/libcdb.py"}]}