Skip to content

Getting Started

xwings edited this page Jul 6, 2025 · 2 revisions

Getting Started

This guide will walk you through your first steps with Qiling Framework, from basic usage to more advanced features.

Your First Qiling Script

Emulating a Binary

The simplest way to use Qiling is to emulate a binary file:

from qiling import Qiling

# Basic binary emulation
ql = Qiling(['/path/to/binary'], '/path/to/rootfs')
ql.run()

Real Example:

from qiling import Qiling
from qiling.const import QL_VERBOSE

# Emulate a Linux x86_64 binary
ql = Qiling(['examples/rootfs/x8664_linux/bin/x8664_hello'], 
           'examples/rootfs/x8664_linux',
           verbose=QL_VERBOSE.DEBUG)
ql.run()

Emulating Shellcode

For shellcode analysis:

from qiling import Qiling
from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE

# Linux x86_64 execve shellcode
shellcode = bytes.fromhex('''
    48c7c03c00000048c7c700000000488d35060000000f05
    2f62696e2f736800
''')

ql = Qiling(code=shellcode, 
           rootfs='examples/rootfs/x8664_linux',
           archtype=QL_ARCH.X8664, 
           ostype=QL_OS.LINUX,
           verbose=QL_VERBOSE.DEBUG)
ql.run()

Qiling Object Initialization

Binary Emulation Parameters

Parameter Type Description
argv Sequence[str] Command line arguments (first is the binary path)
rootfs str Emulated filesystem root directory
env dict (optional) Environment variables

Shellcode Emulation Parameters

Parameter Type Description
code bytes Shellcode bytes to emulate
rootfs str (optional) Emulated filesystem root
archtype QL_ARCH Target architecture
ostype QL_OS Target operating system
endian bool (optional) Architecture endianness
thumb bool (optional) ARM Thumb mode

Common Parameters

Parameter Type Description
verbose QL_VERBOSE Logging verbosity level
profile str (optional) Profile file path
multithread bool (optional) Enable multithreading support
libcache bool (optional) Enable library caching

Essential Concepts

Architecture Types (QL_ARCH)

  • QL_ARCH.X86 - 32-bit x86
  • QL_ARCH.X8664 - 64-bit x86
  • QL_ARCH.ARM - 32-bit ARM
  • QL_ARCH.ARM64 - 64-bit ARM
  • QL_ARCH.MIPS - MIPS
  • QL_ARCH.A8086 - 8086
  • QL_ARCH.CORTEX_M - Cortex-M
  • QL_ARCH.RISCV - RISC-V 32-bit
  • QL_ARCH.RISCV64 - RISC-V 64-bit
  • QL_ARCH.PPC - PowerPC

Operating Systems (QL_OS)

  • QL_OS.LINUX - Linux
  • QL_OS.WINDOWS - Windows
  • QL_OS.MACOS - macOS
  • QL_OS.FREEBSD - FreeBSD
  • QL_OS.UEFI - UEFI
  • QL_OS.DOS - DOS
  • QL_OS.QNX - QNX
  • QL_OS.MCU - Microcontroller
  • QL_OS.BLOB - Raw binary blob

Verbosity Levels (QL_VERBOSE)

  • QL_VERBOSE.OFF - No logging
  • QL_VERBOSE.DEFAULT - Basic logging
  • QL_VERBOSE.DEBUG - Detailed debugging
  • QL_VERBOSE.DISASM - Include disassembly
  • QL_VERBOSE.DUMP - Memory dumps

Configuration and Customization

Filesystem Mapping

Map host directories to emulated filesystem:

# Map host /tmp to emulated /tmp
ql.add_fs_mapper('/tmp', '/tmp')

# Map a file to another file
ql.add_fs_mapper('/dev/random', '/dev/zero')

# Create fake files
from qiling.os.posix import QlPosixFileOperations
ql.add_fs_mapper('/proc/version', QlPosixFileOperations.fake_proc_version)

Memory Patching

Modify binary content at runtime:

# Patch bytes at address
ql.patch(0x401000, b'\x90\x90\x90\x90')  # NOP sled

# Patch with string
ql.patch(0x402000, b'Hello, World!\x00')

Environment Variables

# Set environment variables
env = {
    'HOME': '/home/user',
    'PATH': '/bin:/usr/bin',
    'LD_LIBRARY_PATH': '/lib'
}
ql = Qiling(argv, rootfs, env=env)

Emulation Control

Basic Execution

# Run until exit
ql.run()

# Run with constraints
ql.run(begin=0x401000, end=0x401100)  # Run specific address range
ql.run(timeout=1000000)               # Timeout in microseconds
ql.run(count=1000)                    # Max instruction count

Hooking and Instrumentation

Address Hooks:

def my_hook(ql):
    print(f"Hit address: 0x{ql.arch.regs.rip:x}")

# Hook specific address
ql.hook_address(my_hook, 0x401000)

API Hooks:

