OxxxSec
139 subscribers
35 photos
17 files
291 links
My tools are darkness. My purpose is pure.
Post publication days: Monday-Wednesday
Security & embedded software expertise
GitHub:
https://github.com/M0nteCarl0
Alternative tg: +14233163546
Habr: @M0nteCarl0
YT:https://youtube.com/@oxxxsec?si=8TiqgcEWbpn3d
Download Telegram
⚡️ 🕹 Interactive Remote Debugging with GDB & IDA Pro Remote Stub (Python practice)

Automated scripts and hooks are indispensable, but when reverse engineering intricate algorithms, unpacking complex virtual machines, or stepping through heavily protected code, nothing beats an interactive graphical debugger. Qiling includes a built-in GDB Remote Serial Protocol (RSP) stub. With a single configuration flag (ql.debugger = 'gdb:127.0.0.1:9999'), Qiling halts on the first instruction and waits for an incoming connection from standard GDB, IDA Pro, Ghidra, or Binary Ninja.

🧠 Core Concept
- Native GDB RSP Server: Embedded GDB remote server implementation speaking standard GDB serial protocol packets over TCP.
- Cross-Architecture Debugging: Debug ARM, MIPS, or RISC-V binaries using standard gdb-multiarch on your host workstation without setting up QEMU GDB stubs.
- IDA Pro & Ghidra Integration: Connect IDA Pro's 'Remote GDB Debugger' directly to Qiling to inspect registers, set hardware breakpoints, and step through decompiled code.
- Hybrid Automation + Manual Stepping: Run Python hooks, VFS mappings, and API stubs seamlessly while simultaneously controlling execution flow from your GUI debugger.
- Zero Kernel Drivers: Entire debugging session operates cleanly in user-space without triggering anti-debug drivers or requiring OS-level root privileges.

## 💻 Implementation Example
#!/usr/bin/env python3
"""
Post 11: Interactive Remote Debugging with GDB & IDA Pro Remote Stub
Launching Qiling with an embedded GDB server stub and attaching IDA Pro or GDB-multiarch.
"""

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

def start_debug_session(binary_path: str, rootfs_path: str, port: int = 9999) -> None:
print(f"[*] Initializing Qiling sandbox for {binary_path}...")
ql = Qiling(
argv=[binary_path],
rootfs=rootfs_path,
ostype=QL_OS.LINUX,
archtype=QL_ARCH.ARM,
verbose=QL_VERBOSE.DEFAULT
)

# 1. Configure embedded GDB remote stub
# Format: "gdb:IP:PORT" or "qndb" for Qiling's native terminal debugger
debug_listen = f"127.0.0.1:{port}"
ql.debugger = f"gdb:{debug_listen}"

print("=" * 65)
print(f"[+] GDB Remote Debugger listening on: {debug_listen}")
print("[+] How to connect:")
print(f" GDB CLI : gdb-multiarch -ex 'target remote {debug_listen}'")
print(f" IDA Pro : Select 'Remote GDB Debugger' -> Host: 127.0.0.1 Port: {port}")
print(f" Ghidra : In Debugger tool -> Connect to GDB via RSP target")
print("=" * 65)

# 2. Run emulation (Qiling will pause at entry point waiting for GDB connection)
print("[*] Waiting for debugger connection...")
ql.run()

if __name__ == "__main__":
TARGET = "rootfs/arm_linux/bin/arm_crypto_challenge"
ROOTFS = "rootfs/arm_linux"
start_debug_session(TARGET, ROOTFS, port=9999)

🔥 Use Cases
- Stepping through stripped ARM/MIPS CTF reverse engineering challenges interactively in IDA Pro.
- Analyzing packed or obfuscated binaries where breakpoints need to be placed dynamically.
- Inspecting stack frames and memory structures with full GUI visualization in Ghidra.
- Debugging binaries that detect native OS debuggers by intercepting detection checks via Qiling stubs.
- Collaborative debugging where multiple security analysts connect to remote emulation instances.

⚠️ Caveats & Responsible Practice
- Architecture Mismatch: Ensure your client debugger understands the target architecture (use gdb-multiarch, not standard x86 gdb).
- Connection Timeout: Start the Qiling Python script first; once it displays the listening port, immediately connect from GDB/IDA.
- Thread Support: In complex multi-threaded binaries, individual thread switching in GDB RSP is supported but basic single-thread mode is most stable.
- Symbol Loading: In IDA Pro, load the ELF/PE database first, then attach the remote debugger with 'Use manual memory map' unchecked.

📦 Test Data & Sample Binaries
- Target Architecture RootFS
- Sample Target Binary
- Qiling GDB Server Stub

🔗 Resources
- Qiling Debugger Documentation
- GDB Remote Serial Protocol Reference

#Qiling #GDB #IDAPro #Ghidra #ReverseEngineering #Debugging #BinaryAnalysis #CyberSecurity
⚡️ 🛡️ UEFI DXE & SMM Firmware Emulation & Vulnerability Research (Python practice)

Unified Extensible Firmware Interface (UEFI) security is critical: malicious DXE drivers and System Management Mode (SMM) implants execute at Ring -2 below the operating system and hypervisor, remaining invisible to traditional EDR solutions. Auditing UEFI firmware components (.efi binaries) is historically cumbersome because they require physical motherboard flashing or complex virtualized OVMF firmware setups. Qiling includes a dedicated UEFI execution engine that emulates the UEFI Core Specification, protocol database, and NVRAM variable services.

🧠 Core Concept
- Native UEFI Subsystem (QL_OSTYPE.UEFI): Emulates UEFI DXE dispatcher, Boot Services (gBS), and Runtime Services (gRT) tables.
- Protocol Database Mocking: Intercept LocateProtocol and InstallProtocolInterface to inspect protocol GUID requests.
- NVRAM Variable Service Virtualization: Emulate GetVariable and SetVariable calls to audit parsing of NVRAM attributes and buffers.
- SMM Handler Auditing: Emulate System Management Interrupt (SMI) handlers to detect SMM memory corruption and Call-Out vulnerabilities.
- Zero-Hardware Triage: Analyze extracted motherboard BIOS firmware modules directly in Python on your analysis workstation.

💻 Implementation Example
#!/usr/bin/env python3
"""
Post 12: UEFI DXE & SMM Firmware Emulation & Vulnerability Research
Loading an extracted UEFI `.efi` driver module, hooking `LocateProtocol` and `GetVariable`.
"""

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

# Mock UEFI NVRAM variables storage
MOCK_UEFI_NVRAM = {
"AdminPasswordAuth": b"\x01\x00\x00\x00\xDE\xAD\xBE\xEF",
"SecureBootSetup": b"\x01",
"CustomSecurityConfig": b"A" * 64
}

def hook_uefi_GetVariable(ql: Qiling) -> int:
var_name_ptr = ql.os.function_arg(0)
vendor_guid_ptr = ql.os.function_arg(1)
attributes_ptr = ql.os.function_arg(2)
data_size_ptr = ql.os.function_arg(3)
data_buf_ptr = ql.os.function_arg(4)

# Read UTF-16LE variable name
var_name = ql.os.utils.read_wstring(var_name_ptr)
print(f"[UEFI EFI_GET_VARIABLE] Querying variable: '{var_name}'")

if var_name in MOCK_UEFI_NVRAM:
val = MOCK_UEFI_NVRAM[var_name]
# Write mock variable data into guest buffer
ql.mem.write(data_buf_ptr, val)
# EFI_SUCCESS = 0
return 0
else:
print(f" [-] Variable '{var_name}' not found -> EFI_NOT_FOUND (0x8000000e)")
# EFI_NOT_FOUND = 0x800000000000000E (64-bit EFI status)
return 0x800000000000000E

def run_uefi_sandbox(efi_driver_path: str, rootfs_path: str) -> None:
print(f"[*] Initializing UEFI DXE Driver Sandbox for {efi_driver_path}...")
ql = Qiling(
argv=[efi_driver_path],
rootfs=rootfs_path,
ostype=QL_OS.UEFI,
archtype=QL_ARCH.X8664,
verbose=QL_VERBOSE.DEFAULT
)

# Intercept UEFI Runtime Service: GetVariable
ql.os.set_api("GetVariable", hook_uefi_GetVariable)

print("[*] Starting UEFI driver execution...")
try:
ql.run()
except Exception as err:
print(f"[*] Driver execution completed or yielded: {err}")

if __name__ == "__main__":
TARGET_EFI = "rootfs/x8664_efi/bin/SampleDxeDriver.efi"
ROOTFS_EFI = "rootfs/x8664_efi"
run_uefi_sandbox(TARGET_EFI, ROOTFS_EFI)

🔥 Use Cases
- Auditing proprietary motherboard UEFI DXE drivers for memory corruption and buffer overflows.
- Detecting UEFI rootkits (e.g., CosmicStrand, MoonBounce, BlackLotus) in isolated sandboxes.
- Fuzzing NVRAM variable parsers and UEFI capsule update processing modules.
- Auditing System Management Mode (SMM) communication buffers for SMI Call-Out vulnerabilities.
- Automating firmware security reviews for enterprise hardware certification.

⚠️ Caveats & Responsible
- RootFS Layout: UEFI emulation requires standard UEFI rootfs structures containing core EFI protocol definitions (rootfs/x8664_efi).
- EFI Return Statuses: 64-bit UEFI status codes use the high bit (0x8000000000000000) for errors; ensure return codes match EFI specifications.
- Protocol GUID Registration: If a driver requires custom hardware protocols, register mock interfaces using ql.os.install_protocol().
- Architecture: Most modern desktop/server UEFI drivers are X8664, but ARM64 UEFI is increasingly common in mobile and server SoCs.

📦 Test Data & Sample
- Target Architecture RootFS
- Sample UEFI DXE Drivers
- UEFI Protocols Table

🔗 Resources
- Qiling UEFI Emulation Guide
- UEFI Specification

#Qiling #UEFI #FirmwareSecurity #SMM #ReverseEngineering #HardwareSecurity #Bootkit #CyberSecurity
1
⚡️ 📦 Automated Unpacking & OEP Memory Dumper with Qiling (Python practice)

Packers, crypters, and protectors (such as UPX, custom XOR loaders, or commercial packers) conceal the original code by decrypting executable sections into newly allocated memory at runtime before transferring control to the Original Entry Point (OEP) via a tail jump. Static unpackers break whenever packers undergo minor revisions. Qiling allows you to build generic, dynamic unpackers by placing instruction hooks that monitor program counter transitions between memory regions and dumping fully unpacked PE/ELF memory images automatically upon reaching the OEP.

🧠 Core Concept
- Dynamic Tail Jump Detection: Monitor execution transitions from unpacker stub memory addresses into the newly decrypted payload address range.
- Instruction-Level Code Hooking (ql.hook_code()): Inspect every executed assembly instruction, current program counter (PC), and target branch addresses.
- Automated OEP Identification: Detect when the execution leaves unpacker stub memory blocks and enters the main code section (.text).
- Live Process Image Dumping: Read and reconstruct decrypted memory sections using ql.mem.read() directly into a clean executable file on disk.
- Zero Anti-Unpacking Evasion: Bypass anti-dumping tricks, timing checks, and debugger detections with Qiling's isolated emulation.

💻 Implementation Example
#!/usr/bin/env python3
"""
Post 13: Automated Unpacking & OEP Memory Dumper
Emulating a packed binary, detecting the tail jump to unpacked code section, and dumping memory at OEP.
"""

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

# Known boundaries of unpacker stub vs target code section
UNPACKER_STUB_BASE = 0x408000
UNPACKER_STUB_END = 0x40A000
TARGET_CODE_BASE = 0x401000
TARGET_CODE_END = 0x406000

dumped = False

def detect_oep_and_dump(ql: Qiling, address: int, size: int) -> None:
global dumped

# Check if execution has transitioned from unpacker stub into main code section
if not dumped and TARGET_CODE_BASE <= address < TARGET_CODE_END:
print("=" * 60)
print(f"[!] OEP REACHED at address: 0x{address:08x}!")
print(f" Instruction size: {size} bytes")

# Read the newly decrypted .text section from memory
unpacked_code_size = TARGET_CODE_END - TARGET_CODE_BASE
unpacked_bytes = ql.mem.read(TARGET_CODE_BASE, unpacked_code_size)

