def validate(self, operation, odb_file): """" Validate the type fits at the given address. """ vma = operation.vma # determine the size of this defined data size = operation.size # Verify there is not something already defined here for dd in odb_file.get_structure_list(DefinedData): if dd.overlaps(vma, size): raise DefineDataCollisionException( 'Data collides with existing data %s' % dd.var_name) # Verify there is enough room in the parcel parcels = ParcelList(odb_file.get_structure_list(Parcel)) parcel = parcels.find_parcel_by_vma(vma) if not parcel: raise ValidationError('Cannot define data at bad address 0x%x' % vma) elif parcel.is_code: raise ValidationError('Cannot define data in code region') if not parcel.contains_vma(vma + size - 1): raise ValidationError('Not enough room to define data')
def display(self, start, lines, logical): parcels = ParcelList(self.odb_file.get_structure_list(Parcel)) # if we are analyzing based on lda, first convert to vma if logical: start = parcels.lda_to_vma(start) if start is None: # TODO: Throw exception here? return [] # if we are going backwards if lines < 0: # Note: Use start+1 to include start in the returned disassembly lda_end = parcels.vma_to_lda(start) + 1 lda_start = lda_end + lines if lda_end > lines else 0 start = parcels.lda_to_vma(lda_start) lines = abs(lines) parcels = [p for p in parcels if p.vma_end > start and p.size > 0] display = DisplayMaster(self.odb_file, self.analyzer) dus = display.display(parcels, start, lines) sorted_du = list(dus.values()) sorted_du.sort(key=lambda x: x.vma, reverse=False) return sorted_du
def operate(self, odb_file): self.object_id = odb_file.get_object_id() branches = odb_file.get_structure_list(Branch) parcels = ParcelList( filter(lambda p: p.is_code, odb_file.get_structure_list(Parcel))) for b in branches: if not parcels.contains_vma(b.srcAddr): odb_file.remove_item(b)
def operate(self, odb_file): self.object_id = odb_file.get_object_id() parcels = ParcelList(odb_file.get_structure_list(Parcel)) parcel = parcels.find_parcel_by_vma(self.vma) maxLen = parcel.vma_end - self.vma ofd = Ofd(odb_file.binary) rawData = ofd.bfd.raw_data(ofd.bfd.sections[parcel.sec_name], self.vma, maxLen) rawData = bytes(rawData) # instantiate the type to determine its size type_inst = self.instantiate_type(self.type_kind, self.type_name, odb_file) self.size = type_inst.calc_size(rawData) if (self.size <= 0): raise DefineDataException("Cannot instantiate empty type") try: FitValidator().validate(self, odb_file) except DefineDataCollisionException as e: merged = False for dd in odb_file.get_structure_list(DefinedData): # find the defined data that overlaps if dd.overlaps(self.vma, self.size): # instantiate the class dd_inst = self.instantiate_type(dd.type_kind, dd.type_name, odb_file) # if we can merge if dd_inst.can_merge(self.type_kind, self.type_name): # delete the data we're going to merge with odb_file.execute(UndefineDataOperation(dd.vma)) # run the fit again just to be sure FitValidator().validate(self, odb_file) merged = True if not merged: raise e definedData = odb_file.create_item( DefinedData, { 'type_kind': self.type_kind, 'type_name': self.type_name, 'var_name': self.var_name, 'vma': self.vma, 'size': self.size, }) odb_file.insert_item(definedData)
def failing_test_follow_jmps_i386(self): odb_file = OdbFile( BinaryFile( self.get_test_bin_path( 'active_scan_follow_jmps/active_scan_follow_jmps.bin'), 'binary', 'i386')) odb_file.execute(LoadOperation()) odb_file.execute(PassiveScanOperation()) parcels_before = ParcelList(odb_file.get_structure_list(Parcel)) # number of parcels before the split self.assertEquals(1, len(parcels_before)) # execute the active scan operation odb_file.execute(ActiveScanOperation(0x0)) # number of parcels after the split parcels_after = ParcelList(odb_file.get_structure_list(Parcel)) self.assertEquals(11, len(parcels_after))
def lda_to_vma(self, lda): parcels = ParcelList(self.odb_file.get_structure_list(Parcel)) return parcels.lda_to_vma(lda)
def convert_to_code(vma, odb_file, analyzer, the_ofd): parcels = ParcelList(odb_file.get_structure_list(Parcel)) if len(parcels) == 0: raise Exception('Invalid operation: No parcels found') parcels_to_adjust = [p for p in parcels if p.vma_start > vma] p = parcels.find_parcel_by_vma(vma) if p is None: raise Exception('Invalid address: 0x%08x' % vma) # before doing anything, check if the given address starts with at least one valid instruction if not self._is_valid_instruction(the_ofd, analyzer, vma, p.vma_end): raise Exception("Failed to make code: invalid opcode") bad_code_scanner = BadCodeScanner(analyzer) function_scanner = FunctionScanner( analyzer, odb_file.get_structure_list(Symbol)) branch_scanner = BranchScanner(analyzer) parcel_scanner = ParcelOffsetScanner() scanners = [ # The parcel scanner must come first here, because we need it to 'push_vma' for the bad instruction, if # one is encountered parcel_scanner, bad_code_scanner, function_scanner, branch_scanner, ] # split the data parcel odb_file.execute(SplitParcelOperation(vma)) parcels = ParcelList(odb_file.get_structure_list(Parcel)) p = parcels.find_parcel_by_vma(vma) ldas_before_scan = p.size # one lda per byte def callback(addr, rawData, instr, abfd, self): for scanner in scanners: scanner.scan_line(addr, rawData, instr, abfd, p) section = the_ofd.get_section_from_addr(vma) the_ofd.disassemble(section.name, [], vma, p.vma_end, funcFmtLine=callback, funcFmtLineArgs={ 'self': self, }) ldas_removed = ldas_before_scan - p.num_ldas for p in parcels_to_adjust: p.lda_start -= ldas_removed for scanner in scanners: scanner.commit(odb_file) bad_addr = bad_code_scanner.bad_addr if bad_addr is not None: p = parcels.find_parcel_by_vma(bad_addr) odb_file.execute(SplitParcelOperation(bad_addr)) # # consolidate the code parcels # parcels = ParcelList(odb_file.get_structure_list(Parcel)) transaction = parcels.consolidate() # remove dead parcels for p in transaction.to_remove: odb_file.remove_item(p)
def operate(self, odb_file): self.object_id = odb_file.get_object_id() analyzer = get_processor(odb_file.get_arch(), odb_file) the_ofd = ofd.Ofd(odb_file.get_binary()) def convert_to_code(vma, odb_file, analyzer, the_ofd): parcels = ParcelList(odb_file.get_structure_list(Parcel)) if len(parcels) == 0: raise Exception('Invalid operation: No parcels found') parcels_to_adjust = [p for p in parcels if p.vma_start > vma] p = parcels.find_parcel_by_vma(vma) if p is None: raise Exception('Invalid address: 0x%08x' % vma) # before doing anything, check if the given address starts with at least one valid instruction if not self._is_valid_instruction(the_ofd, analyzer, vma, p.vma_end): raise Exception("Failed to make code: invalid opcode") bad_code_scanner = BadCodeScanner(analyzer) function_scanner = FunctionScanner( analyzer, odb_file.get_structure_list(Symbol)) branch_scanner = BranchScanner(analyzer) parcel_scanner = ParcelOffsetScanner() scanners = [ # The parcel scanner must come first here, because we need it to 'push_vma' for the bad instruction, if # one is encountered parcel_scanner, bad_code_scanner, function_scanner, branch_scanner, ] # split the data parcel odb_file.execute(SplitParcelOperation(vma)) parcels = ParcelList(odb_file.get_structure_list(Parcel)) p = parcels.find_parcel_by_vma(vma) ldas_before_scan = p.size # one lda per byte def callback(addr, rawData, instr, abfd, self): for scanner in scanners: scanner.scan_line(addr, rawData, instr, abfd, p) section = the_ofd.get_section_from_addr(vma) the_ofd.disassemble(section.name, [], vma, p.vma_end, funcFmtLine=callback, funcFmtLineArgs={ 'self': self, }) ldas_removed = ldas_before_scan - p.num_ldas for p in parcels_to_adjust: p.lda_start -= ldas_removed for scanner in scanners: scanner.commit(odb_file) bad_addr = bad_code_scanner.bad_addr if bad_addr is not None: p = parcels.find_parcel_by_vma(bad_addr) odb_file.execute(SplitParcelOperation(bad_addr)) # # consolidate the code parcels # parcels = ParcelList(odb_file.get_structure_list(Parcel)) transaction = parcels.consolidate() # remove dead parcels for p in transaction.to_remove: odb_file.remove_item(p) convert_to_code(self.vma, odb_file, analyzer, the_ofd) # convert newly found functions to code as well following_new_funcs = True while following_new_funcs: funcs = odb_file.get_structure_list(Function) parcels = ParcelList(odb_file.get_structure_list(Parcel)) # assume we are done unless we find at least one function that is within a data parcel following_new_funcs = False for f in funcs: p = parcels.find_parcel_by_vma(f.vma) if ( # if this function is at a valid address (p is not None) and # and the function is within a data parcel (not p.is_code) and # and the function looks to be valid code self._is_valid_instruction(the_ofd, analyzer, f.vma, p.vma_end)): # convert this function to code convert_to_code(f.vma, odb_file, analyzer, the_ofd) # take another pass, in case we discovered new functions to disassemble following_new_funcs = True # break out of the for loop, but not the while break
def test_consolidate_data(self): # positive, simple case p1 = DataParcel(0, 0, 0x1000, '.test') p2 = DataParcel(0, 0x1000, 0x2000, '.test') p3 = DataParcel(0, 0x2000, 0x3000, '.test') parcels = ParcelList([p1, p2, p3]) transaction = parcels.consolidate() self.assertEquals(1, len(parcels)) self.assertEquals(0, parcels[0].vma_start) self.assertEquals(0x3000, parcels[0].vma_end) self.assertEquals(0x3000, parcels[0].num_ldas) self.assertTrue(p2 in transaction.to_remove) self.assertTrue(p3 in transaction.to_remove) # positive, but only middle parcels merged p1 = DataParcel(0, 0, 0x0fff, '.test') p2 = DataParcel(0, 0x1000, 0x2000, '.test') p3 = DataParcel(0, 0x2000, 0x3000, '.test') p4 = DataParcel(0, 0x3001, 0x4000, '.test') parcels = ParcelList([p1, p2, p3, p4]) transaction = parcels.consolidate() self.assertEquals(3, len(parcels)) self.assertEquals(0, parcels[0].vma_start) self.assertEquals(0x1000, parcels[1].vma_start) self.assertEquals(0x3001, parcels[2].vma_start) self.assertEquals(0x0fff, parcels[0].vma_end) self.assertEquals(0x3000, parcels[1].vma_end) self.assertEquals(0x4000, parcels[2].vma_end) self.assertEquals(0x0fff, parcels[0].num_ldas) self.assertEquals(0x2000, parcels[1].num_ldas) self.assertEquals(0x0fff, parcels[2].num_ldas) self.assertTrue(p3 in transaction.to_remove) self.assertEquals(1, len(transaction.to_remove)) # negative, non-contiguous case p1 = DataParcel(0, 0, 0xffff, '.test') p2 = DataParcel(0, 0x1000, 0x1fff, '.test') p3 = DataParcel(0, 0x2000, 0x3000, '.test') parcels = ParcelList([p1, p2, p3]) transaction = parcels.consolidate() self.assertEquals(3, len(parcels)) self.assertEquals(0, len(transaction.to_remove)) self.assertTrue(p1 in parcels) self.assertTrue(p2 in parcels) self.assertTrue(p3 in parcels) # negative, disparate types case p1 = DataParcel(0, 0, 0x1000, '.test') p2 = CodeParcel(0, 0x1000, 0x2000, '.test') p3 = DataParcel(0, 0x2000, 0x3000, '.test') parcels = ParcelList([p1, p2, p3]) transaction = parcels.consolidate() self.assertEquals(3, len(parcels)) self.assertEquals(0, len(transaction.to_remove)) self.assertTrue(p1 in parcels) self.assertTrue(p2 in parcels) self.assertTrue(p3 in parcels) # negative, disparate sections case p1 = DataParcel(0, 0, 0x1000, '.test1') p2 = CodeParcel(0, 0x1000, 0x2000, '.test2') p3 = DataParcel(0, 0x2000, 0x3000, '.test3') parcels = ParcelList([p1, p2, p3]) transaction = parcels.consolidate() self.assertEquals(3, len(parcels)) self.assertEquals(0, len(transaction.to_remove)) self.assertTrue(p1 in parcels) self.assertTrue(p2 in parcels) self.assertTrue(p3 in parcels)
def total_lines(self): parcels = ParcelList(self.odb_file.get_structure_list(Parcel)) return parcels.sum_ldas()
def test_split_to_data_at_start_of_parcel(self): odb_file = OdbFile(BinaryFile(self.get_test_bin_path('ls'), 'elf64-x86-64', 'i386:x86-64')) odb_file.execute(LoadOperation()) odb_file.execute(PassiveScanOperation()) parcels_before = ParcelList(odb_file.get_structure_list(Parcel)) # start of .fini section p = parcels_before.find_parcel_by_vma(0x412bec) num_p1_ldas_before = p.num_ldas fini_lda_before = parcels_before.vma_to_lda(0x412bec) # number of parcels before the split self.assertEquals(25, len(parcels_before)) # execute the split operation odb_file.execute(SplitParcelOperation(0x412bec)) # number of parcels after the split (should only add one more) parcels_after = ParcelList(odb_file.get_structure_list(Parcel)) self.assertEquals(26, len(parcels_after)) # get the two parcels p1 = parcels_after.find_parcel_by_vma(0x412bec) p2 = parcels_after.find_parcel_by_vma(0x412bf0) # make sure they are unique self.assertNotEqual(p1, p2) # check num_ldas in p1 (4 data bytes) self.assertEquals(4, p1.num_ldas) # check num_ldas in p2 self.assertEquals(2, p2.num_ldas) # make sure vmas line up self.assertEquals(0x412bec, p1.vma_start) self.assertEquals(0x412bf0, p1.vma_end) self.assertEquals(0x412bf0, p2.vma_start) self.assertEquals(0x412bf5, p2.vma_end) # # make sure vma->lda mappings line up # # these shouldn't have changed self.assertEquals(fini_lda_before, parcels_after.vma_to_lda(0x412bec)) self.assertEquals(True, p2.is_code) # these changed self.assertEquals(fini_lda_before+1, parcels_after.vma_to_lda(0x412bed)) self.assertEquals(fini_lda_before+2, parcels_after.vma_to_lda(0x412bee)) self.assertEquals(fini_lda_before+3, parcels_after.vma_to_lda(0x412bef)) self.assertEquals(fini_lda_before+4, parcels_after.vma_to_lda(0x412bf0)) self.assertEquals(fini_lda_before+5, parcels_after.vma_to_lda(0x412bf4)) self.assertEquals(False, p1.is_code) # # make sure lda->vma mappings line up # # these shouldn't have changed self.assertEquals(0x412bec, parcels_after.lda_to_vma(fini_lda_before)) # these changed self.assertEquals(0x412bed, parcels_after.lda_to_vma(fini_lda_before+1)) self.assertEquals(0x412bee, parcels_after.lda_to_vma(fini_lda_before+2)) self.assertEquals(0x412bef, parcels_after.lda_to_vma(fini_lda_before+3)) self.assertEquals(0x412bf0, parcels_after.lda_to_vma(fini_lda_before+4)) self.assertEquals(0x412bf4, parcels_after.lda_to_vma(fini_lda_before+5))
def __init__(self, odb_file, vma): self.odb_file = odb_file start = None end = None self.parcels = ParcelList(odb_file.get_structure_list(Parcel)) # first, some validation p = self.parcels.find_parcel_by_vma(vma) if p: # initialize to parcel boundaries start = p.vma_start end = p.vma_end # if this is not in a code section if not p.is_code: raise Exception( "Graph view is not available for data sections!") # else, invalid address else: raise Exception( "Graph view is not available for invalid address 0x%x!" % vma) if type(odb_file.binary) == BinaryString: # TODO: Handle nonzero base address start = 0 end = odb_file.binary.size functions = sorted(odb_file.get_structure_list(Function), key=lambda f: f.vma) for f in functions: if vma >= f.vma: start = f.vma elif vma < f.vma: end = f.vma break # get all the branches within this function # TODO: Deal with indirect branches (i.e., jmp rax) branches = filter( lambda b: b.srcAddr >= start and b.srcAddr < end and b. targetAddr >= start and b.targetAddr < end, odb_file.get_structure_list(Branch)) # dict of nodes, keyed by starting address of the block self._nodes = {} thisBlock = start for b in sorted(branches, key=lambda b: b.srcAddr): # address if the branch is not taken (i.e., the addr of the next instruction) no = b.srcAddr + b.instrLen # address if the branch is taken yes = b.targetAddr # first node self._nodes[thisBlock] = BranchGraphNode(thisBlock, b.srcAddr, no - thisBlock, yes, no) # advance to next block thisBlock = no # make last block if thisBlock != end: end_line = self.parcels.vma_to_lda(end - 1) last_addr = self.parcels.lda_to_vma(end_line) self._nodes[thisBlock] = BranchGraphNode(thisBlock, last_addr, end - thisBlock, None, None) # for each target 'yes' branch address # NOTE: The 'no' branch addresses are guaranteed to be nodes by this point # NOTE: We can't iterate over the nodes with iteritems() since we modify the dictionary in the loop for addr in [n.yesAddr for n in self._nodes.values()]: # if this is a new node if addr not in self._nodes: # find the node that contains this address (the parent) and split it for parent in list(self._nodes.values()): if addr in parent: newNode = parent.split(addr, self.parcels) self._nodes[addr] = newNode
class BranchGraphView(list): def __init__(self, odb_file, vma): self.odb_file = odb_file start = None end = None self.parcels = ParcelList(odb_file.get_structure_list(Parcel)) # first, some validation p = self.parcels.find_parcel_by_vma(vma) if p: # initialize to parcel boundaries start = p.vma_start end = p.vma_end # if this is not in a code section if not p.is_code: raise Exception( "Graph view is not available for data sections!") # else, invalid address else: raise Exception( "Graph view is not available for invalid address 0x%x!" % vma) if type(odb_file.binary) == BinaryString: # TODO: Handle nonzero base address start = 0 end = odb_file.binary.size functions = sorted(odb_file.get_structure_list(Function), key=lambda f: f.vma) for f in functions: if vma >= f.vma: start = f.vma elif vma < f.vma: end = f.vma break # get all the branches within this function # TODO: Deal with indirect branches (i.e., jmp rax) branches = filter( lambda b: b.srcAddr >= start and b.srcAddr < end and b. targetAddr >= start and b.targetAddr < end, odb_file.get_structure_list(Branch)) # dict of nodes, keyed by starting address of the block self._nodes = {} thisBlock = start for b in sorted(branches, key=lambda b: b.srcAddr): # address if the branch is not taken (i.e., the addr of the next instruction) no = b.srcAddr + b.instrLen # address if the branch is taken yes = b.targetAddr # first node self._nodes[thisBlock] = BranchGraphNode(thisBlock, b.srcAddr, no - thisBlock, yes, no) # advance to next block thisBlock = no # make last block if thisBlock != end: end_line = self.parcels.vma_to_lda(end - 1) last_addr = self.parcels.lda_to_vma(end_line) self._nodes[thisBlock] = BranchGraphNode(thisBlock, last_addr, end - thisBlock, None, None) # for each target 'yes' branch address # NOTE: The 'no' branch addresses are guaranteed to be nodes by this point # NOTE: We can't iterate over the nodes with iteritems() since we modify the dictionary in the loop for addr in [n.yesAddr for n in self._nodes.values()]: # if this is a new node if addr not in self._nodes: # find the node that contains this address (the parent) and split it for parent in list(self._nodes.values()): if addr in parent: newNode = parent.split(addr, self.parcels) self._nodes[addr] = newNode ''' { nodes: [ {id:X, instructions: [du1, du2, du3, du4 ... } {id:Y, instructions: [du9, du10, du11 .... } .... ] links: [ { from:x, to:y, type: 'taken' } { from:y, to: z, type: 'notTaken'} { from:o, to: q, type: 'unconditional'} ] } ''' def getNodes(self): dasm = Disassembler(self.odb_file) # convert _nodes to representation needed by front end nodes = [] for startAddr, node in self._nodes.items(): startLine = self.parcels.vma_to_lda(startAddr) endLine = self.parcels.vma_to_lda(startAddr + node.size) if endLine is None: endLine = self.parcels.sum_ldas() dus = dasm.display(startAddr, endLine - startLine, False) nodes.append({'id': node.id, 'instructions': dus}) return nodes def getLinks(self): links = [] # convert _nodes to representation needed by front end for startAddr, node in self._nodes.items(): try: if node.noAddr: links.append({ 'from': node.id, 'to': self._nodes[node.noAddr].id, 'type': 'notTaken' }) links.append({ 'from': node.id, 'to': self._nodes[node.yesAddr].id, 'type': 'taken' }) elif node.yesAddr: links.append({ 'from': node.id, 'to': self._nodes[node.yesAddr].id, 'type': 'unconditional' }) else: # else, this is the last node in the function, which has no links pass except Exception as e: # for now, keep going if something goes wrong # AVD@9/8/15: An exception can happen right now if the last instruction in the function is a # branch to somewhere in the middle of the function. The problem is that the "no" # address is not within the function, since it is actually the first instruction of # the next function. Need to revisit this and see if we can do better than just # skipping this link. continue return links nodes = property(getNodes) links = property(getLinks)
def make_data(self, vma): parcels = ParcelList(self.odb_file.get_structure_list(Parcel)) p = parcels.find_parcel_by_vma(vma) if not p.is_code: raise Exception("Cannot make data on non-code parcel") self.odb_file.execute(SplitParcelOperation(vma))
def test_split_to_data_basic(self): odb_file = OdbFile(BinaryFile(self.get_test_bin_path('ls'), 'elf64-x86-64', 'i386:x86-64')) odb_file.execute(LoadOperation()) odb_file.execute(PassiveScanOperation()) parcels_before = ParcelList(odb_file.get_structure_list(Parcel)) p = parcels_before.find_parcel_by_vma(0x4029c0) num_p1_ldas_before = p.num_ldas text_lda_before = parcels_before.vma_to_lda(0x4029c0) fini_lda_before = parcels_before.vma_to_lda(0x412bec) # number of parcels before the split self.assertEquals(25, len(parcels_before)) # execute the split operation odb_file.execute(SplitParcelOperation(0x4029d5)) # number of parcels after the split parcels_after = ParcelList(odb_file.get_structure_list(Parcel)) self.assertEquals(27, len(parcels_after)) # get the three parcels p1 = parcels_after.find_parcel_by_vma(0x4029c0) p2 = parcels_after.find_parcel_by_vma(0x4029d5) p3 = parcels_after.find_parcel_by_vma(0x4029da) # make sure they are all unique self.assertNotEqual(p1, p2) self.assertNotEqual(p1, p3) self.assertNotEqual(p2, p3) # check num_ldas in p1 (5 instructions) self.assertEquals(5, p1.num_ldas) # check num_ldas in p2 (this one instruction has now become 5 data bytes) self.assertEquals(5, p2.num_ldas) # check num_ldas in p3 (less 5 from p1 and less 1 from p2 - remember it used to only be 1 lda before the split) self.assertEquals(num_p1_ldas_before - 5 - 1, p3.num_ldas) # make sure vmas line up self.assertEquals(0x4029c0, p1.vma_start) self.assertEquals(0x4029d5, p1.vma_end) self.assertEquals(0x4029d5, p2.vma_start) self.assertEquals(0x4029da, p2.vma_end) self.assertEquals(0x4029da, p3.vma_start) self.assertEquals(0x412bec, p3.vma_end) # # make sure vma->lda mappings line up # # these shouldn't have changed self.assertEquals(text_lda_before, parcels_after.vma_to_lda(0x4029c0)) self.assertEquals(text_lda_before+5, parcels_after.vma_to_lda(0x4029d5)) self.assertEquals(True, p1.is_code) self.assertEquals(True, p3.is_code) # these changed self.assertEquals(text_lda_before+6, parcels_after.vma_to_lda(0x4029d6)) self.assertEquals(text_lda_before+7, parcels_after.vma_to_lda(0x4029d7)) self.assertEquals(text_lda_before+8, parcels_after.vma_to_lda(0x4029d8)) self.assertEquals(text_lda_before+9, parcels_after.vma_to_lda(0x4029d9)) self.assertEquals(text_lda_before+10, parcels_after.vma_to_lda(0x4029da)) self.assertEquals(text_lda_before+11, parcels_after.vma_to_lda(0x4029db)) self.assertEquals(False, p2.is_code) # 1 instruction turned into 5 data bytes, so it shifted the following parcels by 4 ldas self.assertEquals(fini_lda_before+4, parcels_after.vma_to_lda(0x412bec)) # # make sure lda->vma mappings line up # # these shouldn't have changed self.assertEquals(0x4029c0, parcels_after.lda_to_vma(text_lda_before)) self.assertEquals(0x4029d5, parcels_after.lda_to_vma(text_lda_before+5)) # these changed self.assertEquals(0x4029d6, parcels_after.lda_to_vma(text_lda_before+6)) self.assertEquals(0x4029d7, parcels_after.lda_to_vma(text_lda_before+7)) self.assertEquals(0x4029d8, parcels_after.lda_to_vma(text_lda_before+8)) self.assertEquals(0x4029d9, parcels_after.lda_to_vma(text_lda_before+9)) self.assertEquals(0x4029da, parcels_after.lda_to_vma(text_lda_before+10)) self.assertEquals(0x4029db, parcels_after.lda_to_vma(text_lda_before+11)) # 1 instruction turned into 5 data bytes, so it shifted the following parcels by 4 ldas self.assertEquals(0x412bec, parcels_after.lda_to_vma(fini_lda_before+4))
def make_code(self, vma): parcels = ParcelList(self.odb_file.get_structure_list(Parcel)) p = parcels.find_parcel_by_vma(vma) if p.is_code: raise Exception("Cannot make code on a code parcel") self.odb_file.execute(ActiveScanOperation(vma))
def test_split_to_data_at_end_of_parcel(self): odb_file = OdbFile(BinaryFile(self.get_test_bin_path('ls'), 'elf64-x86-64', 'i386:x86-64')) odb_file.execute(LoadOperation()) odb_file.execute(PassiveScanOperation()) parcels_before = ParcelList(odb_file.get_structure_list(Parcel)) # last instruction in .plt p = parcels_before.find_parcel_by_vma(0x4029bb) p3 = parcels_before.find_parcel_by_vma(0x4029c0) num_p1_ldas_before = p.num_ldas num_p3_ldas_before = p3.num_ldas plt_lda_before = parcels_before.vma_to_lda(0x402320) text_lda_before = parcels_before.vma_to_lda(0x4029c0) # number of parcels before the split self.assertEquals(25, len(parcels_before)) # execute the split operation odb_file.execute(SplitParcelOperation(0x4029bb)) # number of parcels after the split (should only add one more) parcels_after = ParcelList(odb_file.get_structure_list(Parcel)) self.assertEquals(26, len(parcels_after)) # get the two parcels (plus the .text one following) p1 = parcels_after.find_parcel_by_vma(0x402320) p2 = parcels_after.find_parcel_by_vma(0x4029bb) p3 = parcels_after.find_parcel_by_vma(0x4029c0) # make sure they are all unique self.assertNotEqual(p1, p2) self.assertNotEqual(p1, p3) self.assertNotEqual(p2, p3) # check num_ldas in p1 (5 instructions) self.assertEquals(num_p1_ldas_before - 1, p1.num_ldas) # check num_ldas in p2 (this one instruction has now become 5 data bytes) self.assertEquals(5, p2.num_ldas) # check num_ldas in p3 (should not have changed) self.assertEquals(num_p3_ldas_before, p3.num_ldas) # make sure vmas line up self.assertEquals(0x402320, p1.vma_start) self.assertEquals(0x4029bb, p1.vma_end) self.assertEquals(0x4029bb, p2.vma_start) self.assertEquals(0x4029c0, p2.vma_end) self.assertEquals(0x4029c0, p3.vma_start) self.assertEquals(0x412bec, p3.vma_end) # # make sure vma->lda mappings line up # # these shouldn't have changed self.assertEquals(plt_lda_before, parcels_after.vma_to_lda(0x402320)) self.assertEquals(plt_lda_before+num_p1_ldas_before-2, parcels_after.vma_to_lda(0x4029b6)) self.assertEquals(plt_lda_before+num_p1_ldas_before-1, parcels_after.vma_to_lda(0x4029bb)) self.assertEquals(True, p1.is_code) self.assertEquals(True, p3.is_code) # these changed self.assertEquals(plt_lda_before+num_p1_ldas_before, parcels_after.vma_to_lda(0x4029bc)) self.assertEquals(plt_lda_before+num_p1_ldas_before+1, parcels_after.vma_to_lda(0x4029bd)) self.assertEquals(plt_lda_before+num_p1_ldas_before+2, parcels_after.vma_to_lda(0x4029be)) self.assertEquals(plt_lda_before+num_p1_ldas_before+3, parcels_after.vma_to_lda(0x4029bf)) self.assertEquals(plt_lda_before+num_p1_ldas_before+4, parcels_after.vma_to_lda(0x4029c0)) self.assertEquals(False, p2.is_code) # 1 instruction turned into 5 data bytes, so it shifted the following parcels by 4 ldas self.assertEquals(text_lda_before+4, parcels_after.vma_to_lda(0x4029c0)) # # make sure lda->vma mappings line up # # these shouldn't have changed self.assertEquals(0x402320, parcels_after.lda_to_vma(plt_lda_before)) self.assertEquals(0x4029b6, parcels_after.lda_to_vma(plt_lda_before+num_p1_ldas_before-2)) self.assertEquals(0x4029bb, parcels_after.lda_to_vma(plt_lda_before+num_p1_ldas_before-1)) # these changed self.assertEquals(0x4029bc, parcels_after.lda_to_vma(plt_lda_before+num_p1_ldas_before)) self.assertEquals(0x4029bd, parcels_after.lda_to_vma(plt_lda_before+num_p1_ldas_before+1)) self.assertEquals(0x4029be, parcels_after.lda_to_vma(plt_lda_before+num_p1_ldas_before+2)) self.assertEquals(0x4029bf, parcels_after.lda_to_vma(plt_lda_before+num_p1_ldas_before+3)) self.assertEquals(0x4029c0, parcels_after.lda_to_vma(plt_lda_before+num_p1_ldas_before+4)) # 1 instruction turned into 5 data bytes, so it shifted the following parcels by 4 ldas self.assertEquals(0x4029c0, parcels_after.lda_to_vma(text_lda_before+4))
def get_text_listing(self): display = DisplayMaster(self.odb_file, self.analyzer) parcels = ParcelList(self.odb_file.get_structure_list(Parcel)) return display.get_text_listing(parcels)
def test_split_to_data_single_inst_in_parcel(self): odb_file = OdbFile(BinaryFile( self.get_test_bin_path('code_split_single_instruction/split_parcel_single'), 'elf64-x86-64', 'i386:x86-64')) odb_file.execute(LoadOperation()) odb_file.execute(PassiveScanOperation()) parcels_before = ParcelList(odb_file.get_structure_list(Parcel)) # only one instruction in .single p = parcels_before.find_parcel_by_vma(0x400598) p_text = parcels_before.find_parcel_by_vma(0x400597) p_fini = parcels_before.find_parcel_by_vma(0x4005a0) p_got = parcels_before.find_parcel_by_vma(0x600fe0) num_ldas_before = p.num_ldas num_text_ldas_before = p_text.num_ldas num_fini_ldas_before = p_fini.num_ldas single_lda_before = p.lda_start fini_lda_before = p_fini.lda_start text_lda_before = p_text.lda_start got_lda_before = p_got.lda_start # number of parcels before the split self.assertEquals(25, len(parcels_before)) # execute the split operation odb_file.execute(SplitParcelOperation(0x400598)) # number of parcels after the split (should not have changed) parcels_after = ParcelList(odb_file.get_structure_list(Parcel)) self.assertEquals(25, len(parcels_after)) # get the parcels p = parcels_after.find_parcel_by_vma(0x400598) p_text = parcels_after.find_parcel_by_vma(0x400597) p_fini = parcels_after.find_parcel_by_vma(0x4005a0) # make sure they are all unique self.assertNotEqual(p, p_text) self.assertNotEqual(p, p_fini) self.assertNotEqual(p_text, p_fini) # check num_ldas in p (1 instruction become 7 data bytes) self.assertEquals(7, p.num_ldas) # check num_ldas in text didn't change self.assertEquals(num_text_ldas_before, p_text.num_ldas) # check num_ldas in fini didn't change self.assertEquals(num_fini_ldas_before, p_fini.num_ldas) # make sure vmas line up self.assertEquals(0x4003d0, p_text.vma_start) self.assertEquals(0x400598, p_text.vma_end) self.assertEquals(0x400598, p.vma_start) self.assertEquals(0x40059f, p.vma_end) self.assertEquals(0x4005a0, p_fini.vma_start) self.assertEquals(0x4005ae, p_fini.vma_end) # # make sure vma->lda mappings line up # # these shouldn't have changed self.assertEquals(text_lda_before, parcels_after.vma_to_lda(0x4003d0)) self.assertEquals(text_lda_before+num_text_ldas_before-1, parcels_after.vma_to_lda(0x400597)) self.assertEquals(single_lda_before, parcels_after.vma_to_lda(0x400598)) self.assertEquals(True, p_text.is_code) self.assertEquals(True, p_fini.is_code) # these changed self.assertEquals(single_lda_before+1, parcels_after.vma_to_lda(0x400599)) self.assertEquals(single_lda_before+2, parcels_after.vma_to_lda(0x40059a)) self.assertEquals(single_lda_before+3, parcels_after.vma_to_lda(0x40059b)) self.assertEquals(single_lda_before+4, parcels_after.vma_to_lda(0x40059c)) self.assertEquals(single_lda_before+5, parcels_after.vma_to_lda(0x40059d)) self.assertEquals(single_lda_before+6, parcels_after.vma_to_lda(0x40059e)) self.assertEquals(False, p.is_code) # 1 instruction turned into 7 data bytes, so it shifted the following parcels by 6 ldas self.assertEquals(fini_lda_before+6, parcels_after.vma_to_lda(0x4005a0)) self.assertEquals(got_lda_before+6, parcels_after.vma_to_lda(0x600fe0)) # # make sure lda->vma mappings line up # self.assertEquals(text_lda_before, parcels_after.vma_to_lda(0x4003d0)) self.assertEquals(text_lda_before+num_text_ldas_before-1, parcels_after.vma_to_lda(0x400597)) self.assertEquals(single_lda_before, parcels_after.vma_to_lda(0x400598)) # these shouldn't have changed self.assertEquals(0x4003d0, parcels_after.lda_to_vma(text_lda_before)) self.assertEquals(0x400597, parcels_after.lda_to_vma(text_lda_before+num_text_ldas_before-1)) self.assertEquals(0x400598, parcels_after.lda_to_vma(single_lda_before)) # these changed self.assertEquals(0x400599, parcels_after.lda_to_vma(single_lda_before+1)) self.assertEquals(0x40059a, parcels_after.lda_to_vma(single_lda_before+2)) self.assertEquals(0x40059b, parcels_after.lda_to_vma(single_lda_before+3)) self.assertEquals(0x40059c, parcels_after.lda_to_vma(single_lda_before+4)) self.assertEquals(0x40059d, parcels_after.lda_to_vma(single_lda_before+5)) self.assertEquals(0x40059e, parcels_after.lda_to_vma(single_lda_before+6)) # 1 instruction turned into 7 data bytes, so it shifted the following parcels by 6 ldas self.assertEquals(0x4005a0, parcels_after.lda_to_vma(fini_lda_before+6)) self.assertEquals(0x600fe0, parcels_after.lda_to_vma(got_lda_before+6))
def vma_to_lda(self, vma): parcels = ParcelList(self.odb_file.get_structure_list(Parcel)) return parcels.vma_to_lda(vma)
def test_basic_active_scan(self): # we load this ELF as a raw binary, because it has an invalid opcode fairly early on odb_file = OdbFile( BinaryFile(self.get_test_bin_path('mkdir'), 'elf64-x86-64', 'i386:x86-64')) odb_file.execute(LoadOperation()) odb_file.execute(PassiveScanOperation()) parcels_before = ParcelList(odb_file.get_structure_list(Parcel)) p = parcels_before.find_parcel_by_vma(0x4002ce) gnu_hash_lda_before = parcels_before.vma_to_lda(0x4002cd) dynsym_lda_before = parcels_before.vma_to_lda(0x4002e0) dynstr_lda_before = parcels_before.vma_to_lda(0x400a18) # number of parcels before the split self.assertEquals(24, len(parcels_before)) # execute the split operation odb_file.execute(ActiveScanOperation(0x4002ce)) # number of parcels after the split parcels_after = ParcelList(odb_file.get_structure_list(Parcel)) self.assertEquals(26, len(parcels_after)) # get the three parcels p1 = parcels_after.find_parcel_by_vma(0x400298) p2 = parcels_after.find_parcel_by_vma(0x4002ce) p3 = parcels_after.find_parcel_by_vma(0x4002d5) # make sure they are all unique self.assertNotEqual(p1, p2) self.assertNotEqual(p1, p3) self.assertNotEqual(p2, p3) # check num_ldas in p1 (54 bytes) self.assertEquals(54, p1.num_ldas) # check num_ldas in p2 (3 instructions) self.assertEquals(3, p2.num_ldas) # check num_ldas in p3 (7 bytes) self.assertEquals(7, p3.num_ldas) # make sure vmas line up self.assertEquals(0x400298, p1.vma_start) self.assertEquals(0x4002ce, p1.vma_end) self.assertEquals(0x4002ce, p2.vma_start) self.assertEquals(0x4002d5, p2.vma_end) self.assertEquals(0x4002d5, p3.vma_start) self.assertEquals(0x4002dc, p3.vma_end) # # make sure vma->lda mappings line up # # these shouldn't have changed self.assertEquals(gnu_hash_lda_before, parcels_after.vma_to_lda(0x4002cd)) self.assertEquals(gnu_hash_lda_before + 1, parcels_after.vma_to_lda(0x4002ce)) self.assertEquals(False, p1.is_code) self.assertEquals(False, p3.is_code) # these changed self.assertEquals(gnu_hash_lda_before + 2, parcels_after.vma_to_lda(0x4002d0)) self.assertEquals(gnu_hash_lda_before + 3, parcels_after.vma_to_lda(0x4002d2)) self.assertEquals(gnu_hash_lda_before + 4, parcels_after.vma_to_lda(0x4002d5)) self.assertEquals(gnu_hash_lda_before + 5, parcels_after.vma_to_lda(0x4002d6)) self.assertEquals(gnu_hash_lda_before + 6, parcels_after.vma_to_lda(0x4002d7)) self.assertEquals(gnu_hash_lda_before + 7, parcels_after.vma_to_lda(0x4002d8)) self.assertEquals(gnu_hash_lda_before + 8, parcels_after.vma_to_lda(0x4002d9)) self.assertEquals(gnu_hash_lda_before + 9, parcels_after.vma_to_lda(0x4002da)) self.assertEquals(gnu_hash_lda_before + 10, parcels_after.vma_to_lda(0x4002db)) self.assertEquals(True, p2.is_code) # 7 bytes turned into 3 instructions, so it shifted the following parcels by -4 ldas self.assertEquals(dynsym_lda_before - 4, parcels_after.vma_to_lda(0x4002e0)) self.assertEquals(dynstr_lda_before - 4, parcels_after.vma_to_lda(0x400a18)) # # make sure lda->vma mappings line up # # these shouldn't have changed self.assertEquals(0x4002cd, parcels_after.lda_to_vma(gnu_hash_lda_before)) self.assertEquals(0x4002ce, parcels_after.lda_to_vma(gnu_hash_lda_before + 1)) # these changed self.assertEquals(0x4002d0, parcels_after.lda_to_vma(gnu_hash_lda_before + 2)) self.assertEquals(0x4002d2, parcels_after.lda_to_vma(gnu_hash_lda_before + 3)) self.assertEquals(0x4002d5, parcels_after.lda_to_vma(gnu_hash_lda_before + 4)) self.assertEquals(0x4002d6, parcels_after.lda_to_vma(gnu_hash_lda_before + 5)) self.assertEquals(0x4002d7, parcels_after.lda_to_vma(gnu_hash_lda_before + 6)) self.assertEquals(0x4002d8, parcels_after.lda_to_vma(gnu_hash_lda_before + 7)) self.assertEquals(0x4002d9, parcels_after.lda_to_vma(gnu_hash_lda_before + 8)) self.assertEquals(0x4002da, parcels_after.lda_to_vma(gnu_hash_lda_before + 9)) self.assertEquals(0x4002db, parcels_after.lda_to_vma(gnu_hash_lda_before + 10)) # 7 bytes turned into 3 instructions, so it shifted the following parcels by -4 ldas self.assertEquals(0x4002e0, parcels_after.lda_to_vma(dynsym_lda_before - 4)) self.assertEquals(0x400a18, parcels_after.lda_to_vma(dynstr_lda_before - 4))