def hook_malloc(ql, size):
    print(f"malloc called with size: {size}")
    # Call original malloc
    return ql.os.heap.alloc(size)

# Hook system API
ql.set_api("malloc", hook_malloc)

Instruction Hooks:

def instruction_hook(ql, address, size):
    print(f"Executing: 0x{address:x}")

ql.hook_code(instruction_hook)

Common Examples

Example 1: Basic Binary Analysis

from qiling import Qiling
from qiling.const import QL_VERBOSE

def analyze_binary(binary_path, rootfs_path):
    ql = Qiling([binary_path], rootfs_path, verbose=QL_VERBOSE.DEBUG)
    
    # Track function calls
    def track_calls(ql, address, size):
        if ql.mem.is_mapped(address, 1):
            code = ql.mem.read(address, size)
            if code[0] == 0xe8:  # CALL instruction
                print(f"Function call at: 0x{address:x}")
    
    ql.hook_code(track_calls)
    ql.run()

analyze_binary('examples/rootfs/x8664_linux/bin/x8664_hello',
               'examples/rootfs/x8664_linux')

Example 2: Shellcode Analysis with Hooks

from qiling import Qiling
from qiling.const import QL_ARCH, QL_OS

def analyze_shellcode():
    # Sample shellcode (execve /bin/sh)
    shellcode = bytes.fromhex('31c048bbd19d9691d08c97ff48f7db53545f995257545eb03b0f05')
    
    ql = Qiling(code=shellcode, 
               archtype=QL_ARCH.X8664, 
               ostype=QL_OS.LINUX)
    
    # Hook syscalls
    def syscall_hook(ql, intno):
        if intno == 0x80:  # Linux syscall
            print(f"Syscall: {ql.arch.regs.rax}")
    
    ql.hook_intr(syscall_hook)
    
    # Memory access tracking
    def mem_hook(ql, access, address, size, value):
        print(f"Memory access: {access} at 0x{address:x}")
    
    ql.hook_mem_read(mem_hook)
    ql.hook_mem_write(mem_hook)
    
    ql.run()

analyze_shellcode()

Example 3: Using Profiles

Create a profile file my_config.ql:

[CODE]
entry_point = 0x8048000
stack_address = 0xfffdd000

[LOG]
level = DEBUG
console = true

[MISC]
current_path = /tmp

Use in your script:

ql = Qiling([binary], rootfs, profile='my_config.ql')
ql.run()

Debugging

Built-in Debugger (QDB)

from qiling import Qiling

ql = Qiling([binary], rootfs)
ql.debugger = "qdb"  # Enable QDB debugger
ql.run()

QDB Commands:

  • run / r - Start execution
  • step / s - Single step
  • continue / c - Continue execution
  • info reg - Show registers
  • x/10x $rsp - Examine memory
  • breakpoint 0x401000 - Set breakpoint

GDB Server

ql = Qiling([binary], rootfs)
ql.debugger = "gdb:localhost:9999"
ql.run()

Connect with GDB:

gdb
(gdb) target remote localhost:9999
(gdb) continue

Next Steps

Now that you understand the basics:

  1. Explore Advanced Usage for hooking, instrumentation, and analysis
  2. Check out API Reference for detailed documentation
  3. Look at Examples in the repository
  4. Try Fuzzing with Qiling
  5. Learn about Debugging capabilities

Common Patterns

Safe Emulation Wrapper

def safe_emulate(binary_path, rootfs_path, timeout=10000000):
    try:
        ql = Qiling([binary_path], rootfs_path)
        ql.run(timeout=timeout)
        return True
    except Exception as e:
        print(f"Emulation failed: {e}")
        return False

Dynamic Analysis Framework

class QilingAnalyzer:
    def __init__(self, target, rootfs):
        self.ql = Qiling([target], rootfs)
        self.api_calls = []
        self.memory_writes = []
        
    def setup_hooks(self):
        self.ql.hook_code(self.track_execution)
        self.ql.hook_mem_write(self.track_memory_writes)
        
    def track_execution(self, ql, address, size):
        # Custom tracking logic
        pass
        
    def track_memory_writes(self, ql, access, address, size, value):
        self.memory_writes.append((address, size, value))
        
    def analyze(self):
        self.setup_hooks()
        self.ql.run()
        return {
            'api_calls': self.api_calls,
            'memory_writes': self.memory_writes
        }

Troubleshooting

Common Issues

Binary fails to load:

  • Check if rootfs contains necessary libraries
  • Verify architecture and OS detection
  • Use verbose=QL_VERBOSE.DEBUG for details

Shellcode doesn't execute:

  • Verify architecture and OS parameters
  • Check entry point and memory layout
  • Ensure proper shellcode format

Import errors:

  • Check Qiling installation
  • Verify Python version compatibility
  • Try reinstalling: pip install --upgrade qiling

Need help? Join our Telegram chat or open an issue.

Clone this wiki locally