# Dump the clean unpacked section to disk
output_file = "unpacked_text_section.bin"
with open(output_file, "wb") as f:
f.write(unpacked_bytes)

print(f"[+] Successfully dumped {len(unpacked_bytes)} bytes of unpacked code to '{output_file}'")
print("=" * 60)

dumped = True
# Stop emulation now that unpacking is complete
ql.stop()

def run_unpacking_engine(packed_binary: str, rootfs_path: str) -> None:
print(f"[*] Loading packed binary: {packed_binary}...")
ql = Qiling([packed_binary], rootfs_path, ostype=QL_OS.LINUX, archtype=QL_ARCH.X8664, verbose=QL_VERBOSE.DISABLED)

# Hook every instruction execution
ql.hook_code(detect_oep_and_dump)

print("[*] Emulating unpacker routine...")
try:
ql.run()
except Exception as err:
print(f"[*] Emulation completed: {err}")

if __name__ == "__main__":
TARGET = "rootfs/x8664_linux/bin/packed_sample_elf"
ROOTFS = "rootfs/x8664_linux"
run_unpacking_engine(TARGET, ROOTFS)

🔥 Use Cases
- Unpacking unknown malware droppers and ransomware stubs automatically in triage pipelines.
- Defeating custom XOR/RC4/AES loaders without manually reversing decoding loops in IDA.
- Extracting clean, decompilable ELF/PE binaries for Ghidra or Binary Ninja static analysis.
- Detecting multi-stage loaders that decrypt subsequent layers into dynamic heap memory.
- Generating clean signatures (YARA rules, hashes) from the genuine unpacked payload core.

⚠️ Caveats & Responsible
- Performance with Instruction Hooks: hook_code() adds execution overhead; filter the hook address range if the unpacker stub boundaries are known.
- IAT Reconstruction: Dynamic memory dumps contain resolved API pointers; for complete standalone execution, IAT / relocation fixup may be required (e.g., using pefile or Scylla).
- Self-Modifying Pages: Ensure memory protection flags (PROT_WRITE | PROT_EXEC) allow in-place modification during unpacking.
- Multi-Layer Packers: For multi-stage crypters, maintain a state counter to dump each subsequent layer upon transition.

📦 Test Data & Sample
- Target Architecture RootFS
- Packed Sample Binaries
- UPX Reference Repository

🔗 Resources
- Qiling Hooking API
- Unpacking Concepts Guide

#Qiling #Unpacking #ReverseEngineering #MalwareAnalysis #OEP #BinaryAnalysis #CyberSecurity #AppSec
⚡️ 🔓 Dynamic String Decryption & Malware Config Extraction (Python practice)

Modern malware families (e.g., Cobalt Strike, Emotet, Qakbot, LockBit) rarely leave command-and-control (C2) domains, encryption keys, or API names in plaintext. Instead, they utilize custom string decryption routines (stack strings, rolling XOR, RC4, or custom substitution ciphers) invoked hundreds of times throughout the binary. Rather than manually reimplementing these proprietary algorithms in Python, Qiling allows you to emulate only the target decryption subroutine directly inside the binary and extract all decrypted strings dynamically.

🧠 Core Concept
- Targeted Function Emulation: Jump directly into a specific internal subroutine address without executing the surrounding malware logic.
- Automated Iteration over Ciphertext Tables: Pass encrypted buffers and keys sequentially into the emulated decryption function.
- Direct Memory Buffer Extraction: Read decrypted plaintext bytes directly from the return register or output buffer pointer in memory.
- Zero Algorithm Reimplementation: No need to spend hours reversing complex assembly math; let the binary decrypt its own strings in Qiling.
- Batch Configuration Dumping: Extract hundreds of obfuscated strings (C2 URLs, user-agents, registry keys) in seconds.

💻 Implementation Example
#!/usr/bin/env python3
"""
Post 14: Dynamic String Decryption & Malware Config Extraction
Emulating an internal string decryption routine at address 0x401820 and extracting all decrypted strings.
"""

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

# Known encrypted string offsets in binary and their lengths
ENCRYPTED_STRING_RECORDS = [
{"offset": 0x406020, "length": 24, "id": "C2_Primary"},
{"offset": 0x406040, "length": 18, "id": "C2_Backup"},
{"offset": 0x406060, "length": 32, "id": "AES_Key_Init"},
{"offset": 0x406090, "length": 45, "id": "UserAgent_String"},
]

def extract_decrypted_malware_strings(binary_path: str, rootfs_path: str) -> None:
ql = Qiling([binary_path], rootfs_path, ostype=QL_OS.LINUX, archtype=QL_ARCH.X8664, verbose=QL_VERBOSE.DISABLED)

# Internal decryption function boundaries: decrypt_string(char *enc_data, int len, char *out_buf)
DECRYPT_FUNC_ENTRY = 0x401820
DECRYPT_FUNC_EXIT = 0x401895
OUTPUT_BUFFER_ADDR = 0x7FFF8000 # Scratch buffer for decrypted output

decrypted_config = {}

print(f"[*] Starting targeted emulation of string decryptor @ 0x{DECRYPT_FUNC_ENTRY:08x}...")

for record in ENCRYPTED_STRING_RECORDS:
enc_addr = record["offset"]
enc_len = record["length"]
label = record["id"]

# Set up function arguments according to System V AMD64 ABI:
# RDI = encrypted_buffer_ptr, RSI = length, RDX = output_buffer_ptr
ql.arch.regs.rdi = enc_addr
ql.arch.regs.rsi = enc_len
ql.arch.regs.rdx = OUTPUT_BUFFER_ADDR

# Set stack pointer and return address
ql.arch.regs.rsp = 0x7FFFF000

# Execute only the decryption function
ql.run(begin=DECRYPT_FUNC_ENTRY, end=DECRYPT_FUNC_EXIT)

# Read decrypted plaintext string from output buffer
decrypted_bytes = ql.mem.read(OUTPUT_BUFFER_ADDR, enc_len)
# Strip null bytes and decode
plaintext = decrypted_bytes.split(b"\x00")[0].decode("latin-1")

decrypted_config[label] = plaintext
print(f" [+] Decrypted [{label}]: '{plaintext}'")

print("=" * 60)
print("[*] Complete Extracted Configuration:")
for k, v in decrypted_config.items():
print(f" {k.ljust(20)}: {v}")

if __name__ == "__main__":
TARGET = "rootfs/x8664_linux/bin/malware_obfuscated"
ROOTFS = "rootfs/x8664_linux"
extract_decrypted_malware_strings(TARGET, ROOTFS)

🔥 Use Cases
- Extracting high-fidelity Indicators of Compromise (IOCs) from evasive malware families.
- Decrypting dynamic API hashing tables and imported function strings.
- Recovering embedded encryption keys and IVs used for payload staging or ransomware.
- Automating threat intelligence feeds by batch-processing thousands of daily malware submissions.
- Accelerating manual reverse engineering by resolving string annotations before loading into IDA.

⚠️ Caveats & Responsible Practice
- Global State Initialization: If the decryption function relies on a global S-Box or key table initialized during binary startup, run the binary up to the initialization point first before snapshotting.
- Calling Conventions: Verify target architecture calling conventions (e.g., x86 fastcall / stdcall / cdecl vs x86_64 ABI).
- Stack Cleanliness: Reset the stack pointer (RSP/ESP) between function calls to prevent stack collisions.
- End Address: Ensure the end parameter marks the exact RET instruction of the target function.

📦 Test Data & Sample Binaries
- Target Architecture RootFS
- Obfuscated Sample Binaries
- String Decryptor Harness

🔗 Resources
- Qiling Function Emulation Guide
- Malware Config Extraction Techniques

#Qiling #MalwareAnalysis #ConfigExtraction #ReverseEngineering #ThreatIntel #Deobfuscation #CyberSecurity #Python
⚡️ 🔍 Fine-Grained Memory Access Tracing: Watchpoints & Taint Tracking (Python practice)

Tracking how sensitive data (such as cryptographic keys, credentials, or untrusted network input) flows through memory is fundamental to both vulnerability research and malware analysis. Hardware debuggers are typically limited to only 4 hardware watchpoint registers, making full-range memory monitoring impossible. Qiling provides software-level memory access hooks (ql.hook_mem_read(), ql.hook_mem_write(), and ql.hook_mem_unmapped()) across unlimited memory ranges without performance penalties from page-fault exceptions.

🧠 Core Concept
- Unlimited Memory Watchpoints: Monitor reads and writes across arbitrary address ranges with precise byte-level granularity.
- Data-Flow & Taint Inspection: Identify every assembly instruction that accesses, reads, or modifies a monitored variable or key buffer.
- Detecting Memory Corruption: Intercept out-of-bounds writes, heap overflow corruption, and use-after-free conditions in real time.
- Unmapped Memory Fault Trapping (hook_mem_unmapped): Catch wild pointer dereferences and analyze exploit crashes before segmentation faults kill the processCall-Stack Reconstructionon**: Capture register state and return addresses whenever sensitive memory regions are accessed.

💻 Implementation Example
#!/usr/bin/env python3
"""
Post 15: Fine-Grained Memory Access Tracing
Attaching memory write hooks to sensitive memory regions and logging every instruction modifying crypto keys.
"""

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

# Address range of the sensitive cryptographic key in memory
KEY_BUFFER_BASE = 0x603000
KEY_BUFFER_SIZE = 32 # 256-bit AES key

def hook_key_write_watchpoint(ql: Qiling, access: int, addr: int, size: int, value: int) -> None:
pc = ql.arch.regs.arch_pc

# Read the instruction bytes at current PC for disassembly reference
ins_bytes = ql.mem.read(pc, 4)
print(f"[WATCHPOINT TRIGGERED] Write Access at 0x{addr:08x} (Size: {size} bytes, Val: 0x{value:x})")
print(f" -> Instruction PC: 0x{pc:08x} | Opcode: {ins_bytes.hex()}")
print(f" -> Current Stack Pointer (SP): 0x{ql.arch.regs.arch_sp:08x}")
print(f" -> General Registers: R0=0x{ql.arch.regs.r0:x}, R1=0x{ql.arch.regs.r1:x}")

def hook_unmapped_access_handler(ql: Qiling, access: int, addr: int, size: int, value: int) -> bool:
print(f"[!] MEMORY FAULT: Illegal unmapped access at address: 0x{addr:08x} from PC: 0x{ql.arch.regs.arch_pc:08x}")
# Returning False halts emulation; returning True allows custom mapping recovery
return False

def run_memory_tracing_sandbox(binary_path: str, rootfs_path: str) -> None:
print(f"[*] Initializing Memory Watchpoint Sandbox for {binary_path}...")
ql = Qiling([binary_path], rootfs_path, ostype=QL_OS.LINUX, archtype=QL_ARCH.ARM, verbose=QL_VERBOSE.DISABLED)

# Hook memory writes specifically on our target key address range
ql.hook_mem_write(hook_key_write_watchpoint, begin=KEY_BUFFER_BASE, end=KEY_BUFFER_BASE + KEY_BUFFER_SIZE)

# Hook unmapped memory faults to catch buffer overflows and crashes
ql.hook_mem_unmapped(hook_unmapped_access_handler)

print("[*] Running binary with active memory watchpoints...")
try:
ql.run()
except Exception as err:
print(f"[*] Execution finished: {err}")

if __name__ == "__main__":
TARGET = "rootfs/arm_linux/bin/crypto_manager"
ROOTFS = "rootfs/arm_linux"
run_memory_tracing_sandbox(TARGET, ROOTFS)

🔥 Use Cases
- Taint analysis: Tracking untrusted network input from recv() through internal processing buffers.
- Catching subtle Use-After-Free (UAF) and Out-Of-Bounds (OOB) memory corruption vulnerabilities.
- Identifying which cryptographic subroutines read or modify master encryption keys.
- Auditing proprietary binary license validation routines to pinpoint validation flag addresses.
- Debugging dangling pointers and memory leaks in cross-compiled embedded firmware.

⚠️ Caveats & Responsible
- Callback Performance: Narrow down the begin and end address boundaries to minimize hook invocation overhead.
- Write Values: In memory write hooks, the value argument represents the raw integer value being written to memory.
- Multi-byte Writes: A single SIMD or 64-bit store instruction may trigger a write of 8, 16, or 32 bytes.
- Fault Recovery: If hook_mem_unmapped returns True, you must map valid memory at that address via ql.mem.map() before returning.

📦 Test Data & Sample
- Target Architecture RootFS
- Sample Crypto Manager
- Memory Hooks Reference

🔗 Resources
- Qiling Memory Hook API
- Unicorn Engine Hook Documentation

#Qiling #MemoryWatchpoint #TaintAnalysis #ReverseEngineering #VulnerabilityResearch #ExploitDev #CyberSecurity #Python
⚡️ 🔌 Hardware MMIO & Peripheral Emulation with Qiling (Python practice)

Bare-metal firmware (e.g., ARM Cortex-M, automotive ECUs, industrial PLCs) interacts directly with physical hardware peripherals by reading and writing Memory-Mapped I/O (MMIO) register addresses. In standard emulators, the first time firmware queries a UART status register, hardware timer, or GPIO pin, it hangs in an infinite polling loop waiting for hardware flags that never change. Qiling provides ql.mem.map_mmio(), allowing researchers to map virtual peripheral address spaces and attach Python read/write callback handlers to simulate real hardware.

🧠 Core Concept
- MMIO Virtualization (ql.mem.map_mmio()): Map physical microcontroller address spaces (e.g., 0x40000000 - 0x40010000) with Python callback dispatchers.
- UART Serial Port Simulation: Intercept transmitted characters and feed mock serial commands to the firmware's input buffer.
- Hardware Register Status Spoofing: Return expected status bits (e.g., UART_TX_READY, TIMER_EXPIRED, PLL_LOCKED) to unblock hardware initialization loops.
- Interrupt Injection: Trigger simulated hardware interrupts (IRQs) upon peripheral timer expiration or packet arrival.
- Hardware-Independent Firmware Auditing: Run real IoT / automotive firmware binaries on standard x86 workstations without hardware rigs.

💻 Implementation Example
#!/usr/bin/env python3
"""
Post 16: Hardware MMIO & Peripheral Emulation
Emulating an ARM Cortex-M micro-controller UART peripheral and watchdog timer MMIO range.
"""

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

# Simulated Hardware Register Offsets for UART Peripheral (Base: 0x4000C000)
UART_DR = 0x00 # Data Register (Read RX / Write TX)
UART_FR = 0x18 # Flag Register (Status bits)
UART_IBRD = 0x24 # Baud Rate Register

# Flag Register bitmask constants
UART_FR_TXFF = (1 << 5) # Transmit FIFO Full
UART_FR_RXFE = (1 << 4) # Receive FIFO Empty
UART_FR_TXFE = (1 << 7) # Transmit FIFO Empty

def uart_mmio_read(ql: Qiling, offset: int, size: int) -> int:
# Firmware is checking UART status flag register
if offset == UART_FR:
# Return TXFE (Transmit FIFO Empty) so firmware knows UART is ready to transmit
return UART_FR_TXFE
elif offset == UART_DR:
# Simulate an incoming character 'K' from serial console
print("[MMIO UART] Firmware read received byte: 'K'")
return ord("K")
return 0

def uart_mmio_write(ql: Qiling, offset: int, size: int, value: int) -> None:
if offset == UART_DR:
char_val = chr(value & 0xFF)
print(f"[MMIO UART TX] Firmware transmitted: '{char_val}' (0x{value:02x})")
elif offset == UART_IBRD:
print(f"[MMIO UART CONFIG] Baud rate divisor set to: {value}")

def run_cortex_m_mmio_sandbox(binary_path: str) -> None:
print(f"[*] Initializing Bare-Metal ARM Cortex-M Sandbox for {binary_path}...")
ql = Qiling(
argv=[binary_path],
rootfs="", # Bare-metal: no Linux rootfs needed
ostype=QL_OS.LINUX,
archtype=QL_ARCH.ARM,
verbose=QL_VERBOSE.DEFAULT
)

# Map UART Peripheral MMIO range: 0x4000C000 (Size 4KB)
UART_BASE = 0x4000C000
MMIO_SIZE = 0x1000

ql.mem.map_mmio(UART_BASE, MMIO_SIZE, uart_mmio_read, uart_mmio_write, info="[Virtual_UART0]")
print(f"[+] Mapped Virtual UART MMIO at 0x{UART_BASE:08x}")

print("[*] Running bare-metal firmware with active MMIO emulation...")
try:
ql.run(count=100000) # Execute 100k instructions
except Exception as err:
print(f"[*] Emulation boundary: {err}")

if __name__ == "__main__":
TARGET = "rootfs/arm_baremetal/firmware.bin"
run_cortex_m_mmio_sandbox(TARGET)

🔥 Use Cases
- Emulating automotive CAN bus controllers and engine management firmware (ECUs).
- Overcoming endless register polling loops during IoT device boot sequences.
- Simulating industrial Modbus/Profibus fieldbus controller peripherals.
- Auditing embedded hardware crypto engines (AES/SHA accelerators) via MMIO registers.
- Fuzzing bare-metal RTOS drivers with mutated hardware response packets.

⚠️ Caveats & Responsible
- Page Alignment: MMIO base addresses must align to 4096-byte (4KB) boundaries.
- Register Widths: Firmware might read MMIO registers using 8-bit, 16-bit, or 32-bit instructions; ensure callbacks handle varying size parameters.
- Unimplemented Offsets: Log unhandled register offsets to identify missing peripheral features when firmware fails to initialize.
- Bare-Metal Memory: For raw binary dumps without ELF headers, manually map RAM and Flash regions using ql.mem.map() before starting.

📦 Test Data & Sample
- Target Architecture RootFS: Bare-Metal Firmware (Zero OS RootFS required)
- Sample Microcontroller Firmware
- MMIO Emulation Engine

🔗 Resources
- Qiling MMIO API Reference
- ARM Cortex-M Memory Map Specification

#Qiling #MMIO #HardwareEmulation #BareMetal #ARM #CortexM #FirmwareSecurity #EmbeddedSystems
❤1
⚡️ 🧵 Multi-Threading & Thread Emulation in Qiling (Python practice)

Real-world network daemons, database engines, and complex malware families heavily utilize multi-threading (POSIX pthread_create on Linux, CreateThread on Windows). Emulating multi-threaded binaries inside a CPU emulator is notoriously difficult due to thread-local storage (TLS), atomic synchronization primitives (futex, mutexes, semaphores), and context switching. Qiling incorporates a full user-space thread scheduler that manages thread state transitions, stack allocation, and concurrency synchronization seamlessly.

🧠 Core Concept
- Cooperative & Preemptive Thread Scheduling: Qiling maintains thread pools, scheduling execution slices between concurrent worker threads.
- Thread Local Storage (TLS): Automatically sets up architecture-specific thread pointer registers (FS/GS on x86, TPIDR_EL0 on ARM64).
- POSIX & Win32 Thread Interception: Hook thread creation functions to monitor worker thread entry points, thread IDs, and parameter structures.
- Mutex & Synchronization Tracing: Intercept locking primitives (pthread_mutex_lock, WaitForSingleObject) to debug deadlocks and race conditions.
- Thread-Specific Breakpoints: Attach instruction and API hooks targeted specifically at individual worker threads.

💻 Implementation Example
#!/usr/bin/env python3
"""
Post 17: Multi-Threading & Thread Emulation in Qiling
Emulating a multi-threaded Linux binary, tracing mutex locks, and intercepting worker thread execution.
"""

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

def hook_pthread_create(ql: Qiling) -> int:
thread_ptr = ql.os.function_arg(0)
attr_ptr = ql.os.function_arg(1)
start_routine = ql.os.function_arg(2)
arg_ptr = ql.os.function_arg(3)

print(f"[THREAD CREATION] pthread_create() -> Worker Function: 0x{start_routine:08x}, Context Arg: 0x{arg_ptr:08x}")
# Let Qiling's native pthread manager handle the thread creation
return ql.os.posix.pthread.pthread_create(thread_ptr, attr_ptr, start_routine, arg_ptr)

def hook_pthread_mutex_lock(ql: Qiling) -> int:
mutex_addr = ql.os.function_arg(0)
current_tid = getattr(ql.os.thread_management, "cur_thread", None)
print(f"[MUTEX LOCK] Thread TID={current_tid} acquired mutex at 0x{mutex_addr:08x}")
return 0 # SUCCESS

def run_multithreaded_sandbox(binary_path: str, rootfs_path: str) -> None:
print(f"[*] Initializing Multi-Threaded Sandbox for {binary_path}...")
ql = Qiling(
argv=[binary_path],
rootfs=rootfs_path,
ostype=QL_OS.LINUX,
archtype=QL_ARCH.X8664,
verbose=QL_VERBOSE.DEFAULT
)

# Intercept pthread management calls
ql.os.set_api("pthread_create", hook_pthread_create)
ql.os.set_api("pthread_mutex_lock", hook_pthread_mutex_lock)

print("[*] Starting multi-threaded binary execution...")
try:
ql.run()
print("[+] All threads completed successfully.")
except Exception as err:
print(f"[-] Execution stopped: {err}")

if __name__ == "__main__":
TARGET = "rootfs/x8664_linux/bin/multithread_worker"
ROOTFS = "rootfs/x8664_linux"
run_multithreaded_sandbox(TARGET, ROOTFS)

🔥 Use Cases
- Analyzing botnets with dedicated communication, DDoS, and scanner background worker threads.
- Hunting race conditions and Time-of-Check to Time-of-Use (TOCTOU) bugs in concurrent daemons.
- Auditing thread synchronization logic and critical section locks in financial/banking binaries.
- Stepping through worker thread payload decryptors in ransomware samples.
- Simulating high-concurrency embedded network daemons without kernel overhead.

⚠️ Caveats & Responsible Practice
- Deterministic Execution: By default, Qiling uses cooperative thread scheduling; thread execution order is deterministic, simplifying crash reproduction.
- Stack Allocation: Each created thread allocates its own virtual stack; monitor memory usage in binaries spawning hundreds of threads.
- Blocking Syscalls: Infinite sleep loops in background threads should be stubbed out via ql.os.set_api('sleep', ...) to prevent emulation stalls.
- Architecture Differences: Windows threads use different structures (TEB/Fiber); use QL_OS.WINDOWS for Win32 threading.

📦 Test Data & Sample Binaries
- Target Architecture RootFS
- Multi-Threaded Test Binaries
- Thread Management Engine

🔗 Resources
- Qiling Thread Management Docs
- POSIX Threads Programming

#Qiling #MultiThreading #Pthreads #ReverseEngineering #BinaryAnalysis #Concurrency #CyberSecurity #Python
⚡️ 🕵️ Bypassing Anti-Analysis, Anti-VM & Timing Checks (Python practice)

Advanced malware, commercial packers, and digital rights management (DRM) protections incorporate aggressive anti-analysis routines. They measure CPU cycle counters (RDTSC), inspect processor feature registers (CPUID), examine Linux /proc/self/status for TracerPid, or query Windows PEB flags (BeingDebugged). Running these samples in standard debuggers leads to immediate evasion or deceptive execution paths. Qiling provides complete, transparent control over CPU instructions and OS environment queries, rendering evasion tactics useless.

🧠 Core Concept
- Defeating CPU Time-Deltas (RDTSC): Hook the RDTSC instruction to return predictable, monotonically incrementing cycle counts, preventing time-delta detection.
- CPUID Spoofing: Intercept CPUID instruction callbacks to return genuine Intel/AMD CPU vendor strings instead of hypervisor signatures (e.g., 'VMwareVMware', 'KVMKVMKVM').
- PEB BeingDebugged Patching: Initialize Windows PEB structures with BeingDebugged = 0 and NtGlobalFlag = 0.
- Linux Anti-Debug Defeat: Intercept /proc/self/status reads and force TracerPid: 0 regardless of attached analysis harnesses.
- Hardware Breakpoint Register Masking: Neutralize debug register queries (DR0-DR7) to conceal inspection hooks.

💻 Implementation Example
#!/usr/bin/env python3
"""
Post 18: Bypassing Anti-Analysis, Anti-VM & Timing Checks
Creating a comprehensive anti-anti-analysis hook suite in Qiling that transparently neutralizes evasion tactics.
"""

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

# Monotonic cycle counter to defeat RDTSC timing checks
fake_cycle_counter = 1000

def hook_rdtsc(ql: Qiling) -> None:
global fake_cycle_counter
fake_cycle_counter += 150 # Increment by a small, realistic instruction delta

