☰
Qiling 框架架构全解析:分层设计、执行流程、Hook 系统与混合内核扩展
2026/10/15 1:52:41 网站建设 项目流程
  • 逆向工程
  • 网络安全
  • 虚拟化
  • 物联网

【免费下载链接】qiling

A True Instrumentable Binary Emulation Framework

项目地址:https://gitcode.com/gh_mirrors/qi/qiling
点击查看免费下载

导读

本文围绕开源仓库根目录下的 ARCHITECTURE.md 展开,系统讲解 Qiling(A True Instrumentable Binary Emulation Framework)的分层架构、核心组件、从初始化到执行的完整运行流程、Hook 拦截体系,以及面向未来演进的"混合内核架构(Hybrid Kernel Architecture)"扩展方案。读完本文,你将能理解 Qiling 如何在 Unicorn 裸 CPU 模拟之上叠加 OS 抽象层,掌握Qiling主入口类的关键属性与用法、select_arch/select_os等运行时组件选择机制,并能够借助 KernelProxy 将缺失的 Linux 系统调用转发给真实内核执行。

一、总体架构:从 Unicorn 到完整 OS 模拟

Qiling 构建在 Unicorn CPU 模拟引擎之上,通过增加系统级抽象(系统调用、虚拟文件系统、二进制加载器),实现"无需真实硬件即可完整执行二进制程序"。其分层结构自上而下为:

┌──────────────────────────────────────────────────┐ │ User Script / qltool │ ├──────────────────────────────────────────────────┤ │ Qiling Core (core.py) │ │ hooks · state snapshots · patches │ ├────────────┬─────────────┬───────────────────────┤ │ OS Layer │ Loader │ Memory Manager │ │ (QlOs) │ (QlLoader) │ (QlMemoryManager) │ │ syscalls │ ELF/PE/ │ map · read · write │ │ APIs │ MachO/etc │ MMIO callbacks │ ├────────────┴─────────────┴───────────────────────┤ │ Architecture Layer (QlArch) │ │ registers · disassembly · calling conventions │ ├──────────────────────────────────────────────────┤ │ Unicorn Engine (CPU) │ │ instruction-level emulation │ └──────────────────────────────────────────────────┘

各层的职责可以概括为:

  • 用户脚本 / qltool:使用 qltool 命令行或 Python 脚本驱动模拟会话;
  • Qiling Core:由 qiling/core.py 中的Qiling类承担,负责装配所有组件、管理 hook、状态快照与二进制补丁;
  • OS Layer / Loader / Memory Manager:对应QlOs、QlLoader、QlMemoryManager,分别处理系统调用与 API、二进制格式解析、内存映射与读写;
  • Architecture Layer:QlArch提供寄存器访问、反汇编(Capstone)与调用约定;
  • Unicorn Engine:提供最底层的指令级模拟能力。

二、仓库目录结构

