Esempio n. 1
0
def hook_TerminateProcess(ql: Qiling, address: int, params):
    # Samples will try to kill other process! We don't want to always stop!
    process = params["hProcess"]

    if process == ql.os.profile.getint(
            "KERNEL", "pid"):  # or process == ql.os.image_address:
        ql.emu_stop()
        ql.os.PE_RUN = False

    return 1
Esempio n. 2
0
    def test_x8664_uefi(self):
        def force_notify_RegisterProtocolNotify(ql: Qiling, address: int,
                                                params):
            ql.log.info(
                f'[force_notify] address = {address:#x}, params = {params}')

            self.ck.visited_oncall = True

            event_id = params['Event']

            if event_id in ql.loader.events:
                event = ql.loader.events[event_id]

                # let's force notify
                event["Set"] = False

                utils.signal_event(ql, event_id)
                utils.execute_protocol_notifications(ql, True)

                return EFI_SUCCESS

            return EFI_INVALID_PARAMETER

        def my_onenter(ql: Qiling, address: int, params):
            ql.log.info(
                f'[my_onenter] address = {address:#x}, params = {params}')

            self.ck.visited_onenter = True

        def my_onexit(ql: Qiling, address: int, params, retval: int):
            ql.log.info(
                f'[my_onexit] address = {address:#x}, params = {params}')

            self.ck.visited_onexit = True

        if __name__ == "__main__":
            with open(f'{ROOTFS_UEFI}/rom2_nvar.pickel', 'rb') as f:
                env = pickle.load(f)

            ql = Qiling([f'{ROOTFS_UEFI}/bin/TcgPlatformSetupPolicy'],
                        ROOTFS_UEFI,
                        env=env,
                        verbose=QL_VERBOSE.DEBUG)
            self.ck = Checklist()

            ql.set_api("RegisterProtocolNotify",
                       force_notify_RegisterProtocolNotify)
            ql.set_api("CopyMem", my_onenter, QL_INTERCEPT.ENTER)
            ql.set_api("LocateProtocol", my_onexit, QL_INTERCEPT.EXIT)

            ql.run()

            self.assertTrue(self.ck.visited_oncall)
            self.assertTrue(self.ck.visited_onenter)
            self.assertTrue(self.ck.visited_onexit)
Esempio n. 3
0
def sandbox(path: str, args: List[str], rootfs: str) -> None:
    options = {
        "filename": [path, *args],
        "rootfs": rootfs,
        "env": None,
        "shellcoder": None,
        "ostype": "Linux",
        "archtype": "arm_thumb",
        # "archtype": "arm",
        "bigendian": False,
        "output": "debug",
        "verbose": 1,
        "profile": None,
        "console": True,
        "log_dir": None,
        "log_split": None,
        "append": None,
        "libcache": False,
        "stdin": 0,
        "stdout": 0,
        "stderr": 0
    }
    ql = Qiling(**options)
    for syscall, hook in hooks.items():
        ql.set_syscall(syscall, syscall_hook(syscall))
    ql.hook_mem_invalid(hook_invalid_memory)
    ql.debugger = ":9999"
    ql.run()
Esempio n. 4
0
    def test_elf_linux_x86_getdents64(self):
        mock_stdout = pipe.SimpleOutStream(sys.stdout.fileno())
        ql = Qiling(["../examples/rootfs/x86_linux/bin/x86_getdents64"],
                    "../examples/rootfs/x86_linux",
                    verbose=QL_VERBOSE.DEBUG,
                    stdout=mock_stdout)

        ql.run()
        self.assertTrue("bin\n" in ql.os.stdout.read().decode("utf-8"))

        del ql
Esempio n. 5
0
    def test_elf_linux_mips32el_static(self):
        def random_generator(size=6,
                             chars=string.ascii_uppercase + string.digits):
            return ''.join(random.choice(chars) for x in range(size))

        ql = Qiling([
            "../examples/rootfs/mips32el_linux/bin/mips32el_hello_static",
            random_generator(random.randint(1, 99))
        ], "../examples/rootfs/mips32el_linux")
        ql.run()
        del ql
Esempio n. 6
0
def ql_syscall_prlimit64(ql: Qiling, pid: int, res: int, new_limit: int,
                         old_limit: int):
    # setrlimit() and getrlimit()
    if pid == 0 and new_limit == 0:
        rlim = resource.getrlimit(res)
        ql.mem.write(old_limit, ql.packs(rlim[0]) + ql.packs(rlim[1]))

        return 0

    # set other process which pid != 0
    return -1