# RDTSC returns low 32-bits in EAX, high 32-bits in EDX
eax_val = fake_cycle_counter & 0xFFFFFFFF
edx_val = (fake_cycle_counter >> 32) & 0xFFFFFFFF

ql.arch.regs.rax = eax_val
ql.arch.regs.rdx = edx_val
print(f"[ANTI-ANALYSIS BYPASS] Spoofed RDTSC cycle count: {fake_cycle_counter}")

def hook_is_debugger_present(ql: Qiling) -> int:
print("[ANTI-ANALYSIS BYPASS] IsDebuggerPresent() intercepted -> Returning 0 (FALSE)")
return 0

def hook_check_remote_debugger(ql: Qiling) -> int:
hProcess = ql.os.function_arg(0)
pbDebuggerPresent_ptr = ql.os.function_arg(1)

print("[ANTI-ANALYSIS BYPASS] CheckRemoteDebuggerPresent() intercepted -> Setting FALSE")
# Write 0 (FALSE) to the output pointer
ql.mem.write(pbDebuggerPresent_ptr, b"\x00\x00\x00\x00")
return 1 # Returns non-zero for API success

def setup_stealth_sandbox(binary_path: str, rootfs_path: str) -> None:
print(f"[*] Initializing Stealth Sandbox for {binary_path}...")
ql = Qiling([binary_path], rootfs_path, ostype=QL_OS.WINDOWS, archtype=QL_ARCH.X86, verbose=QL_VERBOSE.DEFAULT)

# 1. Hook Win32 Anti-Debug APIs
ql.os.set_api("IsDebuggerPresent", hook_is_debugger_present)
ql.os.set_api("CheckRemoteDebuggerPresent", hook_check_remote_debugger)

# 2. Hook RDTSC instruction
ql.hook_insn(hook_rdtsc, "rdtsc")

print("[*] Running evasive malware with full anti-analysis countermeasures active...")
ql.run()

if __name__ == "__main__":
TARGET = "rootfs/x86_windows/bin/evasive_malware.exe"
ROOTFS = "rootfs/x86_windows"
setup_stealth_sandbox(TARGET, ROOTFS)

🔥 Use Cases
- Unmasking evasive malware that terminates or sleeps when detecting virtualized environments.
- Bypassing commercial software protectors (VMProtect, Themida) anti-debug checks.
- Defeating timing-based crackmes and anti-instrumentation CTF challenges.
- Extracting malware payloads that only activate in genuine physical host environments.
- Auditing security software resilience against advanced evasion techniques.

⚠️ Caveats & Responsible Practice
- Instruction Hook Support: ql.hook_insn() depends on Unicorn's instruction hook dispatcher; verify your Unicorn version supports target instruction hooks.
- Complex CPUID Leaf Queries: When spoofing CPUID, handle multiple leaf indices (EAX=0, EAX=1, EAX=0x40000000) appropriately.
- PEB Direct Access: Malware accessing the PEB via inline assembly (mov eax, fs:[0x30]) bypasses Win32 APIs; ensure Qiling's virtual PEB is patched directly.
- Exception-Based Anti-Debug: Be prepared to handle Structured Exception Handling (SEH) traps like INT 3 or INT 2D.

📦 Test Data & Sample Binaries
- Target Architecture RootFS
- Evasive Malware Test Binaries
- Instruction Hook Engine

🔗 Resources
- Qiling Instruction Hooking
- The Ultimate Anti-Debugging Reference

#Qiling #AntiDebug #AntiVM #MalwareAnalysis #Evasion #ReverseEngineering #CyberSecurity #ThreatIntel
⚡️ 🍏 macOS Mach-O Binary Emulation & Apple LibSystem Interception (Python practice)

Analyzing macOS malware, proprietary Apple Silicon (ARM64) CLI tools, or Objective-C/C++ Mach-O binaries typically requires dedicated macOS hardware or complex Hackintosh virtual machines. Qiling supports macOS Mach-O binary execution on Linux or Windows hosts. It parses Mach-O headers, loads dynamic libraries via its simulated dynamic linker (dyld), maps __TEXT and __DATA segments, and emulates core BSD syscalls and Apple libSystem APIs.

🧠 Core Concept
- Cross-Platform Mach-O Loading: Execute 64-bit x86_64 and ARM64 Apple Mach-O binaries on any standard Linux or Windows workstation.
- Apple dyld & Segment Mapping: Simulates Apple's dynamic linker, binding symbols and setting up __PAGEZERO, __TEXT, and __DATA segments.
- BSD Syscall & LibSystem Emulation: Intercepts macOS-specific system calls (e.g., sysctlbyname, proc_pidinfo, csr_get_active_config).
- Objective-C Runtime Inspection: Intercept calls to objc_msgSend to trace method selectors, class names, and object parameters.
- SIP & Sandbox Security Auditing: Emulate system integrity protection (SIP) checks and evaluate macOS malware behavior in an isolated environment.

💻 Implementation Example
#!/usr/bin/env python3
"""
Post 19: macOS Mach-O Binary Emulation & Apple LibSystem Interception
Emulating an ARM64/x86_64 macOS CLI binary, hooking `sysctlbyname` and inspecting security parameters.
"""

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

def hook_sysctlbyname(ql: Qiling) -> int:
name_ptr = ql.os.function_arg(0)
oldp_ptr = ql.os.function_arg(1)
oldlenp_ptr = ql.os.function_arg(2)

# Read queried sysctl property string
query_name = ql.os.utils.read_cstring(name_ptr)
print(f"[MACOS API] sysctlbyname(name='{query_name}')")

# If binary is querying hardware model or security flags
if "hw.model" in query_name:
fake_model = b"MacBookPro18,1\x00"
if oldp_ptr != 0:
ql.mem.write(oldp_ptr, fake_model)
return 0 # KERN_SUCCESS
elif "kern.osversion" in query_name:
fake_ver = b"21G115\x00"
if oldp_ptr != 0:
ql.mem.write(oldp_ptr, fake_ver)
return 0

return 0

def run_macho_sandbox(macho_binary: str, rootfs_path: str) -> None:
print(f"[*] Initializing macOS Mach-O Sandbox for {macho_binary}...")
ql = Qiling(
argv=[macho_binary],
rootfs=rootfs_path,
ostype=QL_OS.MACOS,
archtype=QL_ARCH.X8664,
verbose=QL_VERBOSE.DEFAULT
)

# Intercept Apple LibSystem APIs
ql.os.set_api("sysctlbyname", hook_sysctlbyname)

print("[*] Emulating macOS Mach-O execution...")
try:
ql.run()
except Exception as err:
print(f"[-] Mach-O execution ended: {err}")

if __name__ == "__main__":
TARGET_MACHO = "rootfs/x8664_macos/bin/macho_security_checker"
ROOTFS_MACOS = "rootfs/x8664_macos"
run_macho_sandbox(TARGET_MACHO, ROOTFS_MACOS)

🔥 Use Cases
- Analyzing macOS trojans, infostealers, and adware without maintaining dedicated Apple hardware.
- Inspecting Apple Silicon ARM64 binaries and command-line utilities cross-platform.
- Auditing macOS anti-evasion and SIP detection routines in red team / blue team operations.
- Extracting hardcoded C2 infrastructure and strings from compiled Mach-O executables.
- Fuzzing proprietary macOS command-line file format parsers in scalable Linux clusters.

⚠️ Caveats & Responsible Practice
- RootFS Libraries: macOS dynamic libraries must be present in the rootfs/x8664_macos directory for dynamic linking.
- Objective-C Heavy Binaries: For large GUI apps heavily reliant on Cocoa/AppKit, stub out GUI framework calls or focus on command-line/corelogic binaries.
- Calling Conventions: macOS on ARM64 follows Apple's specific ARM64 ABI calling convention variants.
- Mach Messages: Complex Mach IPC ports require custom message handlers if deeply exercised by the sample.

📦 Test Data & Sample
- Target Architecture RootFS
- Sample Mach-O Binaries
- macOS Dyld Simulation

🔗 Resources
- Qiling macOS Subsystem
- Apple Mach-O File Format Reference

#Qiling #MacOS #MachO #AppleSilicon #ReverseEngineering #MalwareAnalysis #CyberSecurity #ThreatIntel
⚡️ 💾 Legacy 16-bit DOS COM & Real-Mode MBR Emulation with Qiling (Python practice)

Retro-reversing, legacy industrial control software, master boot record (MBR) bootkits, and classic DOS crackmes operate in 16-bit Real Mode with segmented memory addressing (CS:IP, DS:DX) and BIOS/DOS software interrupts (INT 21h, INT 10h, INT 13h). Modern x64 operating systems have completely dropped 16-bit execution support (NTVDM). Qiling features a built-in 16-bit DOS / MBR emulation engine (QL_OSTYPE.DOS), allowing security researchers to execute and hook 16-bit .COM binaries and raw boot sectors with full Python control.

🧠 Core Concept
- 16-bit Real-Mode Architecture: Emulates x86 real-mode segment:offset memory calculation (Address = Segment * 16 + Offset).
- DOS Interrupt Interception (INT 21h): Intercept standard DOS API services (AH=09h display string, AH=0Ah buffered input, AH=3Dh file open).
- BIOS Disk & Video Services: Emulate INT 10h video routines and INT 13h raw sector read/write operations for MBR analysis.
- Zero Virtual Machine Setup: Run legacy 16-bit MS-DOS binaries directly inside a lightweight Python script without DOSBox or FreeDOS VM overhead.
- Direct Register & Memory Inspection: Read 16-bit registers (AX, BX, CX, DX, SI, DI, SP, BP) and memory segments easily.

💻 Implementation Example
#!/usr/bin/env python3
"""
Post 20: Legacy 16-bit DOS COM & Real-Mode MBR Emulation
Emulating a 16-bit DOS `.com` crackme, hooking `int 21h` handlers to inspect string I/O and display output.
"""

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

def hook_dos_int21(ql: Qiling) -> None:
# Read AH register (service function number)
ah = (ql.arch.regs.eax >> 8) & 0xFF
al = ql.arch.regs.eax & 0xFF

# AH = 0x09: Display $-terminated string at DS:DX
if ah == 0x09:
ds = ql.arch.regs.ds
dx = ql.arch.regs.edx & 0xFFFF
# Real-mode linear address calculation: Segment * 16 + Offset
linear_addr = (ds * 16) + dx

# Read string terminated by '$' (DOS convention)
raw_bytes = ql.mem.read(linear_addr, 128)
text = raw_bytes.split(b"$")[0].decode("ascii", errors="ignore")
print(f"[DOS INT 21h | AH=09h Print] '{text}'")

# AH = 0x4C: Terminate Process with Exit Code in AL
elif ah == 0x4C:
print(f"[DOS INT 21h | AH=4Ch Exit] Binary terminated with exit code: {al}")
ql.stop()

def run_dos_sandbox(com_file_path: str) -> None:
print(f"[*] Initializing 16-bit DOS Real-Mode Sandbox for {com_file_path}...")
ql = Qiling(
argv=[com_file_path],
rootfs="", # No Linux rootfs required for DOS COM files
ostype=QL_OS.DOS,
archtype=QL_ARCH.X86,
verbose=QL_VERBOSE.DEFAULT
)

# Hook software interrupt INT 0x21
ql.hook_intno(hook_dos_int21, 0x21)

print("[*] Starting 16-bit DOS emulation...")
try:
ql.run()
except Exception as err:
print(f"[*] Emulation ended: {err}")

if __name__ == "__main__":
TARGET_COM = "rootfs/dos/bin/crackme16.com"
run_dos_sandbox(TARGET_COM)

🔥 Use Cases
- Analyzing legacy 16-bit ransomware and wiper bootkits targeting Master Boot Records (MBRs).
- Solving retro-computing and 16-bit DOS reverse engineering CTF challenges.
- Auditing legacy SCADA and industrial automation utilities compiled for MS-DOS environments.
- Extracting hardcoded algorithms from historical software preservation archives.
- Teaching real-mode x86 assembly and segment:offset memory models in academic courses.

