def __init__(self, invalid: bytes): # create a silent qiling instance self.ql = Qiling([rf"{ROOTFS}/bin/crackme.exe"], ROOTFS, verbose=QL_VERBOSE.OFF) self.ql.os.stdin = pipe.SimpleInStream(sys.stdin.fileno( )) # take over the input to the program using a fake stdin self.ql.os.stdout = pipe.NullOutStream( sys.stdout.fileno()) # disregard program output # execute program until it reaches the part that asks for input self.ql.run(end=0x401030) # record replay starting and ending points. # # since the emulation halted upon entering a function, its return address is there on # the stack. we use it to limit the emulation till function returns self.replay_starts = self.ql.arch.regs.arch_pc self.replay_ends = self.ql.stack_read(0) # instead of restarting the whole program every time a new flag character is guessed, # we will restore its state to the latest point possible, fast-forwarding a good # amount of start-up code that is not affected by the input. # # here we save the state just when the read input function is about to be called so we # could use it to jumpstart the initialization part and get to the read input immediately self.jumpstart = self.ql.save() or {} # calibrate the replay instruction count by running the code with an invalid input # first. the instruction count returned from the calibration process will be then # used as a baseline for consequent replays self.best_icount = self.__run(invalid)
def our_sandbox(path: str, rootfs: str): ql = Qiling([path], rootfs, verbose=QL_VERBOSE.DEFAULT) # this crackme's logic lies within the function passed to DialogBoxParamA through # the lpDialogFunc parameter. normally DialogBoxParamA would call the function # passed through that parameter, but Qiling's implementation for it doesn't do # that. # # to solve this crackme and force the "success" dialog to show, we will: # 1. set up a mock stdin and feed it with the correct flag # 1. hook DialogBoxParamA to see where its lpDialogFunc param points to # 2. set up a valid set of arguments DialogFunc expects to see # 3. call it and see it greets us with a "success" message # [step #1] # set up a mock stdin and feed it with mocked keystrokes ql.os.stdin = pipe.SimpleInStream(sys.stdin.fileno()) ql.os.stdin.write(b'Ea5yR3versing\n') # [step #2] # intercept DialogBoxParamA on exit ql.os.set_api('DialogBoxParamA', hook_DialogBoxParamA_onexit, QL_INTERCEPT.EXIT) ql.run()
def main(input_file: str): mock_stdin = pipe.SimpleInStream(sys.stdin.fileno()) ql = Qiling( ["./x8664_fuzz"], "../../rootfs/x8664_linux", verbose=QL_VERBOSE.OFF, # keep qiling logging off console=False, # thwart program output stdin= mock_stdin, # redirect stdin to our mock to feed it with incoming fuzzed keystrokes stdout=None, stderr=None) def place_input_callback(uc: UcAfl.Uc, input: bytes, persistent_round: int, data: Any) -> Optional[bool]: """Called with every newly generated input. """ ql.os.stdin.write(input) def start_afl(_ql: Qiling): """Callback from inside. """ # We start our AFL forkserver or run once if AFL is not available. # This will only return after the fuzzing stopped. try: if not _ql.uc.afl_fuzz(input_file=input_file, place_input_callback=place_input_callback, exits=[ql.os.exit_point]): _ql.log.warning("Ran once without AFL attached") os._exit(0) except UcAfl.UcAflError as ex: # This hook triggers more than once in this example. # If this is the exception cause, we don't care. # TODO: choose a better hook position :) if ex.errno != UcAfl.UC_AFL_RET_CALLED_TWICE: raise # get image base address ba = ql.loader.images[0].base # make process crash whenever __stack_chk_fail@plt is about to be called. # this way afl will count stack protection violations as crashes ql.hook_address(callback=lambda x: os.abort(), address=ba + 0x1225) # set a hook on main() to let unicorn fork and start instrumentation ql.hook_address(callback=start_afl, address=ba + 0x122c) # okay, ready to roll ql.run()
def run_one_round(payload): mock_stdin = pipe.SimpleInStream(sys.stdin.fileno()) ql = Qiling(["../examples/rootfs/x86_linux/bin/crackme_linux"], "../examples/rootfs/x86_linux", console=False, stdin=mock_stdin) ins_count = [0] ql.hook_code(instruction_count, ins_count) ql.set_syscall("_llseek", my__llseek) ql.os.stdin.write(payload) ql.run() del mock_stdin del ql return ins_count[0]
def main(input_file: str): ql = Qiling( ["./x8664_fuzz"], "../../rootfs/x8664_linux", verbose=QL_VERBOSE.OFF, # keep qiling logging off console=False) # thwart program output # redirect stdin to our mock to feed it with incoming fuzzed keystrokes ql.os.stdin = pipe.SimpleInStream(sys.stdin.fileno()) def place_input_callback(ql: Qiling, input: bytes, persistent_round: int) -> Optional[bool]: """Feed generated stimuli to the fuzzed target. This method is called with every fuzzing iteration. """ # feed fuzzed input to our mock stdin ql.os.stdin.write(input) # signal afl to proceed with this input return True def start_afl(ql: Qiling): """Have Unicorn fork and start instrumentation. """ afl.ql_afl_fuzz(ql, input_file=input_file, place_input_callback=place_input_callback, exits=[ql.os.exit_point]) # get image base address ba = ql.loader.images[0].base # make the process crash whenever __stack_chk_fail@plt is about to be called. # this way afl will count stack protection violations as crashes ql.hook_address(callback=lambda x: os.abort(), address=ba + 0x126e) # set afl instrumentation [re]starting point. we set it to 'main' ql.hook_address(callback=start_afl, address=ba + 0x1275) # okay, ready to roll ql.run()
def main(input_file: str): mock_stdin = pipe.SimpleInStream(sys.stdin.fileno()) ql = Qiling( ["./x8664_fuzz"], "../../rootfs/x8664_linux", verbose=QL_VERBOSE.OFF, # keep qiling logging off console=False, # thwart program output stdin= mock_stdin, # redirect stdin to our mock to feed it with incoming fuzzed keystrokes stdout=None, stderr=None) def place_input_callback(ql: Qiling, input: bytes, persistent_round: int) -> Optional[bool]: """Called with every newly generated input. """ ql.os.stdin.write(input) return True def start_afl(_ql: Qiling): """Callback from inside. """ ql_afl_fuzz(_ql, input_file=input_file, place_input_callback=place_input_callback, exits=[ql.os.exit_point]) # get image base address ba = ql.loader.images[0].base # make process crash whenever __stack_chk_fail@plt is about to be called. # this way afl will count stack protection violations as crashes ql.hook_address(callback=lambda x: os.abort(), address=ba + 0x1225) # set a hook on main() to let unicorn fork and start instrumentation ql.hook_address(callback=start_afl, address=ba + 0x122c) # okay, ready to roll ql.run()
def place_input(self, uc: Uc, data: ctypes.Array, ql: Qiling): ql.restore(self.snapshot) ql.os.stdin = pipe.SimpleInStream(1) ql.os.stdin.write(bytes(data)) return 1
def our_sandbox(path, rootfs): ql = Qiling(path, rootfs, stdin=pipe.SimpleInStream(sys.stdin.fileno())) ql.os.stdin.write(b"Ea5yR3versing\n") ql.hook_address(force_call_dialog_func, 0x00401016) ql.run()
def main(input_file, enable_trace=False): mock_stdin = pipe.SimpleInStream(sys.stdin.fileno()) ql = Qiling(["./arm_fuzz"], "../../rootfs/arm_qnx", stdin=mock_stdin, stdout=1 if enable_trace else None, stderr=1 if enable_trace else None, console=True if enable_trace else False) # or this for output: # ... stdout=sys.stdout, stderr=sys.stderr) def place_input_callback(uc, input, _, data): ql.os.stdin.write(input) def start_afl(_ql: Qiling): """ Callback from inside """ # We start our AFL forkserver or run once if AFL is not available. # This will only return after the fuzzing stopped. try: #print("Starting afl_fuzz().") if not _ql.uc.afl_fuzz(input_file=input_file, place_input_callback=place_input_callback, exits=[ql.os.exit_point]): print("Ran once without AFL attached.") os._exit(0) # that's a looot faster than tidying up. except unicornafl.UcAflError as ex: # This hook trigers more than once in this example. # If this is the exception cause, we don't care. # TODO: Chose a better hook position :) if ex != unicornafl.UC_AFL_RET_CALLED_TWICE: raise LIBC_BASE = int(ql.profile.get("OS32", "interp_address"), 16) # crash in case we reach SignalKill ql.hook_address(callback=lambda x: os.abort(), address=LIBC_BASE + 0x456d4) # Add hook at main() that will fork Unicorn and start instrumentation. main_addr = 0x08048aa0 ql.hook_address(callback=start_afl, address=main_addr) if enable_trace: # The following lines are only for `-t` debug output md = ql.create_disassembler() count = [0] def spaced_hex(data): return b' '.join( hexlify(data)[i:i + 2] for i in range(0, len(hexlify(data)), 2)).decode('utf-8') def disasm(count, ql, address, size): buf = ql.mem.read(address, size) try: for i in md.disasm(buf, address): return "{:08X}\t{:08X}: {:24s} {:10s} {:16s}".format( count[0], i.address, spaced_hex(buf), i.mnemonic, i.op_str) except: import traceback print(traceback.format_exc()) def trace_cb(ql, address, size, count): rtn = '{:100s}'.format(disasm(count, ql, address, size)) print(rtn) count[0] += 1 ql.hook_code(trace_cb, count) # okay, ready to roll. # try: ql.run() # except Exception as ex: # # Probable unicorn memory error. Treat as crash. # print(ex) # os.abort() os._exit(0) # that's a looot faster than tidying up.
def main(input_file, enable_trace=False): mock_stdin = pipe.SimpleInStream(sys.stdin.fileno()) ql = Qiling(["./arm_fuzz"], "../../rootfs/arm_qnx", stdin=mock_stdin, stdout=1 if enable_trace else None, stderr=1 if enable_trace else None, console = True if enable_trace else False) # or this for output: # ... stdout=sys.stdout, stderr=sys.stderr) def place_input_callback(ql: Qiling, input: bytes, _: int): ql.os.stdin.write(input) return True def start_afl(_ql: Qiling): ql_afl_fuzz(_ql, input_file=input_file, place_input_callback=place_input_callback, exits=[ql.os.exit_point]) LIBC_BASE = int(ql.profile.get("OS32", "interp_address"), 16) # crash in case we reach SignalKill ql.hook_address(callback=lambda x: os.abort(), address=LIBC_BASE + 0x38170) # Add hook at main() that will fork Unicorn and start instrumentation. main_addr = 0x08048aa0 ql.hook_address(callback=start_afl, address=main_addr) if enable_trace: # The following lines are only for `-t` debug output md = ql.create_disassembler() count = [0] def spaced_hex(data): return b' '.join(hexlify(data)[i:i+2] for i in range(0, len(hexlify(data)), 2)).decode('utf-8') def disasm(count, ql, address, size): buf = ql.mem.read(address, size) try: for i in md.disasm(buf, address): return "{:08X}\t{:08X}: {:24s} {:10s} {:16s}".format(count[0], i.address, spaced_hex(buf), i.mnemonic, i.op_str) except: import traceback print(traceback.format_exc()) def trace_cb(ql, address, size, count): rtn = '{:100s}'.format(disasm(count, ql, address, size)) print(rtn) count[0] += 1 ql.hook_code(trace_cb, count) # okay, ready to roll. # try: ql.run() # except Exception as ex: # # Probable unicorn memory error. Treat as crash. # print(ex) # os.abort() os._exit(0) # that's a looot faster than tidying up.