|
82 | 82 | from pwnlib.util import misc |
83 | 83 | from pwnlib.util import packing |
84 | 84 | from pwnlib.util.fiddling import unhex |
85 | | -from pwnlib.util.misc import align, align_down |
| 85 | +from pwnlib.util.misc import align, align_down, which |
86 | 86 | from pwnlib.util.sh_string import sh_string |
87 | 87 |
|
88 | 88 | log = getLogger(__name__) |
@@ -2246,3 +2246,122 @@ def disable_nx(self): |
2246 | 2246 | return |
2247 | 2247 |
|
2248 | 2248 | log.error("Could not find PT_GNU_STACK, stack should already be executable") |
| 2249 | + |
| 2250 | + @staticmethod |
| 2251 | + def set_runpath(exepath, runpath): |
| 2252 | + r"""set_runpath(str, str) -> ELF |
| 2253 | +
|
| 2254 | + Patches the RUNPATH of the ELF to the given path using the `patchelf utility <https://github.com/NixOS/patchelf>`_. |
| 2255 | +
|
| 2256 | + The dynamic loader will look for any needed shared libraries in the given path first, |
| 2257 | + before trying the system library paths. This is useful to run a binary with a different |
| 2258 | + libc binary. |
| 2259 | +
|
| 2260 | + Arguments: |
| 2261 | + exepath(str): Path to the binary to patch. |
| 2262 | + runpath(str): Path containing the needed libraries. |
| 2263 | +
|
| 2264 | + Returns: |
| 2265 | + A new ELF instance is returned after patching the binary with the external ``patchelf`` tool. |
| 2266 | +
|
| 2267 | + Example: |
| 2268 | +
|
| 2269 | + >>> tmpdir = tempfile.mkdtemp() |
| 2270 | + >>> ls_path = os.path.join(tmpdir, 'ls') |
| 2271 | + >>> _ = shutil.copy(which('ls'), ls_path) |
| 2272 | + >>> e = ELF.set_runpath(ls_path, './libs') |
| 2273 | + >>> e.runpath == b'./libs' |
| 2274 | + True |
| 2275 | + """ |
| 2276 | + if not which('patchelf'): |
| 2277 | + log.error('"patchelf" tool not installed. See https://github.com/NixOS/patchelf') |
| 2278 | + return None |
| 2279 | + try: |
| 2280 | + subprocess.check_output(['patchelf', '--set-rpath', runpath, exepath], stderr=subprocess.STDOUT) |
| 2281 | + except subprocess.CalledProcessError as e: |
| 2282 | + log.failure('Patching RUNPATH failed (%d): %r', e.returncode, e.stdout) |
| 2283 | + return ELF(exepath, checksec=False) |
| 2284 | + |
| 2285 | + @staticmethod |
| 2286 | + def set_interpreter(exepath, interpreter_path): |
| 2287 | + r"""set_interpreter(str, str) -> ELF |
| 2288 | +
|
| 2289 | + Patches the interpreter of the ELF to the given binary using the `patchelf utility <https://github.com/NixOS/patchelf>`_. |
| 2290 | +
|
| 2291 | + When running the binary, the new interpreter will be used to load the ELF. |
| 2292 | +
|
| 2293 | + Arguments: |
| 2294 | + exepath(str): Path to the binary to patch. |
| 2295 | + interpreter_path(str): Path to the ld.so dynamic loader. |
| 2296 | +
|
| 2297 | + Returns: |
| 2298 | + A new ELF instance is returned after patching the binary with the external ``patchelf`` tool. |
| 2299 | +
|
| 2300 | + Example: |
| 2301 | + >>> tmpdir = tempfile.mkdtemp() |
| 2302 | + >>> ls_path = os.path.join(tmpdir, 'ls') |
| 2303 | + >>> _ = shutil.copy(which('ls'), ls_path) |
| 2304 | + >>> e = ELF.set_interpreter(ls_path, '/tmp/correct_ld.so') |
| 2305 | + >>> e.linker == b'/tmp/correct_ld.so' |
| 2306 | + True |
| 2307 | + """ |
| 2308 | + # patch the interpreter |
| 2309 | + if not which('patchelf'): |
| 2310 | + log.error('"patchelf" tool not installed. See https://github.com/NixOS/patchelf') |
| 2311 | + return None |
| 2312 | + try: |
| 2313 | + subprocess.check_output(['patchelf', '--set-interpreter', interpreter_path, exepath], stderr=subprocess.STDOUT) |
| 2314 | + except subprocess.CalledProcessError as e: |
| 2315 | + log.failure('Patching interpreter failed (%d): %r', e.returncode, e.stdout) |
| 2316 | + return ELF(exepath, checksec=False) |
| 2317 | + |
| 2318 | + @staticmethod |
| 2319 | + def patch_custom_libraries(exe_path, custom_library_path, create_copy=True, suffix='_remotelibc'): |
| 2320 | + r"""patch_custom_libraries(str, str, bool, str) -> ELF |
| 2321 | +
|
| 2322 | + Looks for the interpreter binary in the given path and patches the binary to use |
| 2323 | + it if available. Also patches the RUNPATH to the given path using the `patchelf utility <https://github.com/NixOS/patchelf>`_. |
| 2324 | +
|
| 2325 | + Arguments: |
| 2326 | + exe_path(str): Path to the binary to patch. |
| 2327 | + custom_library_path(str): Path to a folder containing the libraries. |
| 2328 | + create_copy(bool): Create a copy of the binary and apply the patches to the copy. |
| 2329 | + suffix(str): Suffix to append to the filename when creating the copy to patch. |
| 2330 | +
|
| 2331 | + Returns: |
| 2332 | + A new ELF instance is returned after patching the binary with the external ``patchelf`` tool. |
| 2333 | +
|
| 2334 | + Example: |
| 2335 | +
|
| 2336 | + >>> tmpdir = tempfile.mkdtemp() |
| 2337 | + >>> linker_path = os.path.join(tmpdir, 'ld-mock.so') |
| 2338 | + >>> write(linker_path, b'loader') |
| 2339 | + >>> ls_path = os.path.join(tmpdir, 'ls') |
| 2340 | + >>> _ = shutil.copy(which('ls'), ls_path) |
| 2341 | + >>> e = ELF.patch_custom_libraries(ls_path, tmpdir) |
| 2342 | + >>> e.runpath.decode() == tmpdir |
| 2343 | + True |
| 2344 | + >>> e.linker.decode() == linker_path |
| 2345 | + True |
| 2346 | + """ |
| 2347 | + if not which('patchelf'): |
| 2348 | + log.error('"patchelf" tool not installed. See https://github.com/NixOS/patchelf') |
| 2349 | + return None |
| 2350 | + |
| 2351 | + # Create a copy of the ELF to patch instead of the original file. |
| 2352 | + if create_copy: |
| 2353 | + import shutil |
| 2354 | + patched_path = exe_path + suffix |
| 2355 | + shutil.copy2(exe_path, patched_path) |
| 2356 | + exe_path = patched_path |
| 2357 | + |
| 2358 | + # Set interpreter in ELF to the one in the library path. |
| 2359 | + interpreter_name = [filename for filename in os.listdir(custom_library_path) if filename.startswith('ld-')] |
| 2360 | + if interpreter_name: |
| 2361 | + interpreter_path = os.path.realpath(os.path.join(custom_library_path, interpreter_name[0])) |
| 2362 | + ELF.set_interpreter(exe_path, interpreter_path) |
| 2363 | + else: |
| 2364 | + log.warn("Couldn't find ld.so in library path. Interpreter not set.") |
| 2365 | + |
| 2366 | + # Set RUNPATH to library path in order to find other libraries. |
| 2367 | + return ELF.set_runpath(exe_path, custom_library_path) |
0 commit comments