⚠️ Caveats & Responsible Practice
- Linear vs Segmented Address: Always convert Segment:Offset into a 20-bit linear physical address ((Segment << 4) + Offset) when reading guest memory.
- DOS Interrupt Conventions: Strings printed via INT 21h, AH=09h are terminated by the $ character, not null bytes.
- COM File Base: DOS .COM files are loaded at offset 0x0100 within their code segment with the Program Segment Prefix (PSP) occupying 0x0000 - 0x00FF.
- Stack Layout: 16-bit SP operations wrap around the 64KB segment boundary; ensure stack offsets do not overflow.

📦 Test Data & Sample
- Target Architecture RootFS
- Sample DOS COM Binaries
- DOS Interrupt Dispatcher

🔗 Resources
- Qiling DOS Subsystem
- Ralf Brown's Interrupt List

#Qiling #DOS #16Bit #RealMode #ReverseEngineering #RetroComputing #MBR #CyberSecurity
⚡️ 📊 Cross-Architecture Code Coverage Collection: drcov & Lighthouse (Python practice)

When analyzing binaries, developing exploits, or running directed fuzzers, knowing exactly which basic blocks and functions were executed is essential. In GUI disassemblers (IDA Pro, Ghidra, Binary Ninja), plugins like Lighthouse visualize this execution flow with color-coded coverage maps. Qiling includes high-performance basic block tracing (ql.hook_block()) and a built-in coverage extension capable of exporting standard DynamoRIO drcov coverage files across any supported CPU architecture.

🧠 Core Concept
- Basic Block Tracing (ql.hook_block()): Automatically tracks every executed basic block starting address and instruction length.
- Standard drcov File Generation: Exports coverage dumps directly compatible with IDA Pro Lighthouse, Ghidra, and Binary Ninja plugins.
- Cross-Architecture Coverage: Generate accurate coverage maps for ARM, MIPS, or PPC binaries without needing DynamoRIO or hardware tracing pins.
- Differential Coverage Analysis: Compare coverage dumps from different input test cases to isolate conditional branch triggers.
- Fuzzer Corpus Optimization: Identify unique code paths to minimize fuzzer testcase corpora efficiently.

💻 Implementation Example
#!/usr/bin/env python3
"""
Post 21: Cross-Architecture Code Coverage Collection (drcov / IDA Lighthouse)
Executing target binary, recording executed basic blocks, and exporting `.drcov` file for Ghidra/IDA.
"""

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

def collect_code_coverage(binary_path: str, rootfs_path: str, test_input: str, output_cov_file: str) -> None:
print(f"[*] Initializing Qiling coverage collection for {binary_path}...")
ql = Qiling(
argv=[binary_path, test_input],
rootfs=rootfs_path,
ostype=QL_OS.LINUX,
archtype=QL_ARCH.ARM,
verbose=QL_VERBOSE.DISABLED
)

# 1. Initialize Qiling Coverage Extension
cov = coverage.Coverage(ql)

# 2. Activate drcov output format
cov.activate(coverage.FORMAT_DRCOV)

print(f"[*] Executing binary with test input: '{test_input}'...")
try:
ql.run()
except Exception as err:
print(f"[*] Execution finished: {err}")

# 3. Dump coverage data to file
cov.dump(output_cov_file)
print(f"[+] Coverage successfully written to '{output_cov_file}'")
print(f"[+] You can now load '{output_cov_file}' in IDA Pro (Lighthouse plugin) or Ghidra!")

if __name__ == "__main__":
TARGET = "rootfs/arm_linux/bin/arm_parser"
ROOTFS = "rootfs/arm_linux"
OUTPUT_FILE = "coverage_trace.drcov"

collect_code_coverage(TARGET, ROOTFS, "TEST_PAYLOAD_ADMIN_AUTH", OUTPUT_FILE)

🔥 Use Cases
- Visualizing explored code paths and unreached functions in IDA Pro / Ghidra using Lighthouse.
- Evaluating unit test and fuzzing branch coverage across cross-compiled embedded binaries.
- Differential binary analysis: identifying which blocks execute during successful vs failed logins.
- Hunting dead code, unreferenced easter eggs, and hidden administrative backdoors.
- Optimizing fuzzing input corpora by eliminating redundant test cases that traverse identical paths.

⚠️ Caveats & Responsible Practice
- Base Address Synchronization: Ensure the base address in your IDA / Ghidra database matches the ASLR base or load address configured in Qiling.
- Performance: Basic block hooking introduces minimal overhead, but disable verbose logging (QL_VERBOSE.DISABLED) for maximum tracing speed.
- Coverage Filtering: You can filter coverage collection to only include the main binary module and ignore shared libraries (libc.so).
- drcov Header Format: The output .drcov file includes module tables and block lists adhering to DynamoRIO v2 specification.

📦 Test Data & Sample Binaries
- Target Architecture RootFS
- Coverage Dumps & Lighthouse Plugin
- Qiling Coverage Module

🔗 Resources
- Lighthouse IDA / Ghidra Plugin
- DynamoRIO drcov Format

#Qiling #CodeCoverage #Lighthouse #IDAPro #Ghidra #ReverseEngineering #Fuzzing #CyberSecurity
⚡️ 🧩 Building Custom Qiling Extensions & Middleware Plugins (Python practice)

When building automated analysis pipelines, embedding all your hooks and callbacks into a single monolith script quickly leads to unmaintainable spaghetti code. Qiling features a modular extension and middleware architecture. By subclassing QilingExtension or utilizing the ql.filter pipeline, researchers can develop reusable, pluggable security modules (e.g., automated API call loggers, cryptographic key sniffers, network monitors) that attach cleanly to any Qiling instance with a single line of code.

🧠 Core Concept
- Modular Extension Architecture: Encapsulate complex analysis logic into reusable Python classes adhering to Qiling extension interfaces.
- Lifecycle Hook Management: Automatically attach to initialization, execution start, memory mapping, and teardown events.
- Clean Separation of Concerns: Decouple binary setup from analysis modules (e.g., attaching an API logger across 50 different test harnesses).
- Pipeline Filters (ql.filter): Pre-process or post-process system calls and API invocations dynamically.
- Standardized Output Reporting: Collect structured telemetry (JSON, SQLite) across multiple concurrent emulation instances.

💻 Implementation Example
#!/usr/bin/env python3
"""
Post 22: Building Custom Qiling Extensions & Middleware Plugins
Creating a reusable Qiling extension class that logs and colors all API calls with arguments and return values.
"""

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

# Define a reusable custom Qiling Extension for comprehensive API Telemetry
class APITelemetryLogger(QilingExtension):
def __init__(self, ql: Qiling, log_to_file: bool = False):
super().__init__(ql)
self.log_to_file = log_to_file
self.call_history = []
self._setup_hooks()

def _setup_hooks(self) -> None:
# Register hooks or wrap APIs dynamically
print("[Extension] APITelemetryLogger initialized and attached to Qiling instance.")

def log_api_call(self, api_name: str, args: list, retval: int) -> None:
event = {
"api": api_name,
"args": [f"0x{a:x}" if isinstance(a, int) else str(a) for a in args],
"retval": f"0x{retval:x}"
}
self.call_history.append(event)
print(f" [TELEMETRY] API: {api_name:<20} | Args: {event['args']} | Ret: {event['retval']}")

def generate_report(self) -> dict:
return {
"total_calls": len(self.call_history),
"events": self.call_history
}

def run_with_custom_extension(binary_path: str, rootfs_path: str) -> None:
ql = Qiling([binary_path], rootfs_path, ostype=QL_OS.LINUX, archtype=QL_ARCH.X8664, verbose=QL_VERBOSE.DISABLED)

# Attach our custom extension
logger_ext = APITelemetryLogger(ql)

# Add a sample API hook that feeds our telemetry extension
def hooked_malloc(ql: Qiling) -> int:
size = ql.os.function_arg(0)
ret = ql.os.heap.alloc(size)
logger_ext.log_api_call("malloc", [size], ret)
return ret

ql.os.set_api("malloc", hooked_malloc)

print("[*] Running binary with APITelemetryLogger extension active...")
try:
ql.run()
except Exception:
pass

# Extract telemetry report
report = logger_ext.generate_report()
print("=" * 60)
print(f"[+] Extension Report Generated: {report['total_calls']} API calls recorded.")

if __name__ == "__main__":
TARGET = "rootfs/x8664_linux/bin/sample_app"
ROOTFS = "rootfs/x8664_linux"
run_with_custom_extension(TARGET, ROOTFS)

🔥 Use Cases
- Building reusable enterprise-grade malware sandbox analysis plugins.
- Developing automated vulnerability scanners that attach to any firmware emulation target.
- Standardizing threat intelligence telemetry formats across multi-architecture samples.
- Integrating custom memory sanitation and heap corruption detector extensions.
- Sharing modular reverse engineering plugins across security research teams.

⚠️ Caveats & Responsible Practice
- Inheritance: Always inherit from QilingExtension and invoke super()._init_(ql) to ensure proper engine binding.
- State Isolation: Keep extension internal state thread-safe if analyzing multi-threaded binaries.
- Hook Teardown: Clean up custom allocated resources or open file handles in an overridden close() or teardown method.
- Qiling Version: The extension framework is actively maintained; ensure your plugin adheres to modern Qiling 1.4+ class interfaces.

📦 Test Data & Sample
- Target Architecture RootFS
- Qiling Extension Base Classes
- Community Extension Samples

🔗 Resources
- Qiling Extensions Documentation
- Qiling GitHub Extensions Directory

#Qiling #SoftwareArchitecture #PluginSystem #ReverseEngineering #MalwareAnalysis #AppSec #CyberSecurity #Python
⚡️ 🌐 Virtual Network Socket Simulation & C2 Traffic Interception (Python practice)

When sandboxing botnets, ransomware, or command-and-control (C2) agents, the malware immediately attempts to establish outbound network connections (connect(), send(), recv()). In an isolated lab, connecting to live threat actor servers poses operational risks, while blocking network traffic entirely causes malware to terminate prematurely. Qiling allows you to intercept POSIX sockets and WinSock APIs in user-space, simulating a complete virtual C2 server directly within Python to exchange dynamic network packets.

🧠 Core Concept
- In-Memory Socket Virtualization: Intercept socket(), connect(), sendto(), and recvfrom() without creating physical network adapters.
- Virtual C2 Command Dispatch: Inspect outbound beacon packets and respond with mock C2 tasking packets (e.g., execute command, download payload).
- DNS & IP Address Spoofing: Intercept gethostbyname and getaddrinfo to resolve malicious domain names to virtual sandbox IPs.
- Zero Network Leakage: Completely eliminates the risk of accidental traffic leaks or alerting threat actor infrastructure.
- Stateful Protocol Emulation: Maintain multi-stage conversational state machines for proprietary binary network protocols.

💻 Implementation Example
#!/usr/bin/env python3
"""
Post 23: Virtual Network Socket Simulation & C2 Traffic Interception
Intercepting network beacons from an emulated botnet binary and responding with mock C2 commands.
"""

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

# Simulated C2 Server Tasking Packet
MOCK_C2_TASK = b"CMD:SLEEP:60;REPORT_STATUS=OK;\x00"

def hook_connect(ql: Qiling) -> int:
sockfd = ql.os.function_arg(0)
addr_ptr = ql.os.function_arg(1)
addrlen = ql.os.function_arg(2)

# Read sockaddr_in struct: family (2 bytes), port (2 bytes Big-Endian), IP (4 bytes)
sockaddr_raw = ql.mem.read(addr_ptr, 8)
family, port_be, ip_int = struct.unpack(">HH4s", sockaddr_raw[:8])
ip_str = ".".join(str(b) for b in ip_int)

print(f"[NETWORK] connect(sockfd={sockfd}, IP={ip_str}, Port={port_be}) -> SPOOFING SUCCESS (0)")
# Return 0 (Connection Established successfully)
return 0

def hook_send(ql: Qiling) -> int:
sockfd = ql.os.function_arg(0)
buf_ptr = ql.os.function_arg(1)
length = ql.os.function_arg(2)

# Capture raw transmitted beacon data
sent_data = ql.mem.read(buf_ptr, length)
print(f"[C2 BEACON CAPTURED] Transmitted {length} bytes: {sent_data.hex()} (ASCII: {sent_data})")
return length

def hook_recv(ql: Qiling) -> int:
sockfd = ql.os.function_arg(0)
buf_ptr = ql.os.function_arg(1)
maxlen = ql.os.function_arg(2)

