Created
April 8, 2022 01:10
-
-
Save wyattearp/d937c1e1a5c5902263fcaf86cd0d4457 to your computer and use it in GitHub Desktop.
substiture_mappings.py script from https://github.com/NationalSecurityAgency/ghidra/issues/3529
This file contains hidden or 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 os | |
import sys | |
import gdb | |
def run_cmd(cmd): | |
#tmpfile = '/tmp/tmp_cmd.txt' | |
#os.system(f'rm -f {tmpfile}') | |
#gdb.execute(f'set logging file {tmpfile}') | |
#gdb.execute(f'set logging on') | |
result = gdb.execute(cmd, False, True) | |
#gdb.execute(f'set logging off') | |
#result = open(tmpfile).read() | |
#os.system(f'rm -f {tmpfile}') | |
return result | |
class Mapping: | |
def __init__(self, start, end, size, offset, file, attrs=[]): | |
self.start = start | |
self.end = end | |
self.size = size | |
self.offset = offset | |
self.file = file | |
self.attrs = attrs | |
def __repr__(self): | |
return f'{hex(self.start):>20} {hex(self.end):>18} {hex(self.size):>10} {hex(self.offset):>10} {self.file}' # + f' {self.attrs}' | |
def inside(self, other): | |
if other.start == self.start and self.end == other.end: | |
return False | |
if other.start <= self.start and self.end <= other.end: | |
return True | |
return False | |
def round_page(self): | |
PAGE_MASK = 0xfff | |
start_mask = self.start & PAGE_MASK | |
self.start -= start_mask | |
self.offset -= start_mask | |
self.size += start_mask | |
end_mask = self.end & PAGE_MASK | |
end_short = (0x1000 - end_mask) if end_mask else 0 | |
self.end += end_short | |
self.size += end_short | |
def parse_info_proc_mappings(text): | |
mappings = [] | |
for line in text.split('\n'): | |
line = line.lstrip() | |
if not line.startswith('0x'): | |
continue | |
parts = line.split() | |
start = int(parts[0], 16) | |
end = int(parts[1], 16) | |
size = int(parts[2], 16) | |
offset = int(parts[3], 16) | |
file = '' | |
if len(parts) >= 5: | |
file = parts[4] | |
mappings.append(Mapping(start, end, size, offset, file)) | |
return mappings | |
def parse_maintenance_info_sections(text): | |
mappings = [] | |
current_obj_file = '' | |
for line in text.split('\n'): | |
if 'Object file:' in line: | |
current_obj_file = line.split('Object file:')[1].strip() | |
#print(f'Object file "{current_obj_file}"') | |
elif ' at ' in line: | |
before_at, after_at = line.split(' at ') | |
split_line = after_at.split() | |
#print(f"split_line={before_at} {split_line}") | |
start, end = before_at.split('->') | |
start = int(start.split()[-1], 16) | |
end = int(end, 16) | |
size = end - start | |
offset = int(split_line[0].rstrip(':'), 16) | |
section_name = split_line[1] | |
attrs = split_line[2:] | |
if start and not ' ' in current_obj_file: # and 'CODE' in attrs: | |
mappings.append(Mapping(start, end, size, offset, current_obj_file, [section_name] + attrs)) | |
return mappings | |
def dedup_mappings(mappings): | |
mappings = sorted(mappings, key=lambda m:(m.start, m.end)) | |
deduped = mappings[:1] | |
for a, b in zip(mappings[:-1],mappings[1:]): | |
if a.start == b.start and a.end == b.end: | |
continue | |
deduped.append(b) | |
return deduped | |
def remove_subsections(mappings): | |
return [m for m in mappings if not any(m.inside(other) for other in mappings)] | |
def fix_overlap(mappings): | |
mappings = sorted(mappings, key=lambda m:(m.start, m.end)) | |
for m in mappings: | |
overlaps = [m2 for m2 in mappings if m != m2 and m2.start <= m.start and m.start < m2.end] | |
if overlaps: | |
new_start = max([m2.end for m2 in overlaps]) | |
m.offset = m.offset + (new_start - m.start) | |
m.start = new_start | |
if m.start > m.end: | |
m.end = m.start | |
m.size = m.end - m.start | |
return mappings | |
''' | |
#result = gdb.execute('maintenance info sections ALLOBJ') | |
result = run_cmd('info proc mappings') | |
mappings = parse_info_proc_mappings(result) | |
print("info proc mappings output:") | |
for mapping in mappings: | |
print(mapping) | |
''' | |
def define_info_proc_mapping_equivalent(): | |
maint_sec = run_cmd('maintenance info sections ALLOBJ') | |
sections = parse_maintenance_info_sections(maint_sec) | |
for mapping in sections: | |
mapping.round_page() | |
sections = [s for s in sections if s.offset >= 0] | |
sections = [s for s in sections if '/usr/lib/debug' not in s.file] | |
deduped_sections = dedup_mappings(sections) | |
cleaned_sections = remove_subsections(deduped_sections) | |
non_overlapping = fix_overlap(cleaned_sections) | |
''' | |
print("All sections:") | |
for mapping in sections: | |
print(mapping) | |
print("Deduped sections:") | |
for mapping in deduped_sections: | |
print(mapping) | |
print("Cleaned sections:") | |
for mapping in cleaned_sections: | |
print(mapping) | |
print("Fixing overlap") | |
for mapping in fix_overlap(cleaned_sections): | |
print(mapping) | |
''' | |
cmd_txt = 'define info proc mappings\n' | |
cmd_lines = [] | |
# This gets pid 1 when running against a QEMU user-mode instance. | |
# The PID is not meaningful for querying the | |
#cmd_lines.append(f'process {gdb.selected_inferior().pid}') | |
cmd_lines.append('Mapped address spaces:') | |
cmd_lines.append('') | |
cmd_lines.append(' Start Addr End Addr Size Offset objfile') | |
for mapping in non_overlapping: | |
cmd_lines.append(f'{mapping}') | |
cmd_txt += ''.join(f'echo {line}\\n\n' for line in cmd_lines) | |
cmd_txt += 'end\n' | |
print(cmd_txt) | |
with open('/tmp/test_mapping', 'w') as fd: | |
fd.write(cmd_txt) | |
run_cmd('source /tmp/test_mapping') | |
#run_cmd(cmd_txt) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment