def replace_main_with_bkpt(binary): # Get the VMA and offset of the .text section for line in subprocess.check_output(['objdump', '-h', binary], universal_newlines=True).splitlines(): tokens = line.split() if len(tokens) == 7 and tokens[1] == '.text': text_vma = hex_int(tokens[3]) text_offset = hex_int(tokens[5]) break # Get the VMA of the 'main' symbol for line in subprocess.check_output(['objdump', '-t', binary], universal_newlines=True).splitlines(): tokens = line.split() if tokens and tokens[-1] == 'main': main_vma = hex_int(tokens[0]) break # Calculate the offset of the main symbol within the binary main_offset = text_offset + (main_vma - text_vma) # Write a bkpt on this offset with open(binary, 'r+b') as f_bin: f_bin.seek(main_offset) f_bin.write(bytes.fromhex('700020e1'))
def __init__(self, f_map, line, alignment_info=None): tokens = line.split() self.name = tokens[0] if len(tokens) == 1: # Grab the next line and decode it line2 = next(f_map) tokens = line2.split() assert len( tokens) == 3, 'Can only handle entries of 3 tokens here!' self.address = hex_int(tokens[0]) self.size = hex_int(tokens[1]) self.obj = tokens[2] else: # Get the information from this line assert len( tokens) == 4, 'Can only handle entries of 4 tokens here!' self.address = hex_int(tokens[1]) self.size = hex_int(tokens[2]) self.obj = tokens[3] # If we can find the alignment, try to do so if alignment_info: obj_name = self.obj[self.obj.rfind('/') + 1:] obj = alignment_info.objects.get(obj_name, None) if obj: self.alignment = obj.sections.get(self.name, None) assert self.alignment, 'Could not find alignment information for this section!'
def read(cls, offset, header, lines): if header.startswith('L'): # Decode the line, if a string is empty the diff is 0 tokens = header[1:].split(':') address_diff = hex_int(tokens[0]) if tokens[0] else 0 size_diff = hex_int(tokens[1]) if tokens[1] else 0 return (0, cls(offset, address_diff, size_diff)) else: return (0, None)
def __init__(self, line): self.lines = [] tokens = line.split() self.address = hex_int(tokens[1]) self.size = hex_int(tokens[2]) self.parameter_size = hex_int(tokens[3]) self.name = tokens[4:] # Data will be added onto these later on self.publics = [] self.stacks = []
def __init__(self, line): tokens = line.split() # Decode INIT records and other records differently if tokens[2] == 'INIT': self.address = hex_int(tokens[3]) self.size = hex_int(tokens[4]) self.entries = tokens[5:] else: self.address = hex_int(tokens[2]) self.size = 0 self.entries = tokens[3:]
def read(cls, header): if not header: return None tokens = header.split(':') if len(tokens) == 2: size_diff = hex_int(tokens[0]) * 8 if tokens[0] else 0 address_diff = hex_int(tokens[1]) else: size_diff = hex_int(tokens[0]) * 8 address_diff = 0 return cls(size_diff, address_diff)
def read(cls, offset, header, lines): if header.startswith('S'): tokens = header[1:].split(':') deleted_size = hex_int(tokens[0]) added_size = hex_int(tokens[1]) added_lines = [] for line in lines[:added_size]: added_lines.append(Line(line)) return (added_size, cls(offset, added_lines, deleted_size)) else: return (0, None)
def read(cls, path): self = cls() with open(path, 'r') as f: # Find all the function patches and determine how many empty patches there are between them patch_lines = [] for line in f: if 'F' in line: # If we were gathering lines for a patch (we won't be in the beginning), create it if patch_lines: self.func_patches.append(FuncPatch.read(patch_lines)) # Add this line (the patch header) to the lines patch_lines = [line] # Find out the offset. If none is encoded it was 0. With the offset we also know # how many empty function patches there are and can create them token_pos = line.find('F') offset = hex_int( line[:token_pos]) if (token_pos != 0) else 0 for _ in range(offset): self.func_patches.append(FuncPatch('')) else: # Add this line for the patch we are preparing patch_lines.append(line) # Finish last patch if patch_lines: self.func_patches.append(FuncPatch.read(patch_lines)) return self
def __init__(self, map_path, align_path=None): # Load all section alignment information if it is present if align_path is not None: alignment_info = AlignmentInformation(align_path) else: alignment_info = None with open(map_path, 'r') as f_map: self.discarded_sections = [] self.pre_sections = [] self.main_sections = [] self.post_sections = [] # The map consists of several parts which we process differently target_sections = None for line in f_map: # Determine where in the map we are if 'Discarded input sections' in line: target_sections = self.discarded_sections continue if 'Memory Configuration' in line: target_sections = None continue if '*(.text.unlikely' in line: target_sections = self.pre_sections continue if '*(.text.[a-zA-Z0-9]*' in line: target_sections = self.main_sections continue if '*(.text.*' in line: target_sections = self.post_sections continue # This is the farthest we go, anything behind this is not useful for us if '*(.gnu.warning)' in line: break # We haven't arrived to the good part yet if target_sections is None: # Look for the start address of the text segment m = re.search( '(?<=PROVIDE \(__executable_start = SEGMENT_START)\s*(\("text-segment",)\s+(?P<base>\w+)(?=\))', line) if m: self.text_start_address = hex_int(m.group('base')) continue # Look at all lines containing .text., but ignore some if '.text' in line and not ('*' in line or '.ARM.extab.text.' in line or '.ARM.exidx.text.' in line or '.text.__stub' in line): # Handle the line depending on where we are in the map # Decode the entry into an object target_sections.append(Section(f_map, line, alignment_info))
def run_replay_tool(seed, sections, build_prefix, opportunity_log): for line in subprocess.check_output( [ os.path.join(config.replay_dir, 'nop'), str(seed), str(config.nopinsertion_chance), build_prefix, opportunity_log ], universal_newlines=True).splitlines(): # If the line is the start of a new section, add it as an entry to the dictionary with a new list onto which insertions will be added. if ':' in line: assert line not in sections, 'Duplicate section!' insertions = [] sections[line] = insertions else: # For any insertion, the get offset at which it happens, and the size change it induces tokens = line.split() insertions.append( (support.hex_int(tokens[0]), support.hex_int(tokens[1])))
def read(cls, offset, header, lines): if header.startswith('A'): size = hex_int(header[1:]) added_lines = [] for line in lines[:size]: added_lines.append(Line(line)) return (size, cls(offset, added_lines)) else: return (0, None)
def read(cls, lines): self = cls('') # Decode the header and remove it from the lines tokens = lines[0].rstrip().split('F') if len(tokens) == 2 and tokens[1]: self.stack_patch = StackPatch.read(tokens[1]) del lines[0] # These lines consist of different patches, each with a header. We will iterate over the headers and # read in the lines associated with them. offset = -1 idx = 0 while idx < len(lines): # Get the header, strip the line, removing newline and such header = lines[idx].rstrip() # Get the location of the identifying uppercase, and the relative offset # If there relative offset is encoded, it was 0. up_idx = support.find_first_uppercase(header) offset += hex_int(header[:up_idx]) + 1 if (up_idx != 0) else 1 # Handle the right type of patch idx += 1 # Move to the line after the header, easier indexing after this for patch_type in (AdditionPatch, DeletionPatch, LinePatch, SubstitutionPatch): (size, patch) = patch_type.read(offset, header[up_idx:], lines[idx:]) if patch: self.patches.append(patch) break # Go to the next header. This header has already been skipped, so skip the extra lines (size) idx += size return self
def read(cls, offset, header, lines): if header.startswith('D'): size = hex_int(header[1:]) return (0, cls(offset, size)) else: return (0, None)
def get_binary_text_size(binary): for line in subprocess.check_output(['objdump', '-h', binary], universal_newlines=True).splitlines(): tokens = line.split() if len(tokens) >= 3 and tokens[1] == '.text': return hex_int(tokens[2])
def __init__(self, line): tokens = line.split() self.address = hex_int(tokens[0]) self.size = hex_int(tokens[1]) self.location = Location(int(tokens[2]), int(tokens[3]))
def __init__(self, line): tokens = line.split() self.address = hex_int(tokens[1]) self.parameter_size = hex_int(tokens[2]) self.name = tokens[3:]