# Inject our mock C2 tasking packet into the binary's receive buffer
response = MOCK_C2_TASK[:maxlen]
ql.mem.write(buf_ptr, response)
print(f"[C2 RESPONSE INJECTED] Sent {len(response)} bytes of mock tasking to malware.")
return len(response)

def run_network_sandbox(binary_path: str, rootfs_path: str) -> None:
print(f"[*] Initializing Isolated Network Sandbox for {binary_path}...")
ql = Qiling([binary_path], rootfs_path, ostype=QL_OS.LINUX, archtype=QL_ARCH.ARM, verbose=QL_VERBOSE.DEFAULT)

# Hook POSIX network APIs
ql.os.set_api("connect", hook_connect)
ql.os.set_api("send", hook_send)
ql.os.set_api("recv", hook_recv)

print("[*] Running malware with virtualized C2 network layer...")
ql.run()

if __name__ == "__main__":
TARGET = "rootfs/arm_linux/bin/botnet_client"
ROOTFS = "rootfs/arm_linux"
run_network_sandbox(TARGET, ROOTFS)

🔥 Use Cases
- Safely analyzing Mirai, Gafgyt, and Mozi botnet command-and-control interaction loops.
- Capturing second-stage download URLs and staging commands from live malware samples.
- Auditing proprietary encrypted communication protocols between IoT devices and cloud servers.
- Fuzzing network packet decoders by injecting malformed C2 command structures into recv().
- Simulating corporate air-gapped network environments for malware detonators.

⚠️ Caveats & Responsible Practice
- Network Endianness: IP addresses and port numbers in sockaddr_in structures are always stored in Big-Endian (Network Byte Order); use > in struct.unpack.
- Non-Blocking Sockets: If the binary sets sockets to non-blocking mode (O_NONBLOCK), simulate EWOULDBLOCK or return data immediately.
- SSL/TLS Encrypted Traffic: If malware uses OpenSSL or mbedTLS, hook high-level SSL read/write APIs (SSL_write, mbedtls_ssl_write) rather than raw TCP sockets.
- File Descriptors: Ensure virtual socket file descriptors do not collide with standard input/output (0, 1, 2).

📦 Test Data & Sample Binaries
- Target Architecture RootFS
- Sample Network Botnet Binaries
- Socket Emulation Architecture

🔗 Resources
- Qiling Network Emulation Guide
- POSIX Socket API Specification

#Qiling #NetworkEmulation #C2 #MalwareAnalysis #Botnet #ThreatHunting #ReverseEngineering #CyberSecurity
⚡️ ✂️ Dynamic Binary Patching & Control Flow Hijacking (Python practice)

When reverse engineering software protected by hardware dongles, license signature verifications, integrity checks, or anti-tamper hashes, modifying the binary on disk invalidates digital signatures and triggers checksum alarms. Qiling allows you to perform in-memory dynamic binary patching (ql.patch()) and register-level control flow hijacking during runtime. You can NOP out conditional branch checks, flip CPU condition flags, or rewrite instructions on the fly without touching a single byte on the physical disk.

🧠 Core Concept
- In-Memory Byte Patching (ql.patch()): Overwrite opcodes in guest memory before execution or upon reaching specific runtime trigger conditions.
- NOPing Conditional Jumps: Replace verification branches (JZ, JNZ, BEQ, BNE) with NOP sleds to force execution down target code paths.
- Dynamic Flag Flipping: Intercept execution right before conditional branches and modify CPU status flags (Zero Flag ZF, Carry Flag CF).
- Bypassing Checksum Verifications: Calculate runtime patches dynamically after internal self-integrity validation loops finish.
- Zero-Disk Footprint: Original sample files remain completely pristine and unmodified for forensic integrity.

💻 Implementation Example
#!/usr/bin/env python3
"""
Post 24: Dynamic Binary Patching & Control Flow Hijacking
Dynamically patching authentication/license validation routines during emulation without touching disk files.
"""

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

# Address of license check branch in x86_64 crackme:
# 0x401150: 74 1A (jz 0x40116c -> jumps to "License Invalid! Exit")
# 0x401152: (continues to "License Validated! Unlocking Features...")
LICENSE_BRANCH_ADDR = 0x401150

def hook_before_license_check(ql: Qiling) -> None:
pc = ql.arch.regs.arch_pc
print(f"[*] Reached license verification trigger at PC: 0x{pc:08x}")

# Method 1: Dynamically patch instructions with NOPs (0x90, 0x90) in memory
print(" [+] Applying in-memory NOP patch to conditional jump...")
ql.patch(LICENSE_BRANCH_ADDR, b"\x90\x90")

# Method 2: Alternatively, flip CPU Zero Flag (ZF) to force branch outcome
# ql.arch.regs.eflags &= ~(1 << 6) # Clear Zero Flag (ZF)
print(" [+] Branch successfully hijacked! Valid code path forced.")

def run_patched_emulation(binary_path: str, rootfs_path: str) -> None:
print(f"[*] Initializing Dynamic Patching Sandbox for {binary_path}...")
ql = Qiling([binary_path], rootfs_path, ostype=QL_OS.LINUX, archtype=QL_ARCH.X8664, verbose=QL_VERBOSE.DEFAULT)

# Hook exact address of license branch
ql.hook_address(hook_before_license_check, LICENSE_BRANCH_ADDR)

print("[*] Running binary with dynamic in-memory patch...")
ql.run()

if __name__ == "__main__":
TARGET = "rootfs/x8664_linux/bin/license_validator"
ROOTFS = "rootfs/x8664_linux"
run_patched_emulation(TARGET, ROOTFS)

🔥 Use Cases
- Defeating software licensing, hardware dongle checks, and serial key validations in crackmes.
- Forcing execution down unexplored or hidden code branches during vulnerability audits.
- Bypassing self-integrity hashing routines by applying patches only after hash verification loops execute.
- Neutralizing emergency kill-switches and anti-tamper triggers in analyzed malware.
- Simulating fault injection attacks (clock glitches, bit flips) by mutating opcodes at specific cycle counts.

⚠️ Caveats & Responsible Practice
- Instruction Length: When patching with NOPs, ensure the replacement byte length exactly matches the original instruction length (e.g., 2 bytes for short JZ, 6 bytes for near JNZ).
- ARM Thumb Mode: On ARM, Thumb instructions are 2 or 4 bytes (e.g., NOP is 0x00 0xbf), whereas ARM mode NOP is 0x00 0x00 0xa0 0xe1.
- Instruction Cache Invalidation: In some architectures, modifying code pages requires notifying Unicorn's Translation Block cache; applying hooks before execution starts is recommended.
- Memory Permissions: Ensure the target code page has write permissions enabled prior to patching.

📦 Test Data & Sample Binaries
- Target Architecture RootFS
- License Validator Crackme Sample
- Binary Patching API

🔗 Resources
- Qiling Patch API Reference
- x86/x64 Opcode Reference

#Qiling #BinaryPatching #ReverseEngineering #Crackme #ExploitDev #FaultInjection #CyberSecurity #Python
⚡️ 🎯 Direct Function Calling & Symbol Execution with Qiling (Python practice)

When auditing shared libraries (.so on Linux, .dll on Windows, .dylib on macOS), analysts often need to test a specific exported hashing algorithm, signature verifier, or token generator with thousands of custom inputs without writing a C harness or compiling wrapper binaries. Qiling features high-level function invocation (ql.os.function_call()). It automatically resolves dynamic symbol names, allocates stack frames, sets up register-based calling conventions, executes the target function in isolation, and returns the result directly to Python.

🧠 Core Concept
- Dynamic Symbol Resolution: Locate exported function addresses automatically from ELF/PE symbol tables (ql.loader.import_symbols).
- Native Python Argument Passing (ql.os.function_call()): Pass Python integers, strings, and byte arrays directly into binary functions without manual register manipulation.
- Automatic ABI Management: Qiling sets up the appropriate architecture calling convention (System V AMD64, Microsoft x64, ARM AAPCS) transparently.
- Direct Output Extraction: Capture return values directly in Python as native return types or inspect modified memory pointers.
- Zero Compilation Wrapper Overhead: Call complex C/C++ library routines directly without compiling C test harnesses or fighting cross-toolchains.

💻 Implementation Example
#!/usr/bin/env python3
"""
Post 25: Direct Function Calling & Symbol Execution
Loading a proprietary crypto shared library and executing an internal hashing function directly with custom buffers.
"""

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

def run_direct_function_invocation(lib_path: str, rootfs_path: str) -> None:
print(f"[*] Loading shared library: {lib_path}...")
ql = Qiling(
argv=[lib_path],
rootfs=rootfs_path,
ostype=QL_OS.LINUX,
archtype=QL_ARCH.X8664,
verbose=QL_VERBOSE.DISABLED
)

# 1. Resolve exported symbol address for: uint32_t calculate_token_hash(char *input, int len, uint32_t seed)
symbol_name = "calculate_token_hash"
func_addr = ql.loader.import_symbols.get(symbol_name)

if not func_addr:
# Fallback to known static function offset if stripped
func_addr = 0x4014F0
print(f"[+] Located target function '{symbol_name}' at: 0x{func_addr:08x}")

# 2. Allocate guest memory for test input buffer
test_input = b"UserAdminSessionToken_2026"
buf_addr = ql.os.heap.alloc(len(test_input) + 1)
ql.mem.write(buf_addr, test_input + b"\x00")

seed = 0x1337CAFE

# 3. Call target function directly using Qiling's OS function caller
print(f"[*] Calling {symbol_name}(buf=0x{buf_addr:08x}, len={len(test_input)}, seed=0x{seed:x})...")
return_value = ql.os.function_call(func_addr, [buf_addr, len(test_input), seed])

print("=" * 60)
print(f"[+] Function execution returned: 0x{return_value:08x} ({return_value})")
print("=" * 60)

if __name__ == "__main__":
TARGET_LIB = "rootfs/x8664_linux/lib/libtoken_crypto.so"
ROOTFS = "rootfs/x8664_linux"
run_direct_function_invocation(TARGET_LIB, ROOTFS)

🔥 Use Cases
- Fuzzing individual parsing and cryptographic functions in proprietary shared libraries.
- Generating authentication tokens and session signatures using genuine vendor algorithms.
- Differential testing of hashing algorithms against reference standard implementations.
- Unit testing proprietary reverse engineered firmware libraries during vulnerability research.
- Automating CTF challenge solving for shared library cracking challenges.

⚠️ Caveats & Responsible Practice
- Heap Allocation: Strings or structs passed by reference must reside in valid mapped memory allocated via ql.os.heap.alloc() or ql.mem.map().
- Library Initialization: If the target library requires _init() or .init_array constructors to run first, initialize the loader before calling subroutines.
- Return Value Types: Return values are retrieved from the architecture return register (e.g. RAX on x64, R0 on ARM); cast appropriately in Python.
- Floating-Point Arguments: Functions taking floating-point arguments (via XMM/NEON registers) require manual vector register setup.

📦 Test Data & Sample Binaries
- Target Architecture RootFS
- Target Shared Libraries (.so)
- Function Call Engine

🔗 Resources
- Qiling Function Call Documentation
- System V AMD64 Calling Convention

#Qiling #FunctionCalling #SharedLibraries #ReverseEngineering #CryptoAnalysis #BinaryAnalysis #CyberSecurity #Python
⚡️ ⏱ FreeRTOS & Bare-Metal Cortex-M RTOS Emulation (Python practice)

Modern IoT microcontrollers, medical devices, and industrial sensors rarely run full Linux operating systems. Instead, they run Real-Time Operating Systems (RTOS) like FreeRTOS on bare-metal ARM Cortex-M processors. Emulating FreeRTOS binaries is uniquely challenging due to task queues, timer callbacks, priority-based preemptive scheduling, and direct hardware interrupt service routines (ISRs). Qiling provides specialized RTOS emulation (QL_OSTYPE.FREERTOS), enabling researchers to emulate FreeRTOS task schedulers and intercept inter-task queue communications.

🧠 Core Concept
- RTOS Engine (QL_OSTYPE.FREERTOS): Emulates FreeRTOS kernel data structures, Task Control Blocks (TCBs), and task context switches.
- Inter-Task Queue Mocking: Intercept xQueueSend, xQueueReceive, and xQueueCreate to inspect data exchanged between concurrent RTOS tasks.
- Task Lifecycle Tracing: Monitor task creation (xTaskCreate), priorities, stack high-water marks, and task states (Running, Ready, Blocked, Suspended).
- Software Timer Emulation: Emulate FreeRTOS timer services (xTimerCreate, xTimerStart) without hardware RTC chips.
- Firmware Vulnerability Discovery: Hunt memory corruption vulnerabilities in proprietary RTOS networking stacks (e.g., FreeRTOS+TCP, LwIP).