qiling/包按关注点划分模块,与架构分层一一对应:

  • qiling/core.py:Qiling类——主入口与编排器;core_hooks.py 与 core_hooks_types.py 实现 hook 系统;core_struct.py 提供字节序感知的 struct 打包工具;
  • qiling/const.py:枚举定义(QL_ARCH、QL_OS、QL_ENDIAN等);exception.py 为自定义异常体系;utils.py 提供select_arch/select_os等组件选择函数;host.py 抽象宿主机平台;
  • qiling/arch/:架构实现(x86/x86-64/8086、ARMv7、ARMv8、MIPS32、RISC-V 32/64、PowerPC 32、Cortex-M、寄存器管理register.py、CPU 模型models.py);
  • qiling/os/:操作系统实现,包含抽象基类 os.py、内存管理器 memory.py、函数调用接口 fcall.py、syscall/API 映射 mapper.py、虚拟路径解析 path.py、文件描述符抽象 filestruct.py、线程原语 thread.py,以及 posix/linux/freebsd/macos/qnx/windows/uefi/dos/mcu/blob 各具体 OS 子目录;
  • qiling/loader/:二进制格式加载器(ELF、PE、PE-UEFI、Mach-O、DOS COM/EXE、MCU 固件、裸 blob);
  • qiling/cc/:各架构调用约定(intel 的 cdecl/stdcall/ms64、arm 的 aarch32/aarch64、mips o32、riscv ABI、ppc ABI);
  • qiling/hw/:MCU 外设模拟(GPIO、定时器、UART、SPI、I2C、网络、ADC/DAC、中断控制器、Flash、DMA 等);
  • qiling/debugger/:调试子系统,含 gdb/(GDB 远程协议服务器)与 qdb/(Qiling 原生调试器,支持逆向调试);
  • qiling/extensions/:可选扩展(multitask.py 基于 gevent 的多线程、trace.py 指令跟踪、sanitizers/ 内存消毒器、afl/ AFL 模糊测试集成、r2/ Radare2 集成、idaplugin/ IDA Pro 插件);
  • qiling/profiles/:默认 OS 配置文件(.ql格式,如 linux.ql 定义栈/堆地址、内核参数)。

仓库根目录还包含 CLI 工具 qltool、TUI 界面 qltui.py、examples/ 示例脚本与 tests/ 测试套件。

三、核心组件剖析

3.1Qiling主类(core.py)

每个模拟会话都会创建一个Qiling实例,它拥有并串联所有其他组件。典型构造方式:

