ql.hook_address(callback: Callable, address: int)

Hook a specific address. The registered callback will be invoked upon execution of the specified address.

    from qiling import Qiling

    def stop(ql: Qiling) -> None:
        ql.log.info('killer switch found, stopping')
        ql.emu_stop()

    ql = Qiling([r'examples/rootfs/x86_windows/bin/wannacry.bin'], r'examples/rootfs/x86_windows')

    # have 'stop' called when execution reaches 0x40819a
    ql.hook_address(stop, 0x40819a)

    ql.run()

ql.hook_code(callback: Callable, user_data: Any = None)

Hook all instructions. The registered callback will be invoked on every assembly instruction, just before it gets executed

from capstone import Cs
from qiling import Qiling
from qiling.const import QL_VERBOSE

def simple_diassembler(ql: Qiling, address: int, size: int, md: Cs) -> None:
    buf = ql.mem.read(address, size)

    for insn in md.disasm(buf, address):
        ql.log.debug(f':: {insn.address:#x} : {insn.mnemonic:24s} {insn.op_str}')

if __name__ == "__main__":
    ql = Qiling([r'examples/rootfs/x8664_linux/bin/x8664_hello'], r'examples/rootfs/x8664_linux', verbose=QL_VERBOSE.DEBUG)

    # have 'simple_disassembler' called on each instruction, passing a Capstone disassembler instance bound to
    # the underlying architecture as an optional argument
    ql.hook_code(simple_diassembler, user_data=ql.arch.disassembler)

    ql.run()

ql.hook_block()

  • hooking a block of code
def ql_hook_block_disasm(ql, address, size):
    ql.log.debug("\n[+] Tracing basic block at 0x%x" % (address))

ql.hook_block(ql_hook_block_disasm)

ql.hook_intno()

  • hooking interupt number to invoke a custom fuction
ql.hook_intno(hook_syscall, 0x80)

ql.hook_insn()

Intercept instructions of a specific type. The supported instruction types are limited to the ones supported by Unicorn.

Supported Intel instructions: - UC_X86_INS_SYSCALL - UC_X86_INS_IN - UC_X86_INS_OUT

from typing import Tuple
from unicorn.x86_const import UC_X86_INS_IN

def handle_in(ql: Qiling, port: int, size: int) -> Tuple[int, int]:
    # call some function to look up the value held in the specified port (not implemented by Qiling)
    value = lookup_port_value(port, size)

    ql.log.debug(f'reading from port {port:#x}, size {size:d} -> {value:#0{size * 2 + 2}x}')

    # return a tuple indicating other hooks may be processed (0) and the read value (value)
    return (0, value)

ql.hook_insn(handle_in, UC_X86_INS_IN)

ql.hook_int()

  • interupt
ql.hook_intr()

ql.hook_mem_unmapped()

Intercept memory accesses to unmapped addresses. That includes memory reads, writes and fetches.

ql.hook_mem_read_invalid()

ql.hook_mem_write_invalid()

ql.hook_mem_fetch_invalid()

qll.hook_mem_invalid()

Intercept invalid memory accesses. That includes memory accesses to unmapped addresses, and protection violations (e.g. writing to a read-only memory range, or fetching from a non-executable memory range).

ql.hook_mem_read(callback: Callable, begin: int = 1, end: int = 0)

Intercept memory reads from memory locations between begin and end. The registered callback will be invoked on every attempt to read from the specified memory range, just before the value is read.

Notes: - If end is not specified, only reads to the address specified in begin will be intercepted - If both begin and end are not specified, all memory reads will be intercepted - The callback value argument is unused (always 0) - The callback may alter the value in memory before it is read (i.e. write a new value to that memory location)

from unicorn.unicorn_const import UC_MEM_READ

def mem_read(ql: Qiling, access: int, address: int, size: int, value: int) -> None:
    # only read accesses are expected here
    assert access == UC_MEM_READ

    ql.log.debug(f'intercepted a memory read from {address:#x}')

stack_lbound = ql.arch.regs.arch_sp
stack_ubound = ql.arch.regs.arch_sp - 0x1000

# hook all reads from the top page of the stack
ql.hook_mem_read(mem_read, begin=stack_ubound, end=stack_lbound)

ql.hook_mem_write(callback: Callable, begin: int = 1, end: int = 0)

Intercept memory writes to memory locations between begin and end. The registered callback will be invoked on every attempt to write to the specified memory range, just before the value is written.

Notes: - If end is not specified, only writes to the address specified in begin will be intercepted - If both begin and end are not specified, all memory writes will be intercepted

from unicorn.unicorn_const import UC_MEM_WRITE

def mem_write(ql: Qiling, access: int, address: int, size: int, value: int) -> None:
    # only write accesses are expected here
    assert access == UC_MEM_WRITE

    ql.log.debug(f'intercepted a memory write to {address:#x} (value = {value:#x})')

trigger_address = 0xdecaf000

# hook all writes to 'trigger_address'
ql.hook_mem_write(mem_write, trigger_address)

ql.hook_mem_fetch()

  • monitoring a process performing memory fetch on a specific address

ql.hook_del()

ql.clear_hooks()

  • clear all hooks
ql.clear_hooks()