💻 Implementation Example
#!/usr/bin/env python3
"""
Post 26: FreeRTOS & Bare-Metal Cortex-M RTOS Emulation
Loading a FreeRTOS ARM Cortex-M firmware image and hooking queue operations to inspect inter-task communication.
"""

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

def hook_xQueueSend(ql: Qiling) -> int:
xQueue_handle = ql.os.function_arg(0)
pvItemToQueue_ptr = ql.os.function_arg(1)
xTicksToWait = ql.os.function_arg(2)

# Read message data pushed into the queue (e.g. 16 bytes telemetry structure)
msg_data = ql.mem.read(pvItemToQueue_ptr, 16)
print(f"[FreeRTOS QUEUE] xQueueSend(Queue=0x{xQueue_handle:08x}, WaitTicks={xTicksToWait})")
print(f" -> Message Content (Hex): {msg_data.hex()} | ASCII: {msg_data}")

# pdPASS = 1 (Success in FreeRTOS)
return 1

def hook_xTaskCreate(ql: Qiling) -> int:
pxTaskCode = ql.os.function_arg(0)
pcName_ptr = ql.os.function_arg(1)
usStackDepth = ql.os.function_arg(2)
pvParameters = ql.os.function_arg(3)
uxPriority = ql.os.function_arg(4)

task_name = ql.os.utils.read_cstring(pcName_ptr)
print(f"[FreeRTOS TASK] Created Task: '{task_name}' | Entry: 0x{pxTaskCode:08x} | Priority: {uxPriority}")
return 1 # pdPASS

def run_freertos_sandbox(firmware_bin: str) -> None:
print(f"[*] Initializing FreeRTOS Cortex-M Sandbox for {firmware_bin}...")
ql = Qiling(
argv=[firmware_bin],
rootfs="", # Bare-metal: no Linux rootfs
ostype=QL_OS.FREERTOS,
archtype=QL_ARCH.CORTEX_M,
verbose=QL_VERBOSE.DEFAULT
)

# Hook FreeRTOS kernel APIs
ql.os.set_api("xQueueSend", hook_xQueueSend)
ql.os.set_api("xTaskCreate", hook_xTaskCreate)

print("[*] Starting FreeRTOS task scheduler emulation...")
try:
ql.run(count=200000) # Execute 200k instructions
except Exception as err:
print(f"[*] RTOS execution checkpoint: {err}")

if __name__ == "__main__":
TARGET_FIRMWARE = "rootfs/arm_freertos/sensor_node.bin"
run_freertos_sandbox(TARGET_FIRMWARE)

🔥 Use Cases
- Vulnerability research in automotive microcontroller firmware and drone flight controllers.
- Auditing proprietary industrial IoT firmware running FreeRTOS, Zephyr, or VxWorks.
- Fuzzing embedded RTOS network stacks (FreeRTOS-TCP, MQTT parsers, CoAP endpoints).
- Extracting hardcoded wireless pairing keys (BLE, Zigbee) exchanged across RTOS tasks.
- Validating memory isolation and stack overflow protections in safety-critical medical devices.

⚠️ Caveats & Responsible Practice
- Vector Table Layout: Bare-metal ARM Cortex-M images require vector tables (Initial SP, Reset Handler) mapped at 0x00000000 or 0x08000000.
- Scheduler Ticks: FreeRTOS relies on SysTick interrupts; Qiling provides virtual tick advancement during task switching.
- Context Saving: FreeRTOS uses hardware floating-point registers (FPU) on Cortex-M4/M7; ensure FPU emulation is active if using floating-point math.
- Task Stack Boundaries: Stack overflow detection hooks can be attached to FreeRTOS stack limit addresses.

📦 Test Data & Sample Binaries
- Target Architecture RootFS: Bare-Metal FreeRTOS (No rootfs required)
- Sample FreeRTOS ARM Cortex-M Image
- FreeRTOS Kernel Emulation

🔗 Resources
- Qiling FreeRTOS Architecture
- FreeRTOS Kernel Reference Manual

#Qiling #FreeRTOS #CortexM #IoT #FirmwareSecurity #EmbeddedSystems #VulnerabilityResearch #CyberSecurity
⚡️ 🔑 Automated Cryptographic Key Extraction from Emulated Binaries (Python practice)

Extracting cryptographic keys (AES, RSA, ChaCha20) from compiled binaries is a classic reverse engineering challenge. Obfuscators, white-box wrappers, and anti-tamper protections make static key extraction extraordinarily tedious. However, at runtime, before data can be encrypted or decrypted by standard crypto routines (such as OpenSSL, mbedTLS, or TinyAES), the expanded round keys and initialization vectors (IVs) MUST exist in memory in a usable format. Qiling enables automated, instant key extraction by hooking standard cryptographic function entrypoints.

🧠 Core Concept
- Crypto Function Entry Point Hooking: Intercept standard crypto initialization routines (AES_set_encrypt_key, mbedtls_aes_setkey_enc, EVP_EncryptInit).
- Automated Key Parameter Extraction: Read key length and raw key memory buffers directly from argument registers according to target ABI.
- Entropy & Key Format Validation: Verify extracted key entropy in Python to confirm valid 128-bit, 192-bit, or 256-bit symmetric keys.
- IV & Nonce Interception: Extract dynamic Initialization Vectors passed alongside cipher keys.
- Zero Algorithmic Analysis Needed: Extract cryptographic credentials without reverse engineering complex mathematical transformation matrices.

💻 Implementation Example
#!/usr/bin/env python3
"""
Post 27: Automated Cryptographic Key Extraction from Emulated Binaries
Emulating an encrypted binary and hooking `AES_set_encrypt_key` to automatically extract 256-bit AES keys.
"""

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

extracted_keys = []

def hook_AES_set_encrypt_key(ql: Qiling) -> int:
userKey_ptr = ql.os.function_arg(0)
bits = ql.os.function_arg(1)
key_struct_ptr = ql.os.function_arg(2)

key_bytes_len = bits // 8
raw_key = ql.mem.read(userKey_ptr, key_bytes_len)
key_hex = binascii.hexlify(raw_key).decode()

print("=" * 60)
print(f"[!] CRYPTOGRAPHIC KEY EXTRACTED: AES-{bits}")
print(f" Key Memory Address: 0x{userKey_ptr:08x}")
print(f" Raw Key (Hex) : {key_hex}")
print(f" Raw Key (Bytes) : {raw_key}")
print("=" * 60)

extracted_keys.append({"bits": bits, "hex": key_hex, "raw": raw_key})
return 0 # 0 indicates SUCCESS in OpenSSL AES API

def run_crypto_extractor(binary_path: str, rootfs_path: str) -> None:
print(f"[*] Initializing Automated Crypto Key Extractor for {binary_path}...")
ql = Qiling([binary_path], rootfs_path, ostype=QL_OS.LINUX, archtype=QL_ARCH.X8664, verbose=QL_VERBOSE.DISABLED)

# Register API hook on OpenSSL / standard AES key expansion entrypoint
ql.os.set_api("AES_set_encrypt_key", hook_AES_set_encrypt_key)
ql.os.set_api("AES_set_decrypt_key", hook_AES_set_encrypt_key)

print("[*] Running binary to trigger cryptographic operations...")
try:
ql.run()
except Exception:
pass

print(f"[+] Extraction complete: Total {len(extracted_keys)} unique cryptographic key(s) captured.")

if __name__ == "__main__":
TARGET = "rootfs/x8664_linux/bin/secure_vault_client"
ROOTFS = "rootfs/x8664_linux"
run_crypto_extractor(TARGET, ROOTFS)

🔥 Use Cases
- Recovering AES/ChaCha20 decryption keys from ransomware samples during incident response.
- Extracting TLS session master secrets and pre-shared keys (PSK) from firmware clients.
- Auditing embedded DRM implementations to verify secure key storage compliance.
- Automating decryption of proprietary configuration files and encrypted database blobs.
- Validating cryptographic key entropy and randomness in IoT device firmware.

⚠️ Caveats & Responsible Practice
- Statically Linked Crypto: If OpenSSL or mbedTLS is statically linked and stripped, identify the key expansion function offset using Ghidra / IDA and hook via ql.hook_address().
- Inlined Key Expansion: If crypto is fully inlined, place memory read watchpoints (ql.hook_mem_read()) on the AES S-Box table to locate key expansion rounds.
- Key Lengths: Standard AES keys are 128 (16 bytes), 192 (24 bytes), or 256 bits (32 bytes).
- Calling Conventions: On Windows x64, arguments are passed in RCX, RDX, R8, R9; on Linux System V AMD64, RDI, RSI, RDX, RCX.

📦 Test Data & Sample Binaries
- Target Architecture RootFS
- Sample Encrypted Client
- OpenSSL API Hook Stubs

🔗 Resources
- OpenSSL AES API Reference
- mbedTLS Crypto Documentation

#Qiling #Cryptography #KeyExtraction #AES #ReverseEngineering #MalwareAnalysis #IncidentResponse #CyberSecurity
⚡️ 🌀 Deobfuscating Control-Flow Flattening via Instruction Tracing (Python practice)

Control-Flow Flattening (CFF) (widely popularized by OLLVM, Tigress, and proprietary protectors) destroys natural control flow graphs (CFGs). It breaks basic blocks into fragments, places them inside a giant switch-case statement inside an infinite loop, and coordinates execution order using a dispatcher state variable. Static analysis decompilers output an unreadable mess of loops. Qiling allows you to deobfuscate flattened code by dynamically tracing basic block execution, recording state variable transitions, and reconstructing genuine execution flow graphs.

🧠 Core Concept
- Dynamic CFG Recovery: Trace basic block executions to observe genuine branch transitions regardless of dispatcher indirection.
- State Variable Tracking: Monitor the dispatcher register/variable (e.g., EAX state counter) across loop iterations.
- Opaque Predicate Elimination: Dynamically identify dead branches that are never executed during runtime.
- Symbolic-Emulation Hybrid Tracing: Combine instruction-level execution logs with control-flow graph reconstructors (NetworkX, Graphviz).
- Automating Compiler Deobfuscation: Generate clean patch scripts or microcode passes for IDA Pro / Ghidra based on observed execution traces.

💻 Implementation Example
#!/usr/bin/env python3
"""
Post 28: Deobfuscating Control-Flow Flattening via Instruction Tracing
Tracing state variable mutations in flattened code blocks and extracting genuine basic block execution order.
"""

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

# Address boundaries of the flattened function
FUNC_BASE = 0x401000
FUNC_END = 0x401600
DISPATCHER_BLOCK_ADDR = 0x401050

execution_trace = []
state_history = []

def trace_flattened_blocks(ql: Qiling, address: int, size: int) -> None:
# Filter trace to target function address space
if FUNC_BASE <= address < FUNC_END:
# Check if execution reached the central dispatcher block
if address == DISPATCHER_BLOCK_ADDR:
# Read the current state variable value (stored in EAX/RAX)
state_val = ql.arch.regs.rax
state_history.append((hex(address), hex(state_val)))
print(f"[DISPATCHER HIT] Central Dispatcher 0x{address:08x} -> Next State Variable: 0x{state_val:x}")
else:
execution_trace.append(address)

def run_deobfuscator_trace(binary_path: str, rootfs_path: str) -> None:
print(f"[*] Initializing Control-Flow Deobfuscator Sandbox for {binary_path}...")
ql = Qiling([binary_path], rootfs_path, ostype=QL_OS.LINUX, archtype=QL_ARCH.X8664, verbose=QL_VERBOSE.DISABLED)

# Hook basic block entries
ql.hook_block(trace_flattened_blocks)

print("[*] Running obfuscated function to capture true execution path...")
try:
ql.run(begin=FUNC_BASE, end=FUNC_END)
except Exception:
pass

print("=" * 60)
print(f"[+] Trace Complete: Captured {len(execution_trace)} basic blocks and {len(state_history)} state transitions.")
print("[+] Recovered True Execution Order:")
for idx, (disp, state) in enumerate(state_history[:10]):
print(f" Step {idx + 1:02d}: State Var = {state}")

