def read(self, file_name): fin = open(file_name, 'rb') magic_word = self.read_long(fin) if magic_word != SolutionWriter.MAGIC_WORD: raise RuntimeError('The file is not a binary solution file') program = {} original = {} try: addr_count = self.read_int(fin) for i in range(0, addr_count): addr = self.read_int(fin) count = self.read_int(fin) if count > 0: program[addr] = [ CAPSInstruction(self.read_int(fin), addr) for j in range(0, count) ] original[addr] = [ CAPSInstruction(program[addr][count - 1].encoding, addr) ] finally: fin.close() return original, program
def test_opcode_field(self): caps = CAPSInstruction(0xe52de004, 0x10550) # str;lr, [sp, #-4]! self.assertEqual(caps.opcode_field, ARM_INS_STR) caps = CAPSInstruction(0xe28cca10, 0x10550) # add ip, ip, #0x10000 self.assertEqual(caps.opcode_field, ARM_INS_ADD) caps = CAPSInstruction(0x18bd8010, 0x10550) # popne;{r4, pc} self.assertEqual(caps.opcode_field, ARM_INS_POP)
def test_conditional_field(self): caps = CAPSInstruction(0xe52de004, 0x10550) # str;lr, [sp, #-4]! self.assertEqual(caps.conditional_field, ARM_CC_AL - 1) caps = CAPSInstruction(0x106f4, 0x10550) # strdeq r0, r1, [r1], -r4 self.assertEqual(caps.conditional_field, ARM_CC_EQ - 1) caps = CAPSInstruction(0x18bd8010, 0x10550) # popne;{r4, pc} self.assertEqual(caps.conditional_field, ARM_CC_NE - 1)
def test_is_a(self): self.assertTrue(CAPSInstruction(0x18bd8010, 0x10550).is_a('pop')) # popne {r4, pc} self.assertTrue(CAPSInstruction(0xe92d4010, 0x10550).is_a('push')) # push {r4, lr} self.assertTrue(CAPSInstruction( 0x106f4, 0x10550).is_a('str')) # strdeq r0, r1, [r1], -r4 self.assertTrue(CAPSInstruction( 0xe28cca10, 0x10550).is_a('add')) # add ip, ip, #0x10000
def test_modify_rn(self): inst = CAPSInstruction('000209b4', 8) # strheq;r0, [r2], -r4 mod = InstructionModifier() self.assertEqual( 'strheq\tr0, [r0], -r4', str(CAPSInstruction(mod.modify_rn(inst.encoding, 0), 8))) self.assertEqual( 'strheq\tr0, [r4], -r4', str(CAPSInstruction(mod.modify_rn(inst.encoding, 4), 8))) self.assertEqual( 'strheq\tr0, [sl], -r4', str(CAPSInstruction(mod.modify_rn(inst.encoding, 10), 8)))
def test_modify_rm(self): inst = CAPSInstruction('000209b4', 8) # strheq;r0, [r2], -r4 mod = InstructionModifier() self.assertEqual( 'strheq\tr0, [r2], -r0', str(CAPSInstruction(mod.modify_rm(inst.encoding, 0), 8))) self.assertEqual( 'strheq\tr0, [r2], -r6', str(CAPSInstruction(mod.modify_rm(inst.encoding, 6), 8))) self.assertEqual( 'strheq\tr0, [r2], -sl', str(CAPSInstruction(mod.modify_rm(inst.encoding, 10), 8)))
def create_program(self): program = [] program.append(CAPSInstruction('e1a03000', 0)) # mov;r3, r0 program.append(CAPSInstruction('e59f1018', 1)) # ldr;r1, [pc, #0x18] program.append(CAPSInstruction('e1a00003', 2)) # mov;r0, r3 program.append(CAPSInstruction('ebffff9e', 3)) # bl;#0x10594 program.append(CAPSInstruction('e3a03000', 4)) # mov;r3, #0 program.append(CAPSInstruction('e1a00003', 5)) # mov;r0, r3 program.append(CAPSInstruction('e8bd8800', 6)) # pop;{fp, pc} program.append(CAPSInstruction('00010830', 7)) # andeq;r0, r1, r0, lsr r8 program.append(CAPSInstruction('000209b4', 8)) # strheq;r0, [r2], -r4 program.append(CAPSInstruction('00010570', 9)) # andeq;r0, r1, r0, ror r5 return program
def test_modify_rd(self): '0xe59f1018' inst = CAPSInstruction('e59f1018', 8) # ldr r1, [pc, #0x18] mod = InstructionModifier() self.assertEqual( 'ldr\tr0, [pc, #0x18]', str(CAPSInstruction(mod.modify_rd(inst.encoding, 0), 8))) self.assertEqual( 'ldr\tr4, [pc, #0x18]', str(CAPSInstruction(mod.modify_rd(inst.encoding, 4), 8))) self.assertEqual( 'ldr\tsl, [pc, #0x18]', str(CAPSInstruction(mod.modify_rd(inst.encoding, 10), 8)))
def read_functions(self): # Function header in our assembly p = re.compile("^\.\w+\:[0-9a-f]+\s*\<[\$|\w+]") if self._instruction_set != DisassembleReader.ARM_SET: raise RuntimeError("Instruction encoding not supported yet") result = [] k, i = "no_method", 0 result.append(ElfFunction(k)) for line in open(self._filename): line = line.rstrip('\n') if p.match(line): k = line if k in result: i += 1 k = line + i result.append(ElfFunction(k)) elif len(line) > 0 and line[0] == ' ': e = line.split(":", 1)[1].split(" ", 1)[0].split(" ", 1) f = result[len(result) - 1] encoding = Instruction.reverse_endianess(e[1]) f.instructions.append(CAPSInstruction(encoding, position=int(e[0], 16))) return result
def corrupt_program(program, err_percent, max_amount): """ Corrupts a program. :param program: Program to corrupt :param err_percent: Percentage of instructions to be corrupted from 0 - 100. The routine will always corrupt at least one instruction with one bit of error :param max_amount: Maximal amount of bit errors per instructions, meaning that each corrupted instructions can have up to this amount of errors """ if 0 > err_percent > 100: raise RuntimeError("Percent cannot be larger than 100 or lower than 0") max_amount = max(1, max_amount) err_percent /= 100 len_program = len(program) corrupted_amount = math.ceil(max(1, len_program * err_percent)) # Find the min and max address in the program min_addr = sys.maxsize max_addr = -sys.maxsize for k in program: min_addr = k if min_addr > k else min_addr max_addr = k if max_addr < k else max_addr while corrupted_amount > 0: p = random.randint(0, len_program) * 4 + min_addr if p in program and len(program[p]) == 1: a = random.randint(1, max_amount) for r in corrupt_bits(31, 0, a, program[p][0].encoding): if _encoding_not_in_list(r, program[p]): program[p].append(CAPSInstruction(r, program[p][0].address)) corrupted_amount -= 1
def _change_parts(self, changer, get_parts, with_part, frequency): """ Change some part of the instruction to increase its frequency :param changer: method to change the instruction part :param next: Function that tells if more parts of that type remains :param with_part: Instruction containing that part :param frequency: Frequency dictionary :param qos: Quality of Service function """ qos = self._qos freq_sorted = list(frequency.keys()) freq_sorted.sort(key=lambda k: frequency[k]) progress_bar = TextProgressBar(iteration=0, total=len(freq_sorted), prefix='Changing Frequence:', decimals=0, bar_length=50, print_dist=1) # Go through all registers, from least frequent to more frequent for i in range(0, len(freq_sorted)): part = freq_sorted[i] # For all instructions containing this register for j in range(0, len(with_part[part])): instruction = with_part[part][j] # Try to change the register with a more frequent one, starting with the most frequent index = len(freq_sorted) - 1 # Flag to indicate that at least a register was correctly changed any_correct = False while not any_correct and index > part: # New, most frequent part new_part = freq_sorted[index] # Now try to change any infrequent register from the instruction. # There could be more than one use of the part # So try them all encoding = instruction.encoding for register_part in get_parts(encoding, part): # Change the address # Using the instruction modifier encoding = changer(encoding, part, new_part, register_part) # Run the program and check if it works correct_qos = qos.run(encoding, instruction.address) any_correct = any_correct or correct_qos # If it is correct, update the frequency dictionary if correct_qos: instruction = CAPSInstruction( encoding, instruction.address) with_part[part][j] = instruction if part in frequency: frequency[part] -= 1 # The most frequent register was no good, try next one if not any_correct: index -= 1 progress_bar.suffix = 'Iterations: {})'.format(i) progress_bar.progress()
def load_corrupted_program_from_json(file_path): with open(file_path) as data_file: data = json.load(data_file) result = {} for k, v in data.items(): addr = int(k) result[addr] = v for i in range(0, len(v)): v[i] = CAPSInstruction(v[i], addr) return result
def test_registers_used(self): # TODO: Change from capstone registers numbers to my register numbers caps = CAPSInstruction(0xe52de004, 0x10550) # str lr, [sp, #-4]! regs = caps.registers_used() self.assertEqual(2, len(regs)) self.assertTrue(AReg.LR in regs and AReg.SP in regs) caps = CAPSInstruction(0x106f4, 0x10550) # strdeq r0, r1, [r1], -r4 regs = caps.registers_used() self.assertEqual(3, len(regs)) self.assertTrue(AReg.R0 in regs and AReg.R1 in regs and AReg.R4 in regs) caps = CAPSInstruction(0x18bd8010, 0x10550) # popne {r4, pc} regs = caps.registers_used() self.assertEqual(3, len(regs)) self.assertTrue(AReg.R4 in regs and AReg.PC in regs and AReg.SP in regs)
def read_instructions(self): if self._instruction_set != DisassembleReader.ARM_SET: raise RuntimeError("Instruction encoding not supported yet") result = [] for line in open(self._filename): if line[0] == ' ': e = line.rstrip('\n').split(":", 1)[1].split(" ", 1)[0].split(" ", 1) encoding = Instruction.reverse_endianess(e[1]) instruction = CAPSInstruction(encoding, position=int(e[0], 16)) result.append(instruction) return result
def test_registers_written(self): caps = CAPSInstruction(0x18bd8010, 0x10550) # popne {r4, pc} regs = caps.registers_written() self.assertEqual(3, len(regs)) self.assertTrue(AReg.SP in regs and AReg.R4 in regs and AReg.PC in regs) caps = CAPSInstruction(0xe92d4800, 0x10550) # push {fp, lr} regs = caps.registers_written() self.assertEqual(0, len(regs))
def corrupt_instruction(program, original_instruction, address, conditional=True, registers=False, opcode=False, amount=2): # We corrupt an instruction bin_c = 1 if conditional else 0 bin_c = bin_c + 1 if registers else bin_c bin_c = bin_c + 1 if opcode else bin_c amount = distribute_random_amount(amount, bin_c) corrupted = [original_instruction.encoding] if conditional: bin_c -= 1 corrupted = corrupt_conditional(amount[bin_c], original_instruction.encoding) if registers: bin_c -= 1 r = [] for c in corrupted: r.extend([ cc for cc in corrupt_registers(amount[bin_c], c) if cc not in r ]) corrupted = r if opcode: bin_c -= 1 r = [] for c in corrupted: r.extend( [cc for cc in corrupt_opcode(amount[bin_c], c) if cc not in r]) corrupted = r #program[address] = [] for i in range(1, len(corrupted)): inst = CAPSInstruction(corrupted[i], original_instruction.address) if not inst.ignore: program[address].append(inst) return program[address]
def read(self): functions = {} current_fn = None self.instructions = [] section = None for line in open(self._filename): if line.strip() == "": continue elif line.startswith('.'): section = line.split(".")[1] else: if section.startswith('function'): address, name = line.split(';') functions[int(address, 16)] = ElfFunction(name.rstrip()) elif section.startswith('init') or section.startswith('text') or \ section.startswith('plt') or section.startswith('fini'): line_split = line.split(';') try: address, encoding = line_split[0], line_split[1] except: print('[ERROR] Cannot parse line: {}'.format(line)) continue encoding = int(encoding, 16) address = int(address, 16) inst = CAPSInstruction(encoding, position=address) if address in functions: current_fn = functions[address] if current_fn: current_fn.instructions.append(inst) self.instructions.append(inst) self.functions = [x for x in functions.values()] return self.functions, self.instructions
def test_modify_register(self): inst1 = CAPSInstruction('000209b4', 8) # strheq;r0, [r2], -r4 inst2 = CAPSInstruction('e59f1018', 8) # ldr r1, [pc, #0x18] mod = InstructionModifier() self.assertEqual( 'strheq\tr0, [r2], -r0', str(CAPSInstruction(mod.modify_register(inst1.encoding, 4, 0), 8))) self.assertEqual( 'strheq\tr0, [r6], -r4', str(CAPSInstruction(mod.modify_register(inst1.encoding, 2, 6), 8))) self.assertEqual( 'ldr\tr6, [pc, #0x18]', str(CAPSInstruction(mod.modify_register(inst2.encoding, 1, 6), 8))) self.assertEqual( 'ldr\tr1, [r6, #0x18]', str(CAPSInstruction(mod.modify_register(inst2.encoding, 15, 6), 8)))
def test_modifies_flags(self): self.assertTrue(CAPSInstruction( 0xe3530000, 0x10550).modifies_flags()) # cmp r3, #0 self.assertFalse( CAPSInstruction(0xe28cca10, 0x10550).modifies_flags()) # add ip, ip, #0x10000
def test_constructor(self): caps = CAPSInstruction(0xe52de004, 0x10550) self.assertEqual(0x10550, caps.address) self.assertEqual(str(caps), 'str\tlr, [sp, #-4]!') # This is optional, but I need to check self.assertEqual(0x10550, caps._cap.address)
def test_encoding(self): from_str = CAPSInstruction('000209b4', 8) from_int = CAPSInstruction(0x000209b4, 8) self.assertEqual(str(from_str), 'strheq\tr0, [r2], -r4') self.assertEqual(str(from_int), 'strheq\tr0, [r2], -r4')
def test_is_push_pop(self): self.assertTrue(CAPSInstruction(0x18bd8010, 0x10550).is_push_pop) # popne {r4, pc} self.assertTrue(CAPSInstruction(0xe92d4010, 0x10550).is_push_pop) # push {r4, lr}
def test_is_branch(self): self.assertTrue(CAPSInstruction( 0xe5bef008, 0x10550).is_branch) # ldr pc, [lr, #8]! self.assertTrue(CAPSInstruction(0xebffffeb, 0x10550).is_branch) # bl #0x105ac