当前位置:首页 > 技术 > 正文内容

Qiling 框架核心功能与实战速查手册

访客 技术 2026年9月19日 11

Qiling 是一个基于 Unicorn 引擎构建的高级二进制模拟框架,专为逆向工程、恶意代码分析和漏洞研究设计。它不仅支持多架构(x86、ARM、MIPS 等)和多操作系统(Linux、Windows、UEFI 等),还提供了丰富的钩子机制和虚拟文件系统能力。

环境安装与初始化

通过 pip 安装:

pip install qiling

模拟 Linux x86_64 程序需准备 rootfs,可从官方仓库 examples/rootfs/x8664_linux 获取。

核心架构组件

  • 加载器(Loader):识别二进制格式,加载段,设置栈、堆、环境变量及命令行参数。
  • 架构层(Arch):处理 CPU 特性(如 VFP)、寄存器布局、TLS 初始化等。
  • 操作系统层(OS):实现系统调用、API 映射、标准 I/O、线程管理等运行时行为。

典型实战:QilingLab 解题示例

以下代码展示了如何通过 Qiling 绕过多个反调试与验证逻辑:

from qiling import Qiling
from qiling.const import QL_INTERCEPT
from qiling.os.mapper import QlFsMappedObject
import struct

def setup_challenge1(ql):
    ql.mem.map(0x1000, 0x1000)
    ql.mem.write(0x1337, ql.pack16(1337))

def fake_uname(ql, *args):
    rdi = ql.arch.regs.rdi
    ql.mem.write(rdi, b'QilingOS\x00')
    ql.mem.write(rdi + 195, b'ChallengeStart\x00')  # 65*3=195

def setup_challenge2(ql):
    ql.os.set_syscall('uname', fake_uname, QL_INTERCEPT.EXIT)

class FakeRandom(QlFsMappedObject):
    def read(self, size):
        return b'\x10' if size == 1 else b'\x00' * size
    def close(self): return 0

def mock_getrandom(ql, buf, sz, flags):
    ql.mem.write(buf, b'\x00' * sz)
    ql.arch.regs.rax = 0

def setup_challenge3(ql):
    ql.os.set_syscall('getrandom', mock_getrandom, QL_INTERCEPT.CALL)
    ql.add_fs_mapper('/dev/urandom', FakeRandom())

def bypass_cmp(ql):
    ql.arch.regs.eax = 1

def setup_challenge4(ql):
    hook_addr = ql.loader.load_address + 0xE43
    ql.hook_address(bypass_cmp, hook_addr)

def mock_rand(ql):
    ql.arch.regs.rax = 0

def setup_challenge5(ql):
    ql.os.set_api('rand', mock_rand, QL_INTERCEPT.CALL)

def skip_infinite_loop(ql):
    ql.arch.regs.rax = 0

def setup_challenge6(ql):
    ql.hook_address(skip_infinite_loop, ql.loader.load_address + 0xF16)

def noop_sleep(ql):
    pass

def setup_challenge7(ql):
    ql.os.set_api('sleep', noop_sleep, QL_INTERCEPT.CALL)

def patch_struct_check(ql):
    rax = ql.arch.regs.rax
    data = ql.mem.read(rax, 24)
    str_ptr, magic, flag_ptr = struct.unpack("QQQ", data)
    ql.mem.write(flag_ptr, b'\x01')

def setup_challenge8(ql):
    ql.hook_address(patch_struct_check, ql.loader.load_address + 0xFB5)

def fix_byte_flag(ql):
    addr = ql.arch.regs.rax
    ql.mem.write(addr, b'\x01')

def setup_challenge9(ql):
    ql.hook_address(fix_byte_flag, ql.loader.load_address + 0x1061)

def spoof_string(ql):
    ptr = ql.arch.regs.rax
    ql.mem.write(ptr, b'qilinglab\x00')

def setup_challenge10(ql):
    ql.hook_address(spoof_string, ql.loader.load_address + 0x112D)

def fake_cpuid(ql, *args):
    ql.arch.regs.ebx = 0x696C6951  # 'Qili'
    ql.arch.regs.ecx = 0x614C676E  # 'ngLa'
    ql.arch.regs.edx = 0x20202062  # 'b   '

def setup_challenge11(ql):
    ql.hook_address(fake_cpuid, ql.loader.load_address + 0x1191)

if __name__ == '__main__':
    binary = [r'./qilinglab-x86_64']
    fs_root = r'./rootfs/x8664_linux'
    emu = Qiling(binary, fs_root)
    
    # 应用所有挑战补丁
    setup_challenge1(emu)
    setup_challenge2(emu)
    setup_challenge3(emu)
    setup_challenge4(emu)
    setup_challenge5(emu)
    setup_challenge6(emu)
    setup_challenge7(emu)
    setup_challenge8(emu)
    setup_challenge9(emu)
    setup_challenge10(emu)
    setup_challenge11(emu)
    
    emu.verbose = 0
    emu.run()

关键操作速查表

寄存器访问

操作示例
读取 EAXval = ql.arch.regs.eax
写入 RDIql.arch.regs.rdi = 0x4000
获取 PCpc = ql.arch.regs.arch_pc
获取 SPsp = ql.arch.regs.arch_sp

内存操作

  • ql.mem.map(addr, size):映射内存
  • ql.mem.read(addr, n):读取 n 字节
  • ql.mem.write(addr, data):写入字节数据
  • ql.mem.string(addr):读取 C 字符串
  • ql.mem.search(pattern):全局搜索字节序列

数据打包/解包

自动适配架构位数与字节序:

  • ql.pack32(0x12345678) → b'\x78\x56\x34\x12'(小端)
  • ql.unpack64(data):解包为 64 位整数

钩子机制

  • 地址钩子:ql.hook_address(callback, 0x401000)
  • 系统调用钩子:ql.os.set_syscall('read', my_read)
  • API 钩子:ql.os.set_api('printf', fake_printf, QL_INTERCEPT.CALL)
  • 内存访问钩子:ql.hook_mem_read(log_fn, 0x5000, 0x6000)

虚拟文件系统 (VFS)

通过 QlFsMappedObject 自定义文件行为:

class FakeProc(QlFsMappedObject):
    def read(self, size): return b"fake content"
    def close(self): return 0

ql.add_fs_mapper("/proc/cpuinfo", FakeProc())

远程调试支持

启用 GDB 兼容调试服务器:

ql.debugger = "gdb:127.0.0.1:9999"

或使用内置 Qdb 调试器:

ql.debugger = "qdb::rr"  # 启用记录回放

扩展开发建议

对于未实现的系统调用或 API,推荐先通过 set_syscall 或 set_api 进行原型验证,再贡献至核心代码库。POSIX 系统调用实现在 qiling/os/linux/syscall/ 目录,Windows API 在 qiling/os/windows/dll/。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。