Qiling IDA Pro Plugin

Qiling IDA Pro Plugin

Combines IDA Pro static analysis with Qiling dynamic emulation.

Supported Platforms

8086x86x86-64ARMARM64MIPS
Windows (PE)---
Linux (ELF)-
MacOS (MachO)---
BSD (ELF)-
UEFI----
DOS (COM)-----
MBR-----

Installation

Prerequisites: Qiling installed (pip3 install qiling or dev branch).

1# Linux
2ln -s ~/.local/lib/<python>/site-packages/qiling/extensions/idaplugin/qilingida.py /path/to/ida/plugins/
3
4# macOS
5ln -s /usr/local/lib/<python>/site-packages/qiling/extensions/idaplugin/qilingida.py /Applications/<IDA>/ida.app/Contents/MacOS/plugins/
6
7# Windows
8mklink C:\path\to\IDA\plugins\qilingida.py C:\Users\<user>\AppData\Roaming\Python\<python>\site-packages\qiling\extensions\idaplugin\qilingida.py

As Script File

File → Script file... → select qilingida.py.

Available under Edit → Plugins → Qiling Emulator and right-click context menu.

Requirements: IDA Pro 7.x, Python 3.6+. Recommended: macOS or Linux. Avoid Python 3.8 (IDA crashes; see Hex-Rays blog for fix).

Setup

Right-click → Qiling EmulatorSetup. Provide:

Success message: "Qiling is initialized successfully" in output window.

Emulation Controls

View Windows

To edit a register: right-click in register view → Edit Register.

Accessing the Qiling Object in IDAPython Console

1ql_plugin = ida_ida.ql_plugin
2qlemu = ql_plugin.qlemu
3ql = qlemu.ql  # safe to use in most cases
4
5# Address conversion
6qladdr = qlemu.ql_addr_from_ida(idaaddr)
7ql.mem.read(qladdr, 4)
8idaaddr = qlemu.ida_addr_from_ql_addr(qladdr)

Custom User Scripts

A Python file with a QILING_IDA class. Three callback methods:

MethodWhen called
custom_prepare(ql)Before ql.run()
custom_continue(ql)When user clicks Continue
custom_step(ql)When user steps an instruction

custom_continue and custom_step must return a list of hooks (plugin removes them after each action).

Minimal template

 1from qiling import *
 2
 3class QILING_IDA():
 4    def __init__(self):
 5        pass
 6
 7    def custom_prepare(self, ql):
 8        pass
 9
10    def custom_continue(self, ql):
11        return []
12
13    def custom_step(self, ql):
14        return []

Register logging example

 1from qiling import *
 2import logging
 3
 4class QILING_IDA():
 5    def _show_context(self, ql):
 6        registers = [k for k in ql.arch.regs.register_mapping.keys() if type(k) is str]
 7        for idx in range(0, len(registers), 3):
 8            regs = registers[idx:idx+3]
 9            s = "\t".join(map(lambda v: f"{v:4}: {ql.arch.regs.__getattribute__(v):016x}", regs))
10            logging.info(s)
11
12    def custom_prepare(self, ql):
13        self._show_context(ql)
14
15    def custom_continue(self, ql):
16        self._show_context(ql)
17        return []
18
19    def custom_step(self, ql):
20        def step_hook(ql, addr, size):
21            logging.info(f"Executing: {hex(addr)}")
22            self._show_context(ql)
23        return [ql.hook_code(step_hook)]

Scripts can be reloaded dynamically: Reload User Scripts in the menu.

Snapshots

Snapshots capture full emulation context (registers, memory, file descriptors).

OLLVM De-flattening

Removes Control Flow Flattening obfuscation from OLLVM-protected binaries.

Block types after auto-analysis:

Workflow:

  1. Complete basic Setup (rootfs + script)
  2. Right-click → Auto Analysis For Deflat
  3. Manually correct misclassified blocks via right-click → Mark as Real/Fake/Return Block
  4. Right-click → Deflat
  5. Press F5 to decompile — output is clean without obfuscation