-
Notifications
You must be signed in to change notification settings - Fork 758
Getting Started
xwings edited this page Jul 6, 2025
·
2 revisions
This guide will walk you through your first steps with Qiling Framework, from basic usage to more advanced features.
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()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()| 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 |
| 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 |
| 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 |
-
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
-
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
-
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
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)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')# Set environment variables
env = {
'HOME': '/home/user',
'PATH': '/bin:/usr/bin',
'LD_LIBRARY_PATH': '/lib'
}
ql = Qiling(argv, rootfs, env=env)# 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 countAddress 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)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')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()Create a profile file my_config.ql:
[CODE]
entry_point = 0x8048000
stack_address = 0xfffdd000
[LOG]
level = DEBUG
console = true
[MISC]
current_path = /tmpUse in your script:
ql = Qiling([binary], rootfs, profile='my_config.ql')
ql.run()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
ql = Qiling([binary], rootfs)
ql.debugger = "gdb:localhost:9999"
ql.run()Connect with GDB:
gdb
(gdb) target remote localhost:9999
(gdb) continueNow that you understand the basics:
- Explore Advanced Usage for hooking, instrumentation, and analysis
- Check out API Reference for detailed documentation
- Look at Examples in the repository
- Try Fuzzing with Qiling
- Learn about Debugging capabilities
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 Falseclass 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
}Binary fails to load:
- Check if rootfs contains necessary libraries
- Verify architecture and OS detection
- Use
verbose=QL_VERBOSE.DEBUGfor 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.
- Home
- Getting Started
- Core Concepts
- Usage
- Features
- Tutorials
- Development
- Resources