Esempio n. 7
0
def hook___p___argv(ql: Qiling, address: int, params):
    ret = ql.os.heap.alloc(ql.pointersize)
    argv_addr = ql.os.heap.alloc(ql.pointersize * len(ql.os.argv))
    count = 0
    for each in ql.os.argv:
        argv = ql.os.heap.alloc(len(each) + 1)
        ql.mem.write(argv, bytes(each, 'ascii') + b'\x00')
        ql.mem.write(argv_addr + count * ql.pointersize, ql.pack(argv))
        count += 1
    ql.mem.write(ret, ql.pack(argv_addr))
    return ret
Esempio n. 8
0
def sanitized_emulate(path,
                      rootfs,
                      fault_type,
                      output="debug",
                      enable_trace=False):
    ql = Qiling([path], rootfs, output=output)
    ql.env['FaultType'] = fault_type
    enable_sanitized_heap(ql)
    ql.run()
    if not ql.os.heap.validate():
        my_abort("Canary corruption detected")
Esempio n. 9
0
    def test_x8664_getcwd(self):
        mock_stdout = pipe.SimpleOutStream(sys.stdout.fileno())
        ql = Qiling(["../examples/rootfs/x8664_linux/bin/testcwd"],
                    "../examples/rootfs/x8664_linux",
                    verbose=QL_VERBOSE.DEBUG,
                    stdout=mock_stdout)

        ql.run()
        self.assertEqual(ql.os.stdout.read(), b'/\n/lib\n/bin\n/\n')

        del ql
Esempio n. 10
0
    def test_elf_linux_execve_x8664(self):
        ql = Qiling(["../examples/rootfs/x8664_linux/bin/posix_syscall_execve"],  "../examples/rootfs/x8664_linux", verbose=QL_VERBOSE.DEBUG)   
        ql.run()

        for key, value in ql.loader.env.items():
            QL_TEST=value

        self.assertEqual("TEST_QUERY", QL_TEST)
        self.assertEqual("child", ql.loader.argv[0])

        del QL_TEST
        del ql
Esempio n. 11
0
def hook___p__environ(ql: Qiling, address: int, params):
    ret = ql.os.heap.alloc(ql.pointersize * len(ql.os.env))
    count = 0
    for key in ql.os.env:
        pointer = ql.os.heap.alloc(ql.pointersize)
        env = key + "=" + ql.os.env[key]
        env_addr = ql.os.heap.alloc(len(env) + 1)
        ql.mem.write(env_addr, bytes(env, 'ascii') + b'\x00')
        ql.mem.write(pointer, ql.pack(env_addr))
        ql.mem.write(ret + count * ql.pointersize, ql.pack(pointer))
        count += 1
    return ret
Esempio n. 12
0
 def test_pe_win_x8664_cmdln(self):
     ql = Qiling(
     ["../examples/rootfs/x8664_windows/bin/cmdln64.exe", 'arg1', 'arg2 with spaces'],
     "../examples/rootfs/x8664_windows")
     ql.os.stdout = TestOut()
     ql.run()
     expected_string = b'<C:\\Users\\Qiling\\Desktop\\cmdln64.exe arg1 "arg2 with spaces">\n'
     expected_keys = [b'_acmdln', b'_wcmdln', b'GetCommandLineA', b'GetCommandLineW']
     for key in expected_keys:
         self.assertTrue(key in ql.os.stdout.output)
         self.assertEqual(expected_string, ql.os.stdout.output[key])
     del ql
Esempio n. 13
0
    def test_elf_linux_x8664_path_traversion(self):
        mock_stdout = pipe.SimpleOutStream(sys.stdout.fileno())
        ql = Qiling(
            ["../examples/rootfs/x8664_linux/bin/path_traverse_static"],
            "../examples/rootfs/x8664_linux",
            verbose=QL_VERBOSE.DEBUG,
            stdout=mock_stdout)

        ql.run()
        self.assertTrue("root\n" not in ql.os.stdout.read().decode("utf-8"))

        del ql
Esempio n. 14
0
        def sanitized_emulate(path,
                              rootfs,
                              fault_type,
                              verbose=QL_VERBOSE.DEBUG,
                              enable_trace=False):
            ql = Qiling([path], rootfs, verbose=verbose)
            ql.env['FaultType'] = fault_type
            enable_sanitized_heap(ql)
            ql.run()

            if not ql.os.heap.validate():
                my_abort("Canary corruption detected")
Esempio n. 15
0
    def test_x8664_absolute_path(self):
        mock_stdout = pipe.SimpleOutStream(sys.stdout.fileno())
        ql = Qiling(["../examples/rootfs/x8664_linux/bin/absolutepath"],
                    "../examples/rootfs/x8664_linux",
                    verbose=QL_VERBOSE.DEBUG,
                    stdout=mock_stdout)

        ql.run()
        self.assertEqual(ql.os.stdout.read(),
                         b'test_complete\n\ntest_complete\n\n')

        del ql
Esempio n. 16
0
def __getrlimit_common(ql: Qiling, res: int, rlim: int) -> int:
    RLIMIT_STACK = 3
    if res == RLIMIT_STACK:
        if ql.arch.bits == 64:
            stack_size = int(ql.os.profile.get("OS64", "stack_size"), 16)
        elif ql.arch.bits == 32:
            stack_size = int(ql.os.profile.get("OS32", "stack_size"), 16)
        rlimit = (stack_size, -1)
    else:
        rlimit = resource.getrlimit(res)
    ql.mem.write(rlim, ql.pack64s(rlimit[0]) + ql.pack64s(rlimit[1]))
    return 0
Esempio n. 17
0
    def test_elf_linux_arm(self):     
        def my_puts(ql):
            params = ql.os.resolve_fcall_params(ELFTest.PARAMS_PUTS)
            print(f'puts("{params["s"]}")')

            all_mem = ql.mem.save()
            ql.mem.restore(all_mem)

        ql = Qiling(["../examples/rootfs/arm_linux/bin/arm_hello"], "../examples/rootfs/arm_linux", verbose=QL_VERBOSE.DEBUG, profile='profiles/append_test.ql')
        ql.os.set_api('puts', my_puts)
        ql.run()
        del ql
Esempio n. 18
0
def ql_qnx_msg_sys_conf(ql:Qiling, coid, smsg, sparts, rmsg, rparts, *args, **kw):
    # struct _sys_conf in services/system/public/sys/sysmsg.h
    (type, subtype, cmd, name, spare, value) = unpack("<HHiiiq", get_message_body(ql, smsg, sparts))
    # check parameters
    assert (c_int32(sparts).value) == (-24), "input size is wrong"
    assert (type) == (0x000)

    if not subtype in sysconf_subtypes:
        raise NotImplementedError(f'subtype {subtype} not implemented')
    
    if not cmd in sysconf_conditions:
        raise NotImplementedError(f'cmd type {cmd} not implemented')
    
    # sys_conf(_SYS_SUB_GET, _CONF_STR, *) in lib/c/1a/confstr.c
    if subtype == 0 and cmd == (1 << 20):
        # check parameters
        assert (c_int32(rparts).value) == (2), "output size is wrong"
        if not name in sysconf_names:
            raise NotImplementedError(f'name type {name} not implemented')
        ql.log.debug(f'msg_sys_conf(subtype = {sysconf_subtypes[subtype]}, cmd = {sysconf_conditions[cmd]}, name = {sysconf_names[name]}, spare = {spare}, value = {value})')
        # get string
        if name == 200: # == _CS_LIBPATH
            retstr = "/usr/lib\0"
        elif name == 203: # == _CS_TIMEZONE
            retstr = "UTC\0"
        else:
            raise NotImplementedError("sys_conf name not implemented")
        # first iov_t
        iov_base = ql.unpack32(ql.mem.read(rmsg, 4))
        iov_len = ql.unpack32(ql.mem.read(rmsg + 4, 4))
        ql.mem.write(iov_base, pack("<IIIiq", 0, 0, 0, 0, len(retstr)))
        if value != 0:
            # second iov_t
            iov_base = ql.unpack32(ql.mem.read(rmsg + 8, 4))
            iov_len = ql.unpack32(ql.mem.read(rmsg + 12, 4))
            ql.mem.write(iov_base, retstr.encode("utf-8"))
    # sys_conf(_SYS_SUB_GET, _CONF_NUM, *) in lib/c/1/sysconf.c
    elif subtype == 0 and cmd == (2 << 20):
        # check parameters
        assert (c_int32(rparts).value) == (-24), "output size is wrong"
        if not name in sysconf_consts:
            raise NotImplementedError(f'name type {name} not implemented')
        ql.log.debug(f'msg_sys_conf(subtype = {sysconf_subtypes[subtype]}, cmd = {sysconf_conditions[cmd]}, name = {sysconf_consts[name]}, spare = {spare}, value = {value})')
        # get value
        if name == 11: # == _SC_PAGESIZE
            retval = PAGESIZE
        else:
            raise NotImplementedError("sys_conf name not implemented")
        # struct _sys_conf_reply in services/system/public/sys/sysmsg.h
        ql.mem.write(rmsg, pack("<IIIiq", 0, 0, 0, 0, retval))
    else:
        raise NotImplementedError("sys_conf message not implemented")
    return 0
Esempio n. 19
0
def enable_history_trace(ql: Qiling, nrecords: int):
	"""Enable instruction-level tracing in history mode.

	To allow faster execution, the trace info collected throughout program execution is not
	emitted and undergo as minimal post-processing as possible. When program crahses, the
	last `nrecords` trace lines are shown.

	Args:
		ql: qiling instance
		nrecords: number of last records to show
	"""

	# enable detailed disassembly info
	md = ql.create_disassembler()
	md.detail = True

	# if available, use symbols map to resolve memory accesses
	symsmap = getattr(ql.loader, 'symsmap', {})

	# wrap the trace records list to allow it to be passed and modified by-ref
	history: UserList[TraceRecord] = UserList()

	def __trace_hook(ql: Qiling, address: int, size: int):
		"""[internal] Trace hook callback.
		"""

		recent = list(__get_trace_records(ql, address, size, md))

		history.data = (history + recent)[-nrecords:]

	ql.hook_code(__trace_hook)

	# replace the emulation error handler with our own so we can emit the trace
	# records when program crashes. before we do that, we save the original one
	# so we can call it.

	orig_emu_error = ql.os.emu_error

	def __emu_error(*args):
		# first run the original emulation error handler
		orig_emu_error(*args)

		# then parse and emit the trace info we collected
		ql.log.error(f'History:')
		for record in history:
			line = __to_trace_line(record, symsmap)

			ql.log.error(line)

		ql.log.error(f'')

	ql.os.emu_error = __emu_error
Esempio n. 20
0
            def our_sandbox(path, rootfs):
                ql = Qiling(path, rootfs)
                ql.patch(0x004010B5, b'\x90\x90')
                ql.patch(0x004010CD, b'\x90\x90')
                ql.patch(0x0040110B, b'\x90\x90')
                ql.patch(0x00401112, b'\x90\x90')

                ql.os.stdin = pipe.SimpleStringBuffer()
                ql.os.stdin.write(b"Ea5yR3versing\n")

                ql.hook_address(force_call_dialog_func, 0x00401016)
                ql.run()
                del ql
Esempio n. 21
0
def ql_syscall_rt_sigaction(ql: Qiling, signum: int, act: int, oldact: int):
    if oldact:
        if ql.os.sigaction_act[signum] == 0:
            data = b'\x00' * 20
        else:
            data = b''.join(ql.pack32(key) for key in ql.os.sigaction_act[signum])

        ql.mem.write(oldact, data)

    if act:
        ql.os.sigaction_act[signum] = [ql.unpack32(ql.mem.read(act + 4 * key, 4)) for key in range(5)]

    return 0
Esempio n. 22
0
    def __init__(self, ql: Qiling, data: bytes) -> None:
        assert data[0:2] == b'MZ'

        nbytes = ql.unpack16(
            data[2:4]
        ) or 0x200  # number of bytes in last block; 0 means it is fully populated
        nblocks = ql.unpack16(data[4:6])  # number of blocks used
        self.size = (nblocks - 1) * 0x200 + nbytes

        self.init_ss = ql.unpack16(data[14:16])
        self.init_sp = ql.unpack16(data[16:18])
        self.init_ip = ql.unpack16(data[20:22])
        self.init_cs = ql.unpack16(data[22:24])
Esempio n. 23
0
def enable_history_trace(ql: Qiling, nrecords: int):
    """Enable instruction-level tracing in history mode.

	To allow faster execution, the trace info collected throughout program execution is not
	emitted and undergo as minimal post-processing as possible. When program crahses, the
	last `nrecords` trace lines are shown.

	Args:
		ql: qiling instance
		nrecords: number of last records to show
	"""

    # enable detailed disassembly info
    md = ql.create_disassembler()
    md.detail = True

    assert md.arch == CS_ARCH_X86, 'currently available only for intel architecture'

    # if available, use symbols map to resolve memory accesses
    symsmap = getattr(ql.loader, 'symsmap', {})

    history: Deque[TraceRecord] = deque(maxlen=nrecords)

    def __trace_hook(ql: Qiling, address: int, size: int):
        """[internal] Trace hook callback.
		"""

        history.extend(__get_trace_records(ql, address, size, md))

    ql.hook_code(__trace_hook)

    # replace the emulation error handler with our own so we can emit the trace
    # records when program crashes. before we do that, we save the original one
    # so we can call it.

    orig_emu_error = ql.os.emu_error

    def __emu_error(*args):
        # first run the original emulation error handler
        orig_emu_error(*args)

        # then parse and emit the trace info we collected
        ql.log.error(f'History:')
        for record in history:
            line = __to_trace_line(record, symsmap)

            ql.log.error(line)

        ql.log.error(f'')

    ql.os.emu_error = __emu_error
Esempio n. 24
0
        def _t():
            if 'QL_FAST_TEST' in os.environ:
                return
            ql = Qiling(["../examples/rootfs/x86_windows/bin/al-khaser.bin"],
                        "../examples/rootfs/x86_windows")

            # The hooks are to remove the prints to file. It crashes. will debug why in the future
            def results(ql):

                if ql.reg.ebx == 1:
                    print("BAD")
                else:
                    print("GOOD ")
                ql.reg.eip = 0x402ee4

            #ql.hook_address(results, 0x00402e66)
            # the program alloc 4 bytes and then tries to write 0x2cc bytes.
            # I have no idea of why this code should work without this patch
            ql.patch(0x00401984, b'\xb8\x04\x00\x00\x00')

            def end(ql):
                print("We are finally done")
                ql.emu_stop()

            ql.hook_address(end, 0x004016ae)

            ql.run()
            del ql
            return True
Esempio n. 25
0
def ql_syscall_lseek(ql: Qiling, fd: int, offset: int, lseek_origin: int):
    if 0 <= fd < NR_OPEN and ql.os.fd[fd] != 0:
        offset = ql.unpacks(ql.pack(offset))

        try:
            regreturn = ql.os.fd[fd].seek(offset, lseek_origin)
        except OSError:
            regreturn = -1
    else:
        regreturn = -EBADF

    # ql.log.debug("lseek(fd = %d, ofset = 0x%x, origin = 0x%x) = %d" % (lseek_fd, lseek_ofset, lseek_origin, regreturn))

    return regreturn
Esempio n. 26
0
    def test_elf_hijackapi_linux_x8664(self):
        def my_puts_enter(ql: Qiling):
            params = ql.os.resolve_fcall_params(ELFTest.PARAMS_PUTS)
            self.test_enter_str = params["s"]

        def my_puts_exit(ql):
            self.test_exit_rdi = ql.reg.rdi

        ql = Qiling(["../examples/rootfs/x8664_linux/bin/x8664_puts"],
                    "../examples/rootfs/x8664_linux",
                    verbose=QL_VERBOSE.DEBUG)
        ql.set_api('puts', my_puts_enter, QL_INTERCEPT.ENTER)
        ql.set_api('puts', my_puts_exit, QL_INTERCEPT.EXIT)

        ql.run()

        if self.test_exit_rdi == 140736282240864:
            self.test_exit_rdi = 0x1

        self.assertEqual(0x1, self.test_exit_rdi)
        self.assertEqual("CCCC", self.test_enter_str)

        del self.test_exit_rdi
        del self.test_enter_str
        del ql
Esempio n. 27
0
    def test_elf_linux_x8664(self):
        def my_puts(ql: Qiling):
            params = ql.os.resolve_fcall_params(ELFTest.PARAMS_PUTS)
            print(f'puts("{params["s"]}")')
            reg = ql.reg.read("rax")
            print("reg : %#x" % reg)
            self.set_api = reg

        def write_onEnter(ql: Qiling, fd: int, str_ptr: int, str_len: int,
                          *args):
            self.set_api_onenter = True
            print("enter write syscall!")

            # override syscall pc (ignored) and set of params with our own
            return None, (fd, str_ptr + 1, str_len - 1)

        def write_onexit(ql: Qiling, fd: int, str_ptr: int, str_len: int,
                         retval: int, *args):
            self.set_api_onexit = True
            print("exit write syscall!")

            # override syscall return value with our own
            return str_len + 1

        ql = Qiling([
            "../examples/rootfs/x8664_linux/bin/x8664_args", "1234test",
            "12345678", "bin/x8664_hello"
        ],
                    "../examples/rootfs/x8664_linux",
                    verbose=QL_VERBOSE.DEBUG)
        ql.set_syscall(1, write_onEnter, QL_INTERCEPT.ENTER)
        ql.set_api('puts', my_puts)
        ql.set_syscall(1, write_onexit, QL_INTERCEPT.EXIT)
        ql.mem.map(0x1000, 0x1000)
        ql.mem.write(0x1000, b"\xFF\xFE\xFD\xFC\xFB\xFA\xFB\xFC\xFC\xFE\xFD")
        ql.mem.map(0x2000, 0x1000)
        ql.mem.write(0x2000, b"\xFF\xFE\xFD\xFC\xFB\xFA\xFB\xFC\xFC\xFE\xFD")
        ql.run()

        self.assertEqual(
            [0x1000, 0x2000],
            ql.mem.search(b"\xFF\xFE\xFD\xFC\xFB\xFA\xFB\xFC\xFC\xFE\xFD"))
        self.assertEqual(0x5555555546ca, self.set_api)
        self.assertEqual(True, self.set_api_onexit)
        self.assertEqual(True, self.set_api_onenter)

        del self.set_api
        del self.set_api_onexit
        del self.set_api_onenter
        del ql
