-
Notifications
You must be signed in to change notification settings - Fork 756
Basic Usage
xwings edited this page Jul 6, 2025
·
2 revisions
Essential patterns and techniques for using Qiling Framework effectively.
Qiling emulates binaries by providing a virtual environment that mimics the target operating system and architecture.
from qiling import Qiling
# Basic binary emulation
ql = Qiling(['path/to/binary'], 'path/to/rootfs')
ql.run()Emulate raw shellcode without a full binary:
from qiling import Qiling
from qiling.const import QL_ARCH, QL_OS
# x86_64 Linux shellcode
shellcode = b"\x48\x31\xc0\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\xff\xc0\x48\x89\xc7\x48\xff\xc0\x0f\x05"
ql = Qiling(code=shellcode,
archtype=QL_ARCH.X8664,
ostype=QL_OS.LINUX)
ql.run()from qiling import Qiling
ql = Qiling(['binary'], 'rootfs')
# Read memory
data = ql.mem.read(0x1000, 100) # Read 100 bytes from 0x1000
# Write memory
ql.mem.write(0x1000, b"Hello World!")
# Read/write pointers
pointer_value = ql.mem.read_ptr(0x2000)
ql.mem.write_ptr(0x2000, 0x12345678)
# Read null-terminated strings
string_data = ql.mem.string(0x3000)from unicorn import UC_PROT_READ, UC_PROT_WRITE, UC_PROT_EXEC
# Map memory region
ql.mem.map(0x10000000, 0x1000,
perms=UC_PROT_READ | UC_PROT_WRITE,
info="custom_region")
# Map memory anywhere
addr = ql.mem.map_anywhere(0x1000, info="dynamic_region")
# Unmap memory
ql.mem.unmap(0x10000000, 0x1000)
# Check if memory is mapped
if ql.mem.is_mapped(0x10000000, 0x1000):
print("Memory is mapped")# Get memory map information
map_info = ql.mem.get_mapinfo()
for start, end, perms, label, container in map_info:
print(f"0x{start:08x}-0x{end:08x} {label}")
# Get formatted memory map
print(ql.mem.get_formatted_mapinfo())
# Search for patterns
for addr in ql.mem.search(b"password"):
print(f"Found pattern at 0x{addr:x}")# Read registers (x86_64 example)
rax_value = ql.arch.regs.rax
rip_value = ql.arch.regs.rip
# Write registers
ql.arch.regs.rax = 0x12345678
ql.arch.regs.rip = 0x401000
# Generic register access
value = ql.arch.regs.read("rax")
ql.arch.regs.write("rax", 0x87654321)
# Get all registers
all_regs = ql.arch.regs.save()
print(f"RAX: 0x{all_regs['rax']:x}")# x86/x86_64
ql.arch.regs.eax # 32-bit
ql.arch.regs.rax # 64-bit
ql.arch.regs.esp # Stack pointer (32-bit)
ql.arch.regs.rsp # Stack pointer (64-bit)
# ARM
ql.arch.regs.r0 # General purpose
ql.arch.regs.sp # Stack pointer
ql.arch.regs.lr # Link register
ql.arch.regs.pc # Program counter
# ARM64
ql.arch.regs.x0 # 64-bit general purpose
ql.arch.regs.w0 # 32-bit view of x0
ql.arch.regs.sp # Stack pointerMonitor instruction execution:
def code_hook(ql, address, size):
print(f"Executing instruction at 0x{address:x}, size: {size}")
# Hook all instructions
ql.hook_code(code_hook)
# Hook specific address
def specific_hook(ql):
print(f"Hit specific address: 0x{ql.arch.regs.rip:x}")
ql.hook_address(specific_hook, 0x401000)Monitor memory access:
def mem_read_hook(ql, access, address, size, value):
print(f"Memory read: 0x{address:x} -> 0x{value:x}")
def mem_write_hook(ql, access, address, size, value):
print(f"Memory write: 0x{address:x} = 0x{value:x}")
ql.hook_mem_read(mem_read_hook)
ql.hook_mem_write(mem_write_hook)Intercept function calls:
def custom_malloc(ql, size):
"""Custom malloc implementation"""
print(f"malloc({size}) called")
# Allocate memory using Qiling's heap
addr = ql.os.heap.alloc(size)
print(f"Allocated at 0x{addr:x}")
return addr
# Hook malloc function
ql.set_api('malloc', custom_malloc)# Push values to stack
ql.stack_push(0x12345678)
ql.stack_push(b"Hello")
# Pop values from stack
value = ql.stack_pop()
# Read/write at stack offsets
data = ql.stack_read(8) # Read 8 bytes above stack pointer
ql.stack_write(8, b"data") # Write at offset# Map host paths to guest paths
ql.add_fs_mapper("/tmp", "./sandbox")
ql.add_fs_mapper("/etc/passwd", "./fake_passwd")
# Remove mappings
ql.remove_fs_mapper("/tmp")import sys
from io import StringIO
# Redirect stdout to capture output
captured_output = StringIO()
ql = Qiling(['binary'], 'rootfs', stdout=captured_output)
ql.run()
# Get captured output
output = captured_output.getvalue()
print(f"Program output: {output}")
# Provide custom stdin
input_data = StringIO("input data\n")
ql = Qiling(['binary'], 'rootfs', stdin=input_data)
ql.run()# Pass arguments to the emulated binary
argv = ['binary_name', '--flag', 'value', 'input.txt']
ql = Qiling(argv, 'rootfs')
ql.run()# Set environment variables
env = {
'PATH': '/bin:/usr/bin',
'HOME': '/home/user',
'LANG': 'en_US.UTF-8',
'DEBUG': '1'
}
ql = Qiling(['binary'], 'rootfs', env=env)
ql.run()# Run with timeout (microseconds)
ql.run(timeout=1000000) # 1 second
# Run with instruction count limit
ql.run(count=1000) # Max 1000 instructions
# Run between specific addresses
ql.run(begin=0x401000, end=0x402000)def stop_hook(ql):
print("Stopping execution")
ql.emu_stop() # Stop emulation
ql.hook_address(stop_hook, 0x401234)
ql.run()# Save complete state
saved_state = ql.save()
# Modify state and run
ql.arch.regs.rax = 0x999
ql.run()
# Restore previous state
ql.restore(saved_state)
ql.run() # Runs from saved state# Save with custom name
ql.save(name="checkpoint1")
# Restore specific snapshot
ql.restore(saved_states, snapshot="checkpoint1")# Enable QDB debugger
ql.debugger = "qdb"
ql.run()
# QDB will provide interactive debugging
# Commands: run, step, continue, breakpoint, info reg, x/10x $rsp# Start GDB server
ql.debugger = "gdb:localhost:9999"
ql.run()
# In another terminal:
# gdb -ex "target remote localhost:9999"from qiling.exception import *
try:
ql = Qiling(['binary'], 'rootfs')
ql.run()
except QlErrorFileNotFound:
print("Binary file not found")
except QlErrorArch:
print("Architecture not supported")
except QlErrorOS:
print("Operating system not supported")
except QlErrorException as e:
print(f"Qiling error: {e}")def mem_invalid_hook(ql, access, address, size, value):
print(f"Invalid memory access at 0x{address:x}")
# Map the page to continue execution
page_addr = address & 0xfffff000
ql.mem.map(page_addr, 0x1000)
print(f"Mapped page at 0x{page_addr:x}")
ql.hook_mem_invalid(mem_invalid_hook)from qiling.const import QL_VERBOSE
# Different verbosity levels
ql = Qiling(['binary'], 'rootfs', verbose=QL_VERBOSE.OFF) # Silent
ql = Qiling(['binary'], 'rootfs', verbose=QL_VERBOSE.DEFAULT) # Basic
ql = Qiling(['binary'], 'rootfs', verbose=QL_VERBOSE.DEBUG) # Detailed
ql = Qiling(['binary'], 'rootfs', verbose=QL_VERBOSE.DISASM) # With disassembly
ql = Qiling(['binary'], 'rootfs', verbose=QL_VERBOSE.DUMP) # With memory dumps# Enable multithread support for threaded applications
ql = Qiling(['threaded_binary'], 'rootfs', multithread=True)
ql.run()# Enable library caching for faster Windows emulation
ql = Qiling(['windows_binary.exe'], 'rootfs_windows', libcache=True)
ql.run()api_calls = []
def monitor_api(ql, *args, **kwargs):
"""Generic API monitor"""
func_name = kwargs.get('func_name', 'unknown')
api_calls.append({
'function': func_name,
'args': args,
'pc': ql.arch.regs.rip
})
print(f"API call: {func_name}")
return None # Call original function
# Hook multiple APIs
apis_to_monitor = ['CreateFileW', 'ReadFile', 'WriteFile', 'CloseHandle']
for api in apis_to_monitor:
ql.set_api(api, lambda ql, *args, **kwargs: monitor_api(ql, *args, func_name=api, **kwargs))
ql.run()
# Print API call summary
print(f"Total API calls: {len(api_calls)}")
for call in api_calls:
print(f" {call['function']} at 0x{call['pc']:x}")def analyze_memory_patterns(ql):
"""Analyze common memory patterns"""
patterns = {
'strings': [],
'urls': [],
'registry_keys': []
}
# Search for different patterns
for addr in ql.mem.search(b"http"):
try:
url = ql.mem.string(addr)
patterns['urls'].append((addr, url))
except:
pass
for addr in ql.mem.search(b"HKEY_"):
try:
key = ql.mem.string(addr)
patterns['registry_keys'].append((addr, key))
except:
pass
return patterns
# Run analysis after emulation
ql.run()
patterns = analyze_memory_patterns(ql)
print(f"Found {len(patterns['urls'])} URLs")
print(f"Found {len(patterns['registry_keys'])} registry keys")class ExecutionTracer:
def __init__(self):
self.trace = []
self.call_stack = []
def trace_instruction(self, ql, address, size):
self.trace.append({
'address': address,
'instruction': ql.mem.read(address, size),
'registers': ql.arch.regs.save()
})
def trace_function_call(self, ql, address, size):
# Simple function call detection (CALL instruction)
instruction = ql.mem.read(address, size)
if instruction[0] == 0xe8: # CALL rel32
self.call_stack.append(address)
def get_trace_summary(self):
return {
'instruction_count': len(self.trace),
'unique_addresses': len(set(t['address'] for t in self.trace)),
'call_depth': len(self.call_stack)
}
tracer = ExecutionTracer()
ql.hook_code(tracer.trace_instruction)
ql.hook_code(tracer.trace_function_call)
ql.run()
summary = tracer.get_trace_summary()
print(f"Executed {summary['instruction_count']} instructions")
print(f"Visited {summary['unique_addresses']} unique addresses")- Use appropriate verbosity levels - Only enable detailed output when needed
- Enable library caching - For Windows binaries with many DLL loads
- Use targeted hooks - Hook specific addresses instead of all instructions
- Memory management - Unmap unused memory regions
- Timeouts - Always set timeouts for unknown binaries
- Isolate rootfs - Use separate rootfs for each analysis
- Monitor resource usage - Watch memory and CPU consumption
- Validate inputs - Check binary files before emulation
- Limit execution - Use timeouts and instruction limits
- Network isolation - Block network access for malware analysis
- Start simple - Begin with basic emulation before adding hooks
- Use QDB - Interactive debugging for complex issues
-
Check memory maps - Verify memory layout with
get_mapinfo() - Monitor registers - Track register changes at critical points
- Incremental development - Add features one at a time
This covers the essential patterns for using Qiling Framework effectively. For more advanced topics, see:
- Hook System - Advanced hooking techniques
- Memory Management - Memory analysis and manipulation
- Platform Guides - Platform-specific usage
- Tutorials - Step-by-step examples
- Home
- Getting Started
- Core Concepts
- Usage
- Features
- Tutorials
- Development
- Resources