ql = Qiling( argv=["/path/to/binary", "arg1"], # 二进制路径 + 参数 rootfs="/path/to/rootfs", # 虚拟文件系统根目录 ostype=QL_OS.LINUX, # 目标 OS(省略时自动探测) archtype=QL_ARCH.X8664, # 目标架构(省略时自动探测) )

结合 core.py 源码,Qiling继承自QlCoreHooks(hook 管理)与QlCoreStructs(字节序感知的 struct),完整构造参数还包括env(环境变量)、code(shellcode 字节,与argv互斥)、cputype(CPU 型号)、verbose(日志级别)、profile(配置文件路径或映射)、console、log_devices、multithread、filter、stop、endian、thumb、libcache等。其中code与argv互斥这一约束在源码中有显式校验(core.py)。

Qiling的关键属性如下表:

Property类型说明
ql.archQlArchCPU 架构——寄存器、反汇编
ql.osQlOs操作系统——系统调用、I/O、API
ql.loaderQlLoader二进制加载器——解析并映射可执行文件
ql.memQlMemoryManager内存管理——map/read/write/MMIO
ql.ucunicorn.Uc底层 Unicorn 引擎实例

此外还有ql.path(argv 中指定的二进制路径)、ql.rootfs(虚拟根目录,模拟程序被限制在其中的路径,源码提示 除非完全信任模拟程序否则不要使用宿主机根目录)、ql.profile(ConfigParser对象,qiling/profiles/*.ql)、ql.hw(裸机硬件管理器,仅 baremetal 模式可用)等属性。

3.2 架构层(qiling/arch/)

QlArch是抽象基类,每个架构子类负责配置:

  • Unicorn 引擎的 mode 与架构常量;
  • 通过ql.arch.regs按名称读写寄存器;
  • 反汇编器(Capstone)与汇编器(Keystone);
  • 栈操作(push/pop,指针宽度感知);
  • 字节序与位宽(16/32/64)。

已支持架构:x86、x86-64、8086、ARM、ARM64、MIPS、RISC-V(32/64)、PowerPC、Cortex-M。ARM 额外支持 endian 与 Thumb 模式参数(见 utils.py select_arch 中为 ARM 传入endian/thumb、为 MIPS 传入endian的处理)。

3.3 OS 层(qiling/os/)

QlOs是抽象基类,每个 OS 子类提供:

  • 系统调用/中断分发:将 CPU 中断路由到处理函数;
  • I/O 流:stdin、stdout、stderr(可拦截替换);
  • 虚拟文件系统:通过rootfs进行路径映射;
  • 函数调用接口:ql.os.fcall,读取参数、设置返回值;
  • API 拦截:set_api()用于 hook 库函数。

POSIX 子系统(qiling/os/posix/):Linux、FreeBSD、macOS、QNX 共享的 syscall 实现,具体处理器位于os/posix/syscall/下,另含kernel_proxy/(混合内核代理,见后文)。

Windows(qiling/os/windows/):通过 hook DLL 导入表模拟 Win32/Win64 API,支持注册表(registry.py)、线程(thread.py)、句柄(handle.py)、纤程(fiber.py),以及大量 DLL 级 API 实现(ntdll.py、user32.py、kernel32、advapi32.py等)。

UEFI(qiling/os/uefi/):模拟 UEFI Boot Services(bs.py)、Runtime Services(rt.py)与 SMM(smm.py),使用 GUID 数据库与协议框架。

3.4 加载器层(qiling/loader/)

QlLoader是抽象基类。加载器负责解析二进制格式、将段映射进内存、解析符号、加载依赖、设置初始 CPU 状态(PC、SP)。加载器与格式/使用方的对应关系:

Loader格式使用方
QlLoaderELFELFLinux、FreeBSD、QNX
QlLoaderPEPE/COFFWindows、UEFI
QlLoaderMachoMach-OmacOS
QlLoaderDOSCOM/EXEDOS
QlLoaderMCUFirmwareCortex-M MCU
QlLoaderBlobRaw bytesShellcode / blob

3.5 内存管理器(qiling/os/memory.py)

QlMemoryManager在 Unicorn 内存模型之上封装了更高级的操作(见 memory.py 定义清单):

  • map(addr, size, perms)/unmap(addr, size)——区域管理;
  • read(addr, size)/write(addr, data)——数据访问;
  • read_ptr(addr)/write_ptr(addr, val)——指针宽度感知的读写;
  • read_cstring(addr)——读取以\0结尾的字符串;
  • map_mmio(addr, size, handler)——内存映射外设的 MMIO 回调支持;
  • 此外还有search(按字节串/正则搜索内存)、find_free_space/map_anywhere(空闲区域查找与映射)、save/restore(内存状态快照)、get_mapinfo等实用能力。

3.6 调用约定(qiling/cc/)

每种架构都有调用约定类,抽象参数传递与返回值处理。QlOs.fcall接口基于这些类,提供与平台无关的统一方式读取函数参数(fcall.py 提供readParams/writeParams/call/call_native等能力)。

四、执行流程

4.1 初始化(Qiling.__init__)

Qiling(argv, rootfs) │ ├─ Detect arch/OS from binary headers (ql_guess_emu_env) │ ELF magic → parse e_machine, OSABI │ PE magic → parse Machine, Subsystem │ MachO magic → parse CPU type │ ├─ Create QlArch (select_arch) → initializes Unicorn engine ├─ Create QlLoader (select_loader) ├─ Create QlMemoryManager ├─ Create QlOs (select_os) │ └─ loader.run() ├─ Parse binary format (headers, segments, sections) ├─ Map segments into memory ├─ Load shared libraries / DLLs ├─ Setup stack, heap, TLS, auxiliary vectors └─ Set initial PC (entry point) and SP

源码印证(core.py):若未显式提供archtype,则调用ql_guess_emu_env(self.path)根据文件魔数自动猜测架构与 OS——ELF 解析e_machine/OSABI,PE 解析Machine/Subsystem,Mach-O 解析 CPU 类型;若已给架构但未给 OS,则通过arch_os_convert由架构推断 OS;随后依次创建QlArch、QlLoader、QlMemoryManager、QlOs,最后调用self.loader.run()完成加载并调用_init_stop_guard()写入退出守卫。

4.2 执行(ql.run())

ql.run(begin, end, timeout, count) │ ├─ Apply binary patches (ql.patch) ├─ Write exit trap (guard address) │ └─ os.run() └─ uc.emu_start(entry_point, exit_point) │ ├─ Unicorn executes instructions │ ├─ Hooks fire on: │ ├─ Every instruction (hook_code) │ ├─ Basic blocks (hook_block) │ ├─ Memory access (hook_mem_read/write) │ ├─ Interrupts (hook_intno) → syscall dispatch │ ├─ Specific addresses (hook_address) │ └─ Specific instructions (hook_insn) │ └─ Stops when PC reaches exit point, timeout, ql.emu_stop(), or unhandled exception

run() 源码 展示了四个参数的语义:begin/end指定模拟起始/结束地址,timeout以微秒为单位的时长上限(默认不限),count为指令数上限(默认不限)。执行前会先初始化调试器(若设置)、应用二进制补丁(do_bin_patch)、写入退出陷阱(write_exit_trap),随后调用self.os.run()进入uc.emu_start。

4.3 系统调用处理

当被模拟程序发出系统调用(int 0x80、syscall、svc等)时:

CPU interrupt/instruction → Unicorn interrupt hook → QlOs syscall dispatcher → Look up handler by syscall number → Handler reads args via calling convention → Emulates syscall behavior → Sets return value in registers

具体到 posix.py 的 load_syscall:先通过syscall_abi.get_id()读取系统调用号,经syscall_mapper映射为名称;再在posix_syscall_hooks三个拦截点(QL_INTERCEPT.ENTER/EXIT/CALL)中查找用户注册的 hook,未注册则回落到 OS 专属或 POSIX 共享的syscall模块中查找同名处理函数;最终通过syscall_abi.get_params(...)按调用约定读取参数、执行处理器、以set_return_value(retval)写回返回值。

五、组件动态选择机制

组件在运行时依据QL_ARCH与QL_OS枚举动态选择,qiling/utils.py 提供:

  • select_arch(archtype, cputype, endian, thumb)→ 架构类(源码);
  • select_os(ostype)→ OS 类(源码);
  • select_loader(ostype, libcache)→ 加载器类(源码);
  • select_debugger(options)→ 调试器类(源码,支持gdb与qdb两种调试器及其参数解析)。

这套机制使统一代码库得以支持多样的平台组合:例如 Linux/FreeBSD/QNX 共用 ELF 加载器,Windows 与 UEFI 共用 PE 加载器,而select_arch内部按架构映射到QlArchX86/QlArchARM/QlArchMips等具体类。arch_convert/os_convert还提供了别名转换(如amd64→x8664、darwin→macos)。

六、Hook 系统

core_hooks.py 对 Unicorn 回调机制进行了封装(包括UC_HOOK_CODE、UC_HOOK_BLOCK、UC_HOOK_INTR、UC_HOOK_INSN、UC_HOOK_MEM_*等底层常量,见 源码导入段)。Hook 类型与触发时机:

Hook 类型触发时机
hook_code每条指令(可限定地址范围)
hook_block每个基本块入口
hook_address到达特定地址
hook_intnoCPU 中断/异常
hook_insn特定指令类型(如syscall)
hook_mem_read内存读
hook_mem_write内存写
hook_mem_invalid非法内存访问

Hook 可限定地址范围,并可通过返回带QL_HOOK_BLOCK掩码的整数来阻断后续 hook 链的执行(见回调协议注释 core_hooks.py)。

七、关键扩展点

  • 自定义系统调用——通过ql.os.set_syscall(name_or_num, handler, intercept)替换或扩展任意系统调用,intercept支持QL_INTERCEPT.CALL(替换)、ENTER(前置)、EXIT(后置)三种语义(posix.py);
  • API hooking——ql.os.set_api(name, callback)拦截库函数调用;
  • 二进制补丁——ql.patch(offset, data, target=None)运行时打补丁,target为None时作用于主可执行文件,否则作用于指定库(core.py);
  • 状态快照——ql.save()/ql.restore()做检查点;save可按需选择包含寄存器(reg)、内存(mem)、硬件(hw,仅裸机)、FD 表(fd)、Unicorn 上下文(cpu_context)、OS 状态(os)、加载器状态(loader),并可选写入快照文件(core.py);
  • 调试器挂接——GDB 远程协议(qiling/debugger/gdb/)或原生 QDB 调试器(qiling/debugger/qdb/);
  • 覆盖率/追踪——extensions/tracing/、extensions/trace.py;
  • 模糊测试——通过extensions/afl/集成 AFL;
  • 硬件外设——在hw/中注册自定义 MCU 外设。

八、混合内核架构(Hybrid Kernel Architecture)

详细实现计划与任务跟踪见 TODO.md。

8.1 要解决的问题

Qiling 以 Python 逐系统调用地重新实现 Linux 内核行为,这对简单操作(文件 I/O、内存管理、stat)可行,但无法覆盖完整内核面:

  • 网络:无 epoll;套接字代理到宿主机套接字且无隔离;无真实 TCP 状态机、无多播、无 raw/netlink 套接字;
  • 多线程:gevent 协程为协作式、单线程;无抢占、无真实并发;futex 退化为 gevent Event;使用 pthreads、互斥锁、条件变量的程序行为不正确;
  • 信号:signal()、sigaction()、kill()大多为桩实现;无信号投递、无EINTR、无SA_RESTART;
  • 长尾:capabilities、cgroups、namespaces、io_uring、seccomp、eBPF——内核 API 面庞大且持续增长。

8.2 解决方案

混合架构:保留 Unicorn 做 CPU 模拟、Qiling 做插桩,但将复杂内核子系统通过kernel proxy辅助进程卸载给真实 Linux 内核;简单系统调用仍由 Python 模拟。

Syscall interrupt → load_syscall() [UNCHANGED — existing dispatch in posix.py] → check posix_syscall_hooks[CALL] → proxy hook registered? → forward to kernel proxy → no proxy hook? → existing Python handler [UNCHANGED]

由用户显式选择转发哪些缺失的系统调用,非自动行为——默认情况下 Qiling 行为与今天完全一致。集成复用现有的set_syscall()CALL hook 机制(posix.py),因此load_syscall()与所有现有分发代码完全不变。

proxy = KernelProxy(ql) proxy.forward_syscall("epoll_create", returns_fd=True) proxy.forward_syscall("epoll_ctl") proxy.forward_syscall("epoll_wait") ql.run()

该方案已在源码中落地:qiling/os/posix/kernel_proxy/ 包含KernelProxy类(init.py)、参数类型描述(argtypes.pyINT/FD/PtrIn/PtrOut/PtrInOut)、IPC 客户端/服务端(ipc.py)、代理 FD(proxy_fd.py),并有测试 tests/test_kernel_proxy.py。

实现细节(对应init.py 源码):

  • KernelProxy(ql)构造时要求宿主机为 Linux(否则抛出QlErrorArch),并启动一个代理子进程python -m qiling.os.posix.kernel_proxy.proxy <socket_fd>,通过 Unix socketpair 通信;
  • 代理进程(proxy.py)加载 libc,通过libc.syscall()执行真实 Linux 系统调用,以内核约定的负数 errno 返回结果;支持raw_syscall(整型参数直传)与raw_syscall_ex(带缓冲区打包,in_bufs初始化入参、out_specs回写输出);
  • forward_syscall(name, returns_fd=False, arg_types=None)在注册时解析访客架构的系统调用号(_resolve_syscall_nr),并为该调用注册一个QL_INTERCEPT.CALLhook;returns_fd=True时,返回值被包装成ql_proxy_fd对象存入 Qiling FD 表(_alloc_fd),由于 FD 表本身已是多态的(ql_socket、ql_file、ql_pipe),现有ql_syscall_read/ql_syscall_close等处理器会自动经由代理 FD 的.read()/.close()方法分发,无需改动;
  • arg_types支持FD(访客 FD 翻译为代理 FD)、PtrIn/PtrOut/PtrInOut(访客内存缓冲区与代理侧互拷),默认全部按INT直传;大小可用 lambda 动态计算(如epoll_wait的PtrOut(size=lambda a: a[2] * 12));
  • 代理进程支持FD_OP消息(READ/WRITE/CLOSE/DUP/FCNTL/IOCTL),使ql_proxy_fd能在代理侧执行文件描述符操作(proxy.py)。

8.3 分阶段实施计划

阶段范围风险目标
0概念验证低用户手动转发特定系统调用——零现有代码改动
1网络基础低-中socket 系统调用专用 hook、ql_proxy_fd、TCP 可用
2完整网络中epoll、poll/select、网络命名空间
3真实线程高每线程一个 Unicorn、共享内存、真实 futex
4信号中真实信号投递、EINTR、处理器执行
5集成低API 打磨、回退、平台支持、基准测试

阶段 0 赋予用户明确控制权——由用户指定要转发的缺失系统调用;阶段 1-2 为网络引入指针感知转发与预置 forwarder;阶段 3(线程)风险最高,延后到网络稳定后再做。每个阶段保持向后兼容——混合模式为可选,默认行为不变。

8.4 备选方案评估

  • 在 Unicorn 中运行真实内核:Unicorn 不模拟硬件(中断控制器、MMU 页表、定时器),需要重建 QEMU system 模式;
  • 基于 ptrace 执行:原生运行并拦截系统调用,速度快,但无跨架构支持、指令级 hook 有限;
  • User-Mode Linux(UML):将内核作为用户态进程运行,仅 x86、维护乏力、系统调用桥接复杂;
  • 自动转发所有未实现系统调用:方便但不可预测——难以调试,可能转发本不应转发的调用(安全、状态泄漏风险)。显式用户控制更安全。

最终选择混合方案,是因为它在不修改现有分发路径的前提下,既保留了 Qiling 的核心价值(插桩 + 跨架构模拟),又在最关键处获得真实内核行为。

九、测试体系

测试位于 tests/ 目录,按平台组织:test_elf.py、test_pe.py、test_macho.py、test_dos.py、test_mcu.py等,以 examples/rootfs/ 下的二进制作为测试夹具。此外还有针对本架构文档新增能力的test_kernel_proxy.py、针对调试器的test_debugger.py/test_qdb.py、模糊测试的test_afl.py、UEFI 的test_uefi.py等。

十、支持的平台组合

  • 架构:x86、x86-64、8086、ARM、ARM64、MIPS、RISC-V(32/64)、PowerPC、Cortex-M;
  • 操作系统:Linux、FreeBSD、macOS、Windows、UEFI、DOS、QNX、MCU(裸机)、Blob。

结语

Qiling 的架构核心在于"分层 + 可插拔":Unicorn 提供指令级模拟底座,QlArch/QlOs/QlLoader/QlMemoryManager各司其职,Qiling主类统一装配,utils.py的运行时选择机制让任意 OS×Arch 组合得以从统一代码库动态组装。在此基础上,Hook 系统与 kernel proxy 分别提供了"Python 侧插桩"与"真实内核卸载"两种互补的扩展路径,前者面向逆向分析与模糊测试,后者则为完整网络、线程、信号等复杂内核语义指明了演进方向——而这正是 TODO.md 所跟踪的混合内核架构路线图的目标。

  • 逆向工程
  • 网络安全
  • 虚拟化
  • 物联网

【免费下载链接】qiling

A True Instrumentable Binary Emulation Framework

项目地址:https://gitcode.com/gh_mirrors/qi/qiling
点击查看免费下载

相关推荐

上一篇:Skinview3D 开源项目教程
下一篇:SimplENet 开源项目教程

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询