-
-
Save sagittarius-a/f3a32344f2991eb3950b25139347dbbb to your computer and use it in GitHub Desktop.
IDA Python loader for /proc/pid/mem without debugging a process
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import re, subprocess, idaapi, ida_segment, ida_kernwin | |
# To install this, simply put it in your ida_install/loaders folder and open | |
# a `/proc/<pid>/mem` file! | |
# | |
# You might need to set `echo 0 > /proc/sys/kernel/yama/ptrace_scope` if you | |
# want to be able to dump processes depending on your system configuration. | |
# Check if the file is supported by our loader | |
def accept_file(li, filename): | |
# Check if the filename is /proc/<pid>/mem, if so, we can handle it! | |
mch = re.match("/proc/(\d+)/mem", filename) | |
if not mch: | |
return 0 | |
# We can handle this file! | |
return {'format': f"{filename} dump", 'processor': 'metapc'} | |
# Load the file into the database! | |
def load_file(li, neflags, fmt): | |
# Get the PID from the format | |
pid = int(re.match("/proc/(\d+)/mem dump", fmt).group(1)) | |
# Ask the user about the bitness to use for segments | |
bitness = ida_kernwin.ask_buttons( | |
"64-bit", "32-bit", "Cancel", 0, "What bitness is this process?") | |
if bitness == -1: | |
# Cancelled | |
return 0 | |
# Convert dialog selection to IDA's segment bitness values | |
if bitness == 1: | |
bitness = 2 # 64-bit | |
idaapi.get_inf_structure().lflags |= idaapi.LFLG_64BIT | |
elif bitness == 0: | |
bitness = 1 # 32-bit | |
# Open the maps file | |
seg = idaapi.segment_t() | |
with open(f"/proc/{pid}/maps") as fd: | |
# Go through each line in the map | |
for line in fd.readlines(): | |
# Parse the /proc/<pid>/maps line, super quality regex | |
mch = re.match("([0-9a-f]+)-([0-9a-f]+) ([r-])([w-])([x-])[ps] [0-9a-f]+ [0-9a-f]+:[0-9a-f]+ \d+\s+(.*)", line) | |
start = int(mch.group(1), 16) | |
end = int(mch.group(2), 16) | |
r = mch.group(3) == "r" | |
w = mch.group(4) == "w" | |
x = mch.group(5) == "x" | |
name = mch.group(6) | |
# If this segment is usable in any way, add it to the database | |
if r or w or x: | |
# Mark things as code if they're executable | |
seg.start_ea = start | |
seg.end_ea = end | |
#seg.bitness = bitness | |
# Set up permissions | |
seg.perm = 0 | |
if r: | |
seg.perm |= ida_segment.SEGPERM_READ | |
if w: | |
seg.perm |= ida_segment.SEGPERM_WRITE | |
if x: | |
seg.perm |= ida_segment.SEGPERM_EXEC | |
# Add the segment! | |
if x: | |
idaapi.add_segm_ex(seg, name, "CODE", 0) | |
else: | |
idaapi.add_segm_ex(seg, name, "DATA", 0) | |
idaapi.set_segm_addressing(idaapi.getseg(start), bitness) | |
# IDA's API doesn't like seeking negative | |
if start >= 0x8000000000000000: | |
print(f"Unsupported address range {start:x}-{end:x} " | |
"leaving uninitialized") | |
continue | |
# Seek to the data and read it | |
li.seek(start) | |
data = li.read(end - start) | |
# It's possible we failed to read certain areas, so only | |
# `put_bytes` if we actually got a valid result | |
if data: | |
# Write in the bytes | |
idaapi.put_bytes(start, data) | |
idaapi.load_plugin('hexx64') | |
# Loaded! | |
return 1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment