mirror of
https://github.com/qilingframework/qiling.git
synced 2026-10-12 02:21:40 +00:00
Fix and comment examples
This commit is contained in:
@@ -8,28 +8,64 @@ sys.path.append("..")
|
||||
|
||||
from qiling import Qiling
|
||||
from qiling.extensions import pipe
|
||||
from qiling.const import QL_INTERCEPT, QL_VERBOSE
|
||||
from qiling.os.windows.api import HWND, UINT, LONG
|
||||
|
||||
def force_call_dialog_func(ql: Qiling):
|
||||
# get DialogFunc address
|
||||
lpDialogFunc = ql.unpack32(ql.mem.read(ql.arch.regs.esp - 0x8, 4))
|
||||
# setup stack for DialogFunc
|
||||
ql.stack_push(0)
|
||||
ql.stack_push(1001)
|
||||
ql.stack_push(273)
|
||||
ql.stack_push(0)
|
||||
ql.stack_push(0x0401018)
|
||||
# force EIP to DialogFunc
|
||||
ql.arch.regs.eip = lpDialogFunc
|
||||
def hook_DialogBoxParamA_onexit(ql: Qiling, address: int, params, retval: int):
|
||||
# extract lpDialogFunc value
|
||||
# [see arguments list at 'qiling/os/windows/dlls/user32.py' -> 'hook_DialogBoxParamA']
|
||||
lpDialogFunc = params['lpDialogFunc']
|
||||
|
||||
def call_DialogFunc(ql: Qiling):
|
||||
# we would like to resume from the exact same address that used to invoke
|
||||
# this hook. in order to prevent an endless loop of hook invocations, we
|
||||
# remove the hook through its handle.
|
||||
hh.remove()
|
||||
|
||||
WM_COMMAND = 0x111
|
||||
IDS_APPNAME = 1001
|
||||
|
||||
# [steps #3 and #4]
|
||||
# set up the arguments and call the address passed through the lpDialogFunc
|
||||
# param. make sure it resumes back to where we were.
|
||||
ql.os.fcall.call_native(lpDialogFunc, (
|
||||
(HWND, 0),
|
||||
(UINT, WM_COMMAND),
|
||||
(UINT, IDS_APPNAME),
|
||||
(LONG, 0),
|
||||
), ql.arch.regs.arch_pc)
|
||||
|
||||
# get DialogBoxParamA return address; should be the first item on the stack
|
||||
retaddr = ql.arch.stack_read(0)
|
||||
|
||||
# we would like to call DialogFunc as soon as DialogBoxParamA returns, so we
|
||||
# hook its return address. once it returns, 'call_DialogFunc' will be invoked.
|
||||
hh = ql.hook_address(call_DialogFunc, retaddr)
|
||||
|
||||
def our_sandbox(path: str, rootfs: str):
|
||||
ql = Qiling([path], rootfs)
|
||||
ql = Qiling([path], rootfs, verbose=QL_VERBOSE.DEFAULT)
|
||||
|
||||
# this crackme's logic lies within the function passed to DialogBoxParamA through
|
||||
# the lpDialogFunc parameter. normally DialogBoxParamA would call the function
|
||||
# passed through that parameter, but Qiling's implementation for it doesn't do
|
||||
# that.
|
||||
#
|
||||
# to solve this crackme and force the "success" dialog to show, we will:
|
||||
# 1. set up a mock stdin and feed it with the correct flag
|
||||
# 1. hook DialogBoxParamA to see where its lpDialogFunc param points to
|
||||
# 2. set up a valid set of arguments DialogFunc expects to see
|
||||
# 3. call it and see it greets us with a "success" message
|
||||
|
||||
# [step #1]
|
||||
# set up a mock stdin and feed it with mocked keystrokes
|
||||
ql.os.stdin = pipe.SimpleInStream(sys.stdin.fileno())
|
||||
ql.os.stdin.write(b"Ea5yR3versing\n")
|
||||
ql.os.stdin.write(b'Ea5yR3versing\n')
|
||||
|
||||
# [step #2]
|
||||
# intercept DialogBoxParamA on exit
|
||||
ql.os.set_api('DialogBoxParamA', hook_DialogBoxParamA_onexit, QL_INTERCEPT.EXIT)
|
||||
|
||||
ql.hook_address(force_call_dialog_func, 0x00401016)
|
||||
ql.run()
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Flag is : Ea5yR3versing
|
||||
our_sandbox(r"rootfs/x86_windows/bin/Easy_CrackMe.exe", r"rootfs/x86_windows")
|
||||
|
||||
@@ -6,9 +6,29 @@
|
||||
import sys
|
||||
sys.path.append("..")
|
||||
|
||||
from typing import Mapping
|
||||
from capstone import Cs
|
||||
|
||||
from qiling import Qiling
|
||||
|
||||
def __map_regs() -> Mapping[int, int]:
|
||||
"""Map Capstone x86 regs definitions to Unicorn's.
|
||||
"""
|
||||
|
||||
from capstone import x86_const as cs_x86_const
|
||||
from unicorn import x86_const as uc_x86_const
|
||||
|
||||
def __canonicalized_mapping(module, prefix: str) -> Mapping[str, int]:
|
||||
return dict((k[len(prefix):], getattr(module, k)) for k in dir(module) if k.startswith(prefix))
|
||||
|
||||
cs_x86_regs = __canonicalized_mapping(cs_x86_const, 'X86_REG')
|
||||
uc_x86_regs = __canonicalized_mapping(uc_x86_const, 'UC_X86_REG')
|
||||
|
||||
return dict((cs_x86_regs[k], uc_x86_regs[k]) for k in cs_x86_regs if k in uc_x86_regs)
|
||||
|
||||
# capstone to unicorn regs mapping
|
||||
CS_UC_REGS = __map_regs()
|
||||
|
||||
def trace(ql: Qiling, address: int, size: int, md: Cs):
|
||||
"""Emit tracing info for each and every instruction that is about to be executed.
|
||||
|
||||
@@ -19,27 +39,36 @@ def trace(ql: Qiling, address: int, size: int, md: Cs):
|
||||
md: initialized disassembler object
|
||||
"""
|
||||
|
||||
# read current instruction bytes and disassemble it
|
||||
buf = ql.mem.read(address, size)
|
||||
insn = next(md.disasm(buf, address))
|
||||
|
||||
nibbles = ql.arch.bits // 4
|
||||
color_faded = '\033[2m'
|
||||
color_reset = '\033[0m'
|
||||
|
||||
esc_dgray = "\x1b[90m"
|
||||
esc_reset = "\x1b[39m"
|
||||
# get values of the registers referenced by this instruction.
|
||||
#
|
||||
# note: since this method is called before the instruction has been emulated, the 'rip'
|
||||
# register still points to the current instruction, while the instruction considers it
|
||||
# as if it was pointing to the next one. that will cause 'rip' to show an incorrect value
|
||||
reads = (f'{md.reg_name(reg)} = {ql.arch.regs.read(CS_UC_REGS[reg]):#x}' for reg in insn.regs_access()[0])
|
||||
|
||||
for insn in md.disasm(buf, address):
|
||||
opcode = ''.join(f'{b:02x}' for b in insn.bytes)
|
||||
# construct a human-readable trace line
|
||||
trace_line = f'{insn.address:0{nibbles}x} | {insn.bytes.hex():24s} {insn.mnemonic:12} {insn.op_str:35s} | {", ".join(reads)}'
|
||||
|
||||
# BUG: insn.regs_read doesn't work well, so we use insn.regs_access()[0] instead
|
||||
reads = (f'{md.reg_name(reg)} = {ql.arch.regs.read(reg):#x}' for reg in insn.regs_access()[0])
|
||||
trace_line = f'{insn.address:0{nibbles}x} | {opcode:20s} {insn.mnemonic:10} {insn.op_str:35s} | {", ".join(reads)}'
|
||||
|
||||
# emit trace line in dark gray so it would be easier to tell trace info from other log entries
|
||||
ql.log.info(f'{esc_dgray}{trace_line}{esc_reset}')
|
||||
# emit the trace line in a faded color, so it would be easier to tell trace info from other log entries
|
||||
ql.log.info(f'{color_faded}{trace_line}{color_reset}')
|
||||
|
||||
if __name__ == "__main__":
|
||||
ql = Qiling(["rootfs/x8664_linux/bin/x8664_hello"], "rootfs/x8664_linux")
|
||||
ql = Qiling([r"rootfs/x8664_linux/bin/x8664_hello"], r"rootfs/x8664_linux")
|
||||
|
||||
# acquire a disassembler instance bound to arch
|
||||
md = ql.arch.disassembler
|
||||
md.detail = True
|
||||
|
||||
# register the trace method to be called before each instruction
|
||||
ql.hook_code(trace, user_data=md)
|
||||
|
||||
# go!
|
||||
ql.run()
|
||||
|
||||
Reference in New Issue
Block a user