Esempio n. 28
0
def __leaf_53(ql: Qiling):
    al = ql.arch.regs.al

    if al == 0x01:
        ql.os.clear_cf()
    elif al == 0x0e:
        ql.arch.regs.ax = 0x0102
        ql.os.clear_cf()
    elif al == 0x07:
        if (ql.arch.regs.bx == 1) and (ql.arch.regs.cx == 3):
            ql.log.info("Emulation Stop")
            ql.emu_stop()
    else:
        raise NotImplementedError()
Esempio n. 29
0
    def test_x86_fake_urandom(self):
        class Fake_urandom(QlFsMappedObject):

            def read(self, size):
                return b"\x01"

            def fstat(self):
                return -1
            
            def close(self):
                return 0

        ql = Qiling(["../examples/rootfs/x86_linux/bin/x86_fetch_urandom"],  "../examples/rootfs/x86_linux", verbose=QL_VERBOSE.DEBUG)
        ql.add_fs_mapper("/dev/urandom", Fake_urandom())

        ql.exit_code = 0
        ql.exit_group_code = 0

        def check_exit_group_code(ql, exit_code, *args, **kw):
            ql.exit_group_code = exit_code

        def check_exit_code(ql, exit_code, *args, **kw):
            ql.exit_code = exit_code            

        ql.os.set_syscall("exit_group", check_exit_group_code, QL_INTERCEPT.ENTER)
        ql.os.set_syscall("exit", check_exit_code, QL_INTERCEPT.ENTER)

        ql.run()
        self.assertEqual(0, ql.exit_code)
        self.assertEqual(0, ql.exit_group_code)        
        del ql
Esempio n. 30
0
    def test_gdbdebug_mips32(self):
        ql = Qiling(["../examples/rootfs/mips32_linux/bin/mips32_hello"],
                    "../examples/rootfs/mips32_linux",
                    verbose=QL_VERBOSE.DEBUG)
        ql.debugger = True

        # some random command test just to make sure we covered most of the command
        def gdb_test_client():
            # yield to allow ql to launch its gdbserver
            time.sleep(1.337 * 2)

            with SimpleGdbClient('127.0.0.1', 9999) as client:
                client.send(
                    'qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;xmlRegisters=i386'
                )
                client.send('vMustReplyEmpty')
                client.send('QStartNoAckMode')
                client.send('Hgp0.0')
                client.send('qXfer:auxv:read::0, 1000')
                client.send('?')
                client.send('qXfer:threads:read::0,fff')
                client.send(f'qAttached:{ql.os.pid}')
                client.send('qC')
                client.send('g')
                client.send('m47ccd10,4')
                client.send('qXfer:threads:read::0,1000')
                client.send('m56555620,4')
                client.send('m5655561c,4')
                client.send('m56555620,4')
                client.send('m5655561c,4')
                client.send('m56555620,4')
                client.send('qTStatus')
                client.send('qTfP')
                client.send('m56555600,40')
                client.send('m56555620,4')
                client.send('Z0,47ccd10,4')
                client.send(
                    'QPassSignals:e;10;14;17;1a;1b;1c;21;24;25;2c;4c;97;')
                client.send('vCont?')
                client.send('vCont;c:pa410.-1')
                client.send('c')
                client.send('k')

                # yield to make sure ql gdbserver has enough time to receive our last command
                time.sleep(1.337)

        threading.Thread(target=gdb_test_client, daemon=True).start()

        ql.run()
        del ql