if __name__ == "__main__":
TARGET = "rootfs/x8664_linux/bin/ollvm_flattened_app"
ROOTFS = "rootfs/x8664_linux"
run_deobfuscator_trace(TARGET, ROOTFS)

🔥 Use Cases
- Defeating OLLVM, Hikari, and Tigress control-flow flattening in protected malware.
- Reconstructing clean Control Flow Graphs (CFGs) for Ghidra / IDA Pro decompilers.
- Identifying and removing opaque predicates and unreachable dead code blocks.
- Auditing commercial software protected by aggressive anti-tamper virtualizers.
- Extracting genuine cryptographic routines concealed inside flattened dispatcher loops.

⚠️ Caveats & Responsible Practice
- Multiple Code Paths: Dynamic tracing captures only the executed path; feed diverse input testcases to reconstruct complete CFG branches.
- State Variable Location: The dispatcher state variable may reside in a CPU register (EAX), a stack local variable ([RBP - 0x10]), or a global memory pointer.
- Loop Boundaries: Specify precise begin and end addresses to prevent tracing runtime libraries (libc.so).
- Graph Generation: Export captured trace lists to DOT/Graphviz format for intuitive visual analysis.

📦 Test Data & Sample Binaries
- Target Architecture RootFS
- OLLVM Obfuscated Test Binaries
- Control Flow Trace Visualizer

🔗 Resources
- Deobfuscating Control Flow Flattening
- OLLVM Source Code

#Qiling #Deobfuscation #OLLVM #ControlFlowFlattening #ReverseEngineering #BinaryAnalysis #CyberSecurity #Python
⚡️ 🗃 Windows Registry & COM Virtualization Deep Dive (Python practice)

Windows malware heavily exploits Component Object Model (COM) interfaces (e.g., CoCreateInstance, WMI, TaskScheduler) and Windows Registry keys for persistence, configuration storage, and defense evasion. In native Windows environments, monitoring these interactions requires kernel drivers, Sysinternals Procmon, or ETW tracing. Qiling features built-in Windows Registry and COM virtualization subsystems. It loads virtual .reg hives into memory and provides programmatic hooks to audit all registry queries, values, and COM interface creations without modifying host systems.

🧠 Core Concept
- In-Memory Registry Virtualization: Loads simulated HKLM, HKCU, and HKCR registry hives from text .reg files in the rootfs.
- Registry Access Hooking: Intercept RegOpenKeyEx, RegQueryValueEx, RegSetValueEx, and RegCreateKey.
- COM Object Instantiation Monitoring: Intercept CoInitialize and CoCreateInstance to track requested CLSID and IID interfaces.
- Persistence Detection: Automatically identify registry Run key writes (SOFTWARE\Microsoft\Windows\CurrentVersion\Run) in real time.
- Host System Safety: Completely isolates all Windows modifications to virtual memory structures.

💻 Implementation Example
#!/usr/bin/env python3
"""
Post 29: Windows Registry & COM Virtualization Deep Dive
Sandboxing a Windows dropper, logging all registry `Run` key writes and COM object invocations.
"""

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

# In-memory log of suspicious persistence activities
persistence_log = []

def hook_RegSetValueExW(ql: Qiling) -> int:
hKey = ql.os.function_arg(0)
lpValueName_ptr = ql.os.function_arg(1)
Reserved = ql.os.function_arg(2)
dwType = ql.os.function_arg(3)
lpData_ptr = ql.os.function_arg(4)
cbData = ql.os.function_arg(5)

val_name = ql.os.utils.read_wstring(lpValueName_ptr)
# Read payload data written to registry
data_bytes = ql.mem.read(lpData_ptr, cbData)

print("=" * 60)
print(f"[!] REGISTRY WRITE DETECTED: RegSetValueExW")
print(f" Value Name : '{val_name}'")
print(f" Data Type : 0x{dwType:x}")
print(f" Data Value : {data_bytes.hex()} (ASCII: {data_bytes})")
print("=" * 60)

persistence_log.append({"val_name": val_name, "data": data_bytes})
return 0 # ERROR_SUCCESS

def hook_CoCreateInstance(ql: Qiling) -> int:
rclsid_ptr = ql.os.function_arg(0)
pUnkOuter = ql.os.function_arg(1)
dwClsContext = ql.os.function_arg(2)
riid_ptr = ql.os.function_arg(3)
ppv_ptr = ql.os.function_arg(4)

clsid_raw = ql.mem.read(rclsid_ptr, 16)
print(f"[COM INTERFACE] CoCreateInstance requested CLSID: {clsid_raw.hex()}")
# S_OK = 0
return 0

def run_win_registry_sandbox(pe_path: str, rootfs_path: str) -> None:
print(f"[*] Initializing Windows Registry Sandbox for {pe_path}...")
ql = Qiling([pe_path], rootfs_path, ostype=QL_OS.WINDOWS, archtype=QL_ARCH.X86, verbose=QL_VERBOSE.DEFAULT)

# Hook Windows Registry and COM APIs
ql.os.set_api("RegSetValueExW", hook_RegSetValueExW)
ql.os.set_api("CoCreateInstance", hook_CoCreateInstance)

print("[*] Running malware to capture registry modifications...")
try:
ql.run()
except Exception:
pass

print(f"[+] Analysis Complete: Captured {len(persistence_log)} persistence registry event(s).")

if __name__ == "__main__":
TARGET = "rootfs/x86_windows/bin/persistence_dropper.exe"
ROOTFS = "rootfs/x86_windows"
run_win_registry_sandbox(TARGET, ROOTFS)

🔥 Use Cases
- Detecting Windows persistence mechanisms (Run keys, AppInit_DLLs, Winlogon helpers).
- Auditing malware usage of COM objects for defense evasion (e.g., COM hijacking).
- Monitoring WMI (Windows Management Instrumentation) queries executed by ransomware.
- Extracting configuration settings stored in proprietary registry paths.
- Safely analyzing Windows software installers and trojanized droppers.

⚠️ Caveats & Responsible Practice
- Registry RootFS Files: Initial registry keys are loaded from .reg text files located inside rootfs/x86_windows/registry/.
- API Variant Hooking: Check both Unicode (RegSetValueExW) and ANSI (RegSetValueExA) variants.
- CLSID Structure: CLSID GUIDs are 16-byte binary structures; format them with Python's uuid.UUID(bytes_le=...) for standard GUID notation.
- Hive Persistence: Memory registry modifications reset when the Python script terminates, leaving no artifacts on disk.

📦 Test Data & Sample Binaries
- Target Architecture RootFS
- Virtual Registry Tables
- COM Object Dispatcher

🔗 Resources
- Qiling Windows Registry Docs
- Microsoft COM Architecture

#Qiling #Windows #Registry #COM #MalwareAnalysis #Persistence #ThreatHunting #CyberSecurity
⚡️ 🚀 Building an Automated Headless Malware Analysis Pipeline with Qiling (Python practice)

Enterprise security operations centers (SOCs) and threat intelligence platforms process tens of thousands of suspicious binary samples daily. Spinning up full virtual machine sandboxes for every sample is resource-heavy, slow (taking minutes per sample), and prone to VM-evasion techniques. By combining Qiling's multi-architecture emulation, syscall/API hooking, VFS isolation, and memory inspection into a unified, headless Python orchestrator, you can build an ultra-fast, cross-platform malware triage engine that produces rich JSON IOC reports in under three seconds per sample.

🧠 Core Concept
- End-to-End Headless Triage: Orchestrates static header parsing, dynamic emulation, API tracking, network spoofing, and IOC extraction in a single pass.
- Zero-Hypervisor Infrastructure: Runs on lightweight Linux containers (Docker/Kubernetes) without nested virtualization or GPU requirements.
- Multi-Architecture Support: Automatically routes x86, x64, ARM, and MIPS samples to appropriate rootfs profiles dynamically.
- Comprehensive Telemetry Aggregation: Aggregates spawned processes, dropped files, touched registry keys, network beacons, and decrypted strings.
- Structured JSON Reporting: Emits standardized machine-readable threat intelligence reports ready for SIEM / SOAR ingestion.

💻 Implementation Example
#!/usr/bin/env python3
"""
Post 30: Building an Automated Headless Malware Analysis Pipeline with Qiling
Full automated triage pipeline script analyzing unknown samples and generating a structured JSON report.
"""

from qiling import Qiling
from qiling.const import QL_VERBOSE, QL_ARCH, QL_OS
import json
import time
import os

class HeadlessMalwareTriageEngine:
def __init__(self, sample_path: str, rootfs_path: str, arch: QL_ARCH, os_type: QL_OS):
self.sample_path = sample_path
self.rootfs_path = rootfs_path
self.arch = arch
self.os_type = os_type

self.report = {
"sample": os.path.basename(sample_path),
"timestamp": time.time(),
"apis_called": [],
"network_activity": [],
"dropped_files": [],
"registry_keys": [],
"execution_status": "PENDING"
}

def _setup_hooks(self, ql: Qiling) -> None:
# 1. API Call Monitor
def hook_generic_api(api_name: str):
def handler(ql: Qiling):
self.report["apis_called"].append(api_name)
return handler

for api in ["CreateFileW", "VirtualAlloc", "RegSetValueExW", "connect", "send"]:
ql.os.set_api(api, hook_generic_api(api))

# 2. Network Activity Interception
def hook_connect(ql: Qiling) -> int:
self.report["network_activity"].append({"action": "CONNECT", "status": "INTERCEPTED"})
return 0
ql.os.set_api("connect", hook_connect)

def analyze(self, timeout_sec: int = 5) -> dict:
print(f"[*] Analyzing sample: {self.sample_path}...")
start_t = time.time()

try:
ql = Qiling(
argv=[self.sample_path],
rootfs=self.rootfs_path,
ostype=self.os_type,
archtype=self.arch,
verbose=QL_VERBOSE.DISABLED
)

self._setup_hooks(ql)
ql.run(timeout=timeout_sec * 1_000_000)
self.report["execution_status"] = "SUCCESS"
except Exception as err:
self.report["execution_status"] = f"STOPPED ({err})"

self.report["duration_sec"] = round(time.time() - start_t, 3)
return self.report

def run_triage_pipeline(sample: str, rootfs: str) -> None:
engine = HeadlessMalwareTriageEngine(sample, rootfs, QL_ARCH.X8664, QL_OS.LINUX)
triage_result = engine.analyze(timeout_sec=3)

# Save structured JSON Report
report_file = "triage_report.json"
with open(report_file, "w") as f:
json.dump(triage_result, f, indent=2)

print("=" * 60)
print(f"[+] Automated Triage Finished in {triage_result['duration_sec']}s")
print(f"[+] Output JSON Report written to '{report_file}':")
print(json.dumps(triage_result, indent=2))
print("=" * 60)

if __name__ == "__main__":
SAMPLE = "rootfs/x8664_linux/bin/unknown_sample"
ROOTFS = "rootfs/x8664_linux"
run_triage_pipeline(SAMPLE, ROOTFS)

🔥 Use Cases
- High-volume malware triage pipelines processing email attachments and telemetry streams.
- Automated Threat Intelligence ingestion generating real-time Indicators of Compromise (IOCs).
- CI/CD security scanning for third-party binary dependencies in software supply chains.
- Scalable cloud sandboxes deployed on Kubernetes without expensive VM hypervisor licenses.
- Enriching Incident Response (IR) alerts with instantaneous behavioral execution logs.

⚠️ Caveats & Responsible Practice
- Sandbox Timeouts: Set reasonable timeouts (timeout=... in microseconds) to prevent infinite loops in malicious samples.
- Container Sandboxing: Run the Python triage orchestrator inside unprivileged Docker containers for defence-in-depth isolation.
- Resource Limits: Limit maximum guest memory allocations to prevent denial-of-service memory exhaustion.
- Architecture Auto-Detection: Use python-magic or pyelftools/pefile to automatically detect architecture and OS before initializing Qiling.

📦 Test Data & Sample Binaries
- Target Architecture RootFS
- Unknown Samples Triage Feed
- Automated Pipeline Harness

🔗 Resources
- Qiling Official Documentation
- Qiling Framework GitHub

#Qiling #MalwareAnalysis #ThreatIntel #Automation #SOC #IncidentResponse #ReverseEngineering #CyberSecurity
👍1