예제 #1
0
    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)
예제 #2
0
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()
예제 #3
0
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()
예제 #4
0
 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]
예제 #5
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()
예제 #6
0
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()
예제 #7
0
 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
예제 #8
0
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()
예제 #9
0
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.
예제 #10
0
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.