Merge branch 'dev' into next

This commit is contained in:
dataisland
2022-03-01 18:51:13 +08:00
169 changed files with 3285 additions and 3052 deletions
+5 -8
View File
@@ -16,14 +16,11 @@ ROOTFS = r"rootfs/x86_linux"
class Solver:
def __init__(self, invalid: bytes):
mock_stdin = pipe.SimpleInStream(sys.stdin.fileno())
mock_stdout = pipe.NullOutStream(sys.stdout.fileno())
# create a silent qiling instance
self.ql = Qiling([rf"{ROOTFS}/bin/crackme_linux"], ROOTFS,
verbose=QL_VERBOSE.OFF, # thwart qiling logger output
stdin=mock_stdin, # take over the input to the program using a fake stdin
stdout=mock_stdout) # disregard program output
self.ql = Qiling([rf"{ROOTFS}/bin/crackme_linux"], ROOTFS, verbose=QL_VERBOSE.OFF)
self.ql.os.stdin = pipe.SimpleInStream(sys.stdin.fileno()) # take over the input to the program using a fake stdin
self.ql.os.stdout = pipe.NullOutStream(sys.stdout.fileno()) # disregard program output
# execute program until it reaches the 'main' function
self.ql.run(end=0x0804851b)
@@ -32,7 +29,7 @@ class Solver:
#
# since the emulation halted upon entering 'main', its return address is there on
# the stack. we use it to limit the emulation till function returns
self.replay_starts = self.ql.reg.arch_pc
self.replay_starts = self.ql.arch.regs.arch_pc
self.replay_ends = self.ql.stack_read(0)
# instead of restarting the whole program every time a new flag character is guessed,
+3 -6
View File
@@ -14,13 +14,10 @@ def instruction_count(ql: Qiling, address: int, size: int, user_data):
user_data[0] += 1
def get_count(flag):
mock_stdin = pipe.SimpleStringBuffer()
mock_stdout = pipe.SimpleStringBuffer()
ql = Qiling(["rootfs/x86_windows/bin/crackme.exe"], "rootfs/x86_windows", verbose=QL_VERBOSE.OFF)
ql = Qiling(["rootfs/x86_windows/bin/crackme.exe"], "rootfs/x86_windows",
verbose=QL_VERBOSE.OFF,
stdin=mock_stdin,
stdout=mock_stdout)
ql.os.stdin = pipe.SimpleStringBuffer()
ql.os.stdout = pipe.SimpleStringBuffer()
ql.os.stdin.write(bytes("".join(flag) + "\n", 'utf-8'))
count = [0]
+55 -17
View File
@@ -8,26 +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.reg.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.reg.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 our_sandbox(path, rootfs):
ql = Qiling(path, rootfs, stdin=pipe.SimpleInStream(sys.stdin.fileno()))
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, 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')
# [step #2]
# intercept DialogBoxParamA on exit
ql.os.set_api('DialogBoxParamA', hook_DialogBoxParamA_onexit, QL_INTERCEPT.EXIT)
ql.os.stdin.write(b"Ea5yR3versing\n")
ql.hook_address(force_call_dialog_func, 0x00401016)
ql.run()
if __name__ == "__main__":
# Flag is : Ea5yR3versing
our_sandbox(["rootfs/x86_windows/bin/Easy_CrackMe.exe"], "rootfs/x86_windows")
our_sandbox(r"rootfs/x86_windows/bin/Easy_CrackMe.exe", r"rootfs/x86_windows")
+2 -2
View File
@@ -10,7 +10,7 @@ from qiling import Qiling
def force_call_dialog_func(ql: Qiling):
# get DialogFunc address
lpDialogFunc = ql.unpack32(ql.mem.read(ql.reg.esp - 0x8, 4))
lpDialogFunc = ql.unpack32(ql.mem.read(ql.arch.regs.esp - 0x8, 4))
# setup stack for DialogFunc
ql.stack_push(0)
ql.stack_push(1001)
@@ -18,7 +18,7 @@ def force_call_dialog_func(ql: Qiling):
ql.stack_push(0)
ql.stack_push(0x0401018)
# force EIP to DialogFunc
ql.reg.eip = lpDialogFunc
ql.arch.regs.eip = lpDialogFunc
def my_sandbox(path, rootfs):
ql = Qiling(path, rootfs)
+2 -2
View File
@@ -10,7 +10,7 @@ from qiling import Qiling
def force_call_dialog_func(ql: Qiling):
# get DialogFunc address
lpDialogFunc = ql.unpack32(ql.mem.read(ql.reg.esp - 0x8, 4))
lpDialogFunc = ql.unpack32(ql.mem.read(ql.arch.regs.esp - 0x8, 4))
# setup stack for DialogFunc
ql.stack_push(0)
ql.stack_push(1001)
@@ -18,7 +18,7 @@ def force_call_dialog_func(ql: Qiling):
ql.stack_push(0)
ql.stack_push(0x0401018)
# force EIP to DialogFunc
ql.reg.eip = lpDialogFunc
ql.arch.regs.eip = lpDialogFunc
def our_sandbox(path, rootfs):
ql = Qiling(path, rootfs)
+7 -7
View File
@@ -119,7 +119,7 @@ def echo_key(ql: Qiling, key):
def show_once(ql: Qiling, key):
klen = len(key)
ql.reg.ax = klen
ql.arch.regs.ax = klen
ql.mem.write(0x87F4, key)
# Partial exectution to skip input reading
ql.run(begin=0x801B, end=0x803d)
@@ -133,7 +133,7 @@ def third_stage(keys):
"rootfs/8086",
console=False)
ql.add_fs_mapper(0x80, QlDisk("rootfs/8086/doogie/doogie.DOS_MBR", 0x80))
ql.set_api((0x1a, 4), set_required_datetime, QL_INTERCEPT.EXIT)
ql.os.set_api((0x1a, 4), set_required_datetime, QL_INTERCEPT.EXIT)
hk = ql.hook_code(stop, begin=0x8018, end=0x8018)
ql.run()
ql.hook_del(hk)
@@ -172,10 +172,10 @@ def read_until_zero(ql: Qiling, addr):
def set_required_datetime(ql: Qiling):
ql.log.info("Setting Feburary 06, 1990")
ql.reg.ch = BIN2BCD(19)
ql.reg.cl = BIN2BCD(1990%100)
ql.reg.dh = BIN2BCD(2)
ql.reg.dl = BIN2BCD(6)
ql.arch.regs.ch = BIN2BCD(19)
ql.arch.regs.cl = BIN2BCD(1990%100)
ql.arch.regs.dh = BIN2BCD(2)
ql.arch.regs.dl = BIN2BCD(6)
def stop(ql, addr, data):
ql.emu_stop()
@@ -187,7 +187,7 @@ def first_stage():
console=False)
ql.add_fs_mapper(0x80, QlDisk("rootfs/8086/doogie/doogie.DOS_MBR", 0x80))
# Doogie suggests that the datetime should be 1990-02-06.
ql.set_api((0x1a, 4), set_required_datetime, QL_INTERCEPT.EXIT)
ql.os.set_api((0x1a, 4), set_required_datetime, QL_INTERCEPT.EXIT)
# A workaround to stop the program.
hk = ql.hook_code(stop, begin=0x8018, end=0x8018)
ql.run()
@@ -5,10 +5,10 @@ class QILING_IDA():
pass
def _show_context(self, ql:Qiling):
registers = [ k for k in ql.reg.register_mapping.keys() if type(k) is str ]
registers = [ k for k in ql.arch.regs.register_mapping.keys() if type(k) is str ]
for idx in range(0, len(registers), 3):
regs = registers[idx:idx+3]
s = "\t".join(map(lambda v: f"{v:4}: {ql.reg.__getattr__(v):016x}", regs))
s = "\t".join(map(lambda v: f"{v:4}: {ql.arch.regs.__getattr__(v):016x}", regs))
ql.log.info(s)
def custom_prepare(self, ql:Qiling):
@@ -17,50 +17,53 @@ Steps:
$ rm -fr afl_outputs/default/
"""
# No more need for importing unicornafl, try ql.afl_fuzz instead!
# No more need for importing unicornafl, try afl.ql_afl_fuzz instead!
import os
import sys
from typing import Any, Optional
from typing import Optional
sys.path.append("../../..")
from qiling import Qiling
from qiling.const import QL_VERBOSE
from qiling.extensions import pipe
from qiling.extensions.afl import ql_afl_fuzz
from qiling.extensions import afl
def main(input_file: str):
mock_stdin = pipe.SimpleInStream(sys.stdin.fileno())
ql = Qiling(["./x8664_fuzz"], "../../rootfs/x8664_linux",
verbose=QL_VERBOSE.OFF, # keep qiling logging off
console=False, # thwart program output
stdin=mock_stdin, # redirect stdin to our mock to feed it with incoming fuzzed keystrokes
stdout=None,
stderr=None)
verbose=QL_VERBOSE.OFF, # keep qiling logging off
console=False) # thwart program output
# redirect stdin to our mock to feed it with incoming fuzzed keystrokes
ql.os.stdin = pipe.SimpleInStream(sys.stdin.fileno())
def place_input_callback(ql: Qiling, input: bytes, persistent_round: int) -> Optional[bool]:
"""Called with every newly generated input.
"""Feed generated stimuli to the fuzzed target.
This method is called with every fuzzing iteration.
"""
# feed fuzzed input to our mock stdin
ql.os.stdin.write(input)
# signal afl to proceed with this input
return True
def start_afl(_ql: Qiling):
"""Callback from inside.
def start_afl(ql: Qiling):
"""Have Unicorn fork and start instrumentation.
"""
ql_afl_fuzz(_ql, input_file=input_file, place_input_callback=place_input_callback, exits=[ql.os.exit_point])
afl.ql_afl_fuzz(ql, input_file=input_file, place_input_callback=place_input_callback, exits=[ql.os.exit_point])
# get image base address
ba = ql.loader.images[0].base
# make process crash whenever __stack_chk_fail@plt is about to be called.
# make the process crash whenever __stack_chk_fail@plt is about to be called.
# this way afl will count stack protection violations as crashes
ql.hook_address(callback=lambda x: os.abort(), address=ba + 0x1225)
# set a hook on main() to let unicorn fork and start instrumentation
# set afl instrumentation [re]starting point. we set it to 'main'
ql.hook_address(callback=start_afl, address=ba + 0x122c)
# okay, ready to roll
@@ -10,11 +10,7 @@ import sys, os, ctypes
class SimpleFuzzer:
def run(self):
ql = Qiling(["./x8664_fuzz"], "../../rootfs/x8664_linux",
console=False, # No output
stdin=None,
stdout=None,
stderr=None)
ql = Qiling(["./x8664_fuzz"], "../../rootfs/x8664_linux", console=False)
ba = ql.loader.images[0].base
try:
# Only instrument the function `fun`, so we don't need to instrument libc and ld
+6 -9
View File
@@ -21,16 +21,13 @@ from qiling.extensions import pipe
from qiling.extensions.afl import ql_afl_fuzz
def main(input_file, enable_trace=False):
mock_stdin = pipe.SimpleInStream(sys.stdin.fileno())
ql = Qiling(["./arm_fuzz"], "../../rootfs/arm_qnx", console=enable_trace)
ql = Qiling(["./arm_fuzz"], "../../rootfs/arm_qnx",
stdin=mock_stdin,
stdout=1 if enable_trace else None,
stderr=1 if enable_trace else None,
console = True if enable_trace else False)
ql.os.stdin = pipe.SimpleInStream(sys.stdin.fileno())
# or this for output:
# ... stdout=sys.stdout, stderr=sys.stderr)
if not enable_trace:
ql.os.stdout = pipe.NullOutStream(sys.stdout.fileno())
ql.os.stderr = pipe.NullOutStream(sys.stderr.fileno())
def place_input_callback(ql: Qiling, input: bytes, _: int):
ql.os.stdin.write(input)
@@ -50,7 +47,7 @@ def main(input_file, enable_trace=False):
if enable_trace:
# The following lines are only for `-t` debug output
md = ql.create_disassembler()
md = ql.arch.disassembler
count = [0]
def spaced_hex(data):
@@ -59,7 +59,7 @@ def patcher(ql):
def check_pc(ql):
print("=" * 50)
print("Hit fuzz point, stop at PC = 0x%x" % ql.reg.arch_pc)
print("Hit fuzz point, stop at PC = 0x%x" % ql.arch.regs.arch_pc)
print("=" * 50)
ql.emu_stop()
+2 -2
View File
@@ -30,9 +30,9 @@ if __name__ == "__main__":
# Known issue: If the syscall func is not be implemented in qiling, qiling does
# not know which func should be replaced.
# In that case, you must specify syscall by its number.
ql.set_syscall(0x04, my_syscall_write)
ql.os.set_syscall(0x04, my_syscall_write)
# set syscall by syscall name
#ql.set_syscall("write", my_syscall_write)
#ql.os.set_syscall("write", my_syscall_write)
ql.run()
+3 -3
View File
@@ -28,7 +28,7 @@ def my_puts_onexit(ql: Qiling):
if __name__ == "__main__":
ql = Qiling(["rootfs/arm_qnx/bin/hello_static"], "rootfs/arm_qnx")
ql.set_api('puts', my_puts_onenter, QL_INTERCEPT.ENTER)
ql.set_api('printf', my_printf_onenter, QL_INTERCEPT.ENTER)
ql.set_api('puts', my_puts_onexit, QL_INTERCEPT.EXIT)
ql.os.set_api('puts', my_puts_onenter, QL_INTERCEPT.ENTER)
ql.os.set_api('printf', my_printf_onenter, QL_INTERCEPT.ENTER)
ql.os.set_api('puts', my_puts_onexit, QL_INTERCEPT.EXIT)
ql.run()
+13 -13
View File
@@ -19,11 +19,11 @@ def get_kaimendaji_password():
value_addr = ql.os.heap.alloc(len(value))
ql.mem.write(value_addr, value)
ql.reg.r0 = value_addr
ql.reg.arch_pc = ql.reg.lr
ql.arch.regs.r0 = value_addr
ql.arch.regs.arch_pc = ql.arch.regs.lr
def get_password(ql, *args, **kwargs):
password_raw = ql.mem.read(ql.reg.r0, ql.reg.r2)
password_raw = ql.mem.read(ql.arch.regs.r0, ql.arch.regs.r2)
password = ''
for item in password_raw:
@@ -36,21 +36,21 @@ def get_kaimendaji_password():
def partial_run_init(ql):
# argv prepare
ql.reg.arch_sp -= 0x30
arg0_ptr = ql.reg.arch_sp
ql.arch.regs.arch_sp -= 0x30
arg0_ptr = ql.arch.regs.arch_sp
ql.mem.write(arg0_ptr, b"kaimendaji")
ql.reg.arch_sp -= 0x10
arg1_ptr = ql.reg.arch_sp
ql.arch.regs.arch_sp -= 0x10
arg1_ptr = ql.arch.regs.arch_sp
ql.mem.write(arg1_ptr, b"000000") # arbitrary password
ql.reg.arch_sp -= 0x20
argv_ptr = ql.reg.arch_sp
ql.mem.write(argv_ptr, ql.pack(arg0_ptr))
ql.mem.write(argv_ptr + ql.pointersize, ql.pack(arg1_ptr))
ql.arch.regs.arch_sp -= 0x20
argv_ptr = ql.arch.regs.arch_sp
ql.mem.write_ptr(argv_ptr, arg0_ptr)
ql.mem.write_ptr(argv_ptr + ql.arch.pointersize, arg1_ptr)
ql.reg.r2 = 2
ql.reg.r3 = argv_ptr
ql.arch.regs.r2 = 2
ql.arch.regs.r3 = argv_ptr
with open("../examples/rootfs/blob/u-boot.bin.img", "rb") as f:
+5 -5
View File
@@ -12,16 +12,16 @@ from qiling.os.linux.syscall_nums import SYSCALL_NR
def write_onenter(ql: Qiling, arg1, arg2, arg3, *args):
print("enter write syscall!")
ql.reg.rsi = arg2 + 1
ql.reg.rdx = arg3 - 1
ql.arch.regs.rsi = arg2 + 1
ql.arch.regs.rdx = arg3 - 1
def write_onexit(ql: Qiling, arg1, arg2, arg3, *args):
print("exit write syscall!")
ql.reg.rax = arg3 + 1
ql.arch.regs.rax = arg3 + 1
if __name__ == "__main__":
ql = Qiling(["rootfs/x8664_linux/bin/x8664_hello"], "rootfs/x8664_linux", verbose=QL_VERBOSE.DEBUG)
ql.set_syscall(SYSCALL_NR.write, write_onenter, QL_INTERCEPT.ENTER)
ql.set_syscall(SYSCALL_NR.write, write_onexit, QL_INTERCEPT.EXIT)
ql.os.set_syscall(SYSCALL_NR.write, write_onenter, QL_INTERCEPT.ENTER)
ql.os.set_syscall(SYSCALL_NR.write, write_onexit, QL_INTERCEPT.EXIT)
ql.run()
@@ -23,7 +23,7 @@ def my_puts_onexit(ql: Qiling):
if __name__ == "__main__":
ql = Qiling(["rootfs/mips32el_linux/bin/mips32el_double_hello"], "rootfs/mips32el_linux", verbose=QL_VERBOSE.DEBUG)
ql.set_api('puts', my_puts_onenter, QL_INTERCEPT.ENTER)
ql.set_api('puts', my_puts_onexit, QL_INTERCEPT.EXIT)
ql.os.set_api('puts', my_puts_onenter, QL_INTERCEPT.ENTER)
ql.os.set_api('puts', my_puts_onexit, QL_INTERCEPT.EXIT)
ql.run()
+1 -1
View File
@@ -16,5 +16,5 @@ def my_puts(ql: Qiling):
if __name__ == "__main__":
ql = Qiling(["rootfs/x8664_linux/bin/x8664_hello"], "rootfs/x8664_linux", verbose=QL_VERBOSE.DEBUG)
ql.set_api('puts', my_puts)
ql.os.set_api('puts', my_puts)
ql.run()
+41 -12
View File
@@ -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)
nibbles = ql.archbit // 4
insn = next(md.disasm(buf, address))
esc_dgray = "\x1b[90m"
esc_reset = "\x1b[39m"
nibbles = ql.arch.bits // 4
color_faded = '\033[2m'
color_reset = '\033[0m'
for insn in md.disasm(buf, address):
opcode = ''.join(f'{b:02x}' for b in insn.bytes)
# 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])
# BUG: insn.regs_read doesn't work well, so we use insn.regs_access()[0] instead
reads = (f'{md.reg_name(reg)} = {ql.reg.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)}'
# 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)}'
# 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")
md = ql.create_disassembler()
# 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()
+3 -3
View File
@@ -39,9 +39,9 @@ def my_onexit(ql: Qiling, address: int, params, retval: int):
def my_sandbox(path, rootfs):
ql = Qiling(path, rootfs, verbose=QL_VERBOSE.DEBUG)
ql.set_api("_cexit", my_onenter, QL_INTERCEPT.ENTER)
ql.set_api("puts", my_puts, QL_INTERCEPT.CALL)
ql.set_api("atexit", my_onexit, QL_INTERCEPT.EXIT)
ql.os.set_api("_cexit", my_onenter, QL_INTERCEPT.ENTER)
ql.os.set_api("puts", my_puts, QL_INTERCEPT.CALL)
ql.os.set_api("atexit", my_onexit, QL_INTERCEPT.EXIT)
ql.run()
+1 -1
View File
@@ -21,7 +21,7 @@ delay_cycles_begin = 0x800015c
delay_cycles_end = 0x800018c
def skip_delay(ql):
ql.reg.pc = delay_cycles_end
ql.arch.regs.pc = delay_cycles_end
ql.hook_address(skip_delay, delay_cycles_begin)
ql.hw.gpioc.hook_set(13, lambda : print('Set PC13'))
+1 -1
View File
@@ -48,7 +48,7 @@ def crack(passwd):
ql.hw.systick.set_ratio(100)
ql.run(count=1000000, end=0x8003225)
if ql.arch.get_pc() == 0x8003225:
if ql.arch.effective_pc == 0x8003225:
print('Success, the passwd is', passwd)
else:
print('Fail, the passwd is not', passwd)
+2 -2
View File
@@ -28,8 +28,8 @@ ql.hw.systick.ratio = 1000
## a temporary method
def hook_smlabb(ql):
ql.reg.r3 = ql.reg.r2 + ql.reg.r1 * ql.reg.r3
ql.reg.pc = (ql.reg.pc + 4) | 1
ql.arch.regs.r3 = ql.arch.regs.r2 + ql.arch.regs.r1 * ql.arch.regs.r3
ql.arch.regs.pc = (ql.arch.regs.pc + 4) | 1
ql.hook_address(hook_smlabb, 0x8007a12)
ql.hook_address(hook_smlabb, 0x8007b60)
+1 -1
View File
@@ -42,7 +42,7 @@ if __name__ == "__main__":
delay_start = 0x8002936
delay_end = 0x8002955
def skip_delay(ql):
ql.reg.pc = delay_end
ql.arch.regs.pc = delay_end
ql.hook_address(skip_delay, delay_start)
ql.run(count=100000)
+2 -2
View File
@@ -61,8 +61,8 @@ def my_netgear(path, rootfs):
ql.os.root = False
ql.add_fs_mapper('/proc', '/proc')
ql.set_syscall(4004, my_syscall_write)
ql.set_api('bind', my_bind, QL_INTERCEPT.ENTER) # intercepting the bind call on enter
ql.os.set_syscall(4004, my_syscall_write)
ql.os.set_api('bind', my_bind, QL_INTERCEPT.ENTER) # intercepting the bind call on enter
ql.run()
+7 -7
View File
@@ -28,7 +28,7 @@ def one_round(ql: Qiling, key: bytes, key_address):
gkeys = generate_key(key)
ql.mem.write(key_address, gkeys)
ql.run(begin=verfication_start_ip, end=verfication_start_ip+6)
lba37 = ql.mem.read(ql.reg.sp + 0x220, 0x200)
lba37 = ql.mem.read(ql.arch.regs.sp + 0x220, 0x200)
for ch in lba37:
if ch != 0x37:
return False
@@ -62,12 +62,12 @@ def second_stage(ql: Qiling):
#nonce = get_nonce(disk)
verfication_data = disk.read_sectors(0x37, 1)
nonce_data = disk.read_sectors(0x36, 1)
ql.reg.sp -= 0x200
verification_data_address = ql.reg.sp
ql.reg.sp -= 0x200
nonce_address = ql.reg.sp + 0x21
ql.reg.sp -= 0x20
key_address = ql.reg.sp
ql.arch.regs.sp -= 0x200
verification_data_address = ql.arch.regs.sp
ql.arch.regs.sp -= 0x200
nonce_address = ql.arch.regs.sp + 0x21
ql.arch.regs.sp -= 0x20
key_address = ql.arch.regs.sp
ql.mem.write(verification_data_address, verfication_data)
ql.mem.write(nonce_address - 0x21, nonce_data)
ql.arch.stack_push(0x200)
+9 -9
View File
@@ -89,8 +89,8 @@ def _WriteFile(ql: Qiling, address: int, params):
if hFile == 0xfffffff5:
s = ql.mem.read(lpBuffer, nNumberOfBytesToWrite)
ql.os.stdout.write(s)
ql.os.utils.string_appearance(s.decode())
ql.mem.write(lpNumberOfBytesWritten, ql.pack(nNumberOfBytesToWrite))
ql.os.stats.log_string(s.decode())
ql.mem.write_ptr(lpNumberOfBytesWritten, nNumberOfBytesToWrite)
else:
f = ql.os.handle_manager.get(hFile)
if f is None:
@@ -101,7 +101,7 @@ def _WriteFile(ql: Qiling, address: int, params):
f = f.obj
buffer = ql.mem.read(lpBuffer, nNumberOfBytesToWrite)
f.write(bytes(buffer))
ql.mem.write(lpNumberOfBytesWritten, ql.pack32(nNumberOfBytesToWrite))
ql.mem.write_ptr(lpNumberOfBytesWritten, nNumberOfBytesToWrite, 4)
return ret
@winsdkapi(cc=STDCALL, params={
@@ -120,7 +120,7 @@ def hook_WriteFile(ql: Qiling, address: int, params):
buffer = ql.mem.read(lpBuffer, nNumberOfBytesToWrite)
try:
r, nNumberOfBytesToWrite = utils.io_Write(ql.amsint32_driver, buffer)
ql.mem.write(lpNumberOfBytesWritten, ql.pack32(nNumberOfBytesToWrite))
ql.mem.write_ptr(lpNumberOfBytesWritten, nNumberOfBytesToWrite, 4)
except Exception as e:
ql.log.exception("")
print("Exception = %s" % str(e))
@@ -170,7 +170,7 @@ def hook_StartServiceA(ql: Qiling, address: int, params):
def hook_stop_address(ql):
print(" >>>> Stop address: 0x%08x" % ql.reg.arch_pc)
print(" >>>> Stop address: 0x%08x" % ql.arch.regs.arch_pc)
ql.emu_stop()
@@ -181,10 +181,10 @@ if __name__ == "__main__":
ql.amsint32_driver = None
# emulate some Windows API
ql.set_api("CreateThread", hook_CreateThread)
ql.set_api("CreateFileA", hook_CreateFileA)
ql.set_api("WriteFile", hook_WriteFile)
ql.set_api("StartServiceA", hook_StartServiceA)
ql.os.set_api("CreateThread", hook_CreateThread)
ql.os.set_api("CreateFileA", hook_CreateFileA)
ql.os.set_api("WriteFile", hook_WriteFile)
ql.os.set_api("StartServiceA", hook_StartServiceA)
#init sality
ql.hook_address(hook_stop_address, 0x40EFFB)
ql.run()
+2 -2
View File
@@ -40,7 +40,7 @@ if __name__ == "__main__":
ql = Qiling(["rootfs/x8664_efi/bin/TcgPlatformSetupPolicy"], "rootfs/x8664_efi", env=env, verbose=QL_VERBOSE.DEBUG)
ql.set_api("RegisterProtocolNotify", force_notify_RegisterProtocolNotify)
ql.set_api("CopyMem", my_onenter, QL_INTERCEPT.ENTER)
ql.os.set_api("RegisterProtocolNotify", force_notify_RegisterProtocolNotify)
ql.os.set_api("CopyMem", my_onenter, QL_INTERCEPT.ENTER)
ql.run()
+1 -1
View File
@@ -75,7 +75,7 @@ def my_sandbox(path, rootfs):
ql.debugger = False
if ql.debugger == True:
ql.set_syscall("vfork", myvfork)
ql.os.set_syscall("vfork", myvfork)
ql.run()
+8 -8
View File
@@ -38,14 +38,14 @@ class colors:
def dump_regs(ql: Qiling):
regs = {
'eax': ql.reg.eax,
'ebx': ql.reg.ebx,
'ecx': ql.reg.ecx,
'edx': ql.reg.edx,
'edi': ql.reg.edi,
'esi': ql.reg.esi,
'ebp': ql.reg.ebp,
'esp': ql.reg.esp
'eax': ql.arch.regs.eax,
'ebx': ql.arch.regs.ebx,
'ecx': ql.arch.regs.ecx,
'edx': ql.arch.regs.edx,
'edi': ql.arch.regs.edi,
'esi': ql.arch.regs.esi,
'ebp': ql.arch.regs.ebp,
'esp': ql.arch.regs.esp
}
if not hasattr(dump_regs, 'regs'):
+54 -47
View File
@@ -3,8 +3,7 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from abc import ABC
from typing import Optional
from abc import abstractmethod
from unicorn import Uc
from unicorn.unicorn import UcContext
@@ -12,21 +11,37 @@ from capstone import Cs
from keystone import Ks
from qiling import Qiling
from qiling.const import QL_ARCH, QL_ENDIAN
from .register import QlRegisterManager
from .utils import QlArchUtils
class QlArch(ABC):
class QlArch:
type: QL_ARCH
bits: int
def __init__(self, ql: Qiling):
self.ql = ql
self.utils = QlArchUtils(ql)
self._disasm: Optional[Cs] = None
self._asm: Optional[Ks] = None
# ql.init_Uc - initialized unicorn engine
@property
def init_uc(self) -> Uc:
return self.get_init_uc()
@abstractmethod
def uc(self) -> Uc:
"""Get unicorn instance bound to arch.
"""
pass
@property
@abstractmethod
def regs(self) -> QlRegisterManager:
"""Architectural registers.
"""
pass
@property
def pointersize(self) -> int:
return self.bits // 8
def stack_push(self, value: int) -> int:
"""Push a value onto the architectural stack.
@@ -37,10 +52,10 @@ class QlArch(ABC):
Returns: the top of stack after pushing the value
"""
self.ql.reg.arch_sp -= self.ql.pointersize
self.ql.mem.write(self.ql.reg.arch_sp, self.ql.pack(value))
self.regs.arch_sp -= self.pointersize
self.ql.mem.write_ptr(self.regs.arch_sp, value)
return self.ql.reg.arch_sp
return self.regs.arch_sp
def stack_pop(self) -> int:
@@ -49,8 +64,8 @@ class QlArch(ABC):
Returns: the value at the top of stack
"""
data = self.ql.unpack(self.ql.mem.read(self.ql.reg.arch_sp, self.ql.pointersize))
self.ql.reg.arch_sp += self.ql.pointersize
data = self.ql.mem.read_ptr(self.regs.arch_sp)
self.regs.arch_sp += self.pointersize
return data
@@ -64,12 +79,12 @@ class QlArch(ABC):
Args:
offset: offset in bytes from the top of the stack, not necessarily aligned to the
native stack item size. the offset may be either positive or netagive, where
a 0 value means overwriting the value at the top of the stack
a 0 value means retrieving the value at the top of the stack
Returns: the value at the specified address
"""
return self.ql.unpack(self.ql.mem.read(self.ql.reg.arch_sp + offset, self.ql.pointersize))
return self.ql.mem.read_ptr(self.regs.arch_sp + offset)
def stack_write(self, offset: int, value: int) -> None:
@@ -84,48 +99,40 @@ class QlArch(ABC):
a 0 value means overwriting the value at the top of the stack
"""
self.ql.mem.write(self.ql.reg.arch_sp + offset, self.ql.pack(value))
# set PC
def set_pc(self, address: int) -> None:
self.ql.reg.arch_pc = address
# get PC
def get_pc(self) -> int:
return self.ql.reg.arch_pc
# set stack pointer
def set_sp(self, address: int) -> None:
self.ql.reg.arch_sp = address
# get stack pointer
def get_sp(self) -> int:
return self.ql.reg.arch_sp
self.ql.mem.write_ptr(self.regs.arch_sp + offset, value)
# Unicorn's CPU state save
def context_save(self) -> UcContext:
return self.ql.uc.context_save()
def save(self) -> UcContext:
return self.uc.context_save()
# Unicorn's CPU state restore method
def context_restore(self, saved_context: UcContext):
self.ql.uc.context_restore(saved_context)
def restore(self, saved_context: UcContext):
self.uc.context_restore(saved_context)
def create_disassembler(self) -> Cs:
"""Get disassembler insatnce bound to arch.
@property
@abstractmethod
def disassembler(self) -> Cs:
"""Get disassembler instance bound to arch.
"""
raise NotImplementedError(self.__class__.__name__)
pass
def create_assembler(self) -> Ks:
"""Get assembler insatnce bound to arch.
@property
@abstractmethod
def assembler(self) -> Ks:
"""Get assembler instance bound to arch.
"""
raise NotImplementedError(self.__class__.__name__)
pass
@property
@abstractmethod
def endian(self) -> QL_ENDIAN:
"""Get processor endianess.
"""
pass
+71 -79
View File
@@ -3,118 +3,110 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from functools import cached_property
from unicorn import Uc, UC_ARCH_ARM, UC_MODE_ARM, UC_MODE_THUMB, UC_MODE_BIG_ENDIAN
from capstone import Cs, CS_ARCH_ARM, CS_MODE_ARM, CS_MODE_THUMB
from keystone import Ks, KS_ARCH_ARM, KS_MODE_ARM, KS_MODE_THUMB
from capstone import Cs, CS_ARCH_ARM, CS_MODE_ARM, CS_MODE_THUMB, CS_MODE_BIG_ENDIAN
from keystone import Ks, KS_ARCH_ARM, KS_MODE_ARM, KS_MODE_THUMB, KS_MODE_BIG_ENDIAN
from qiling import Qiling
from qiling.const import QL_ARCH, QL_ENDIAN
from qiling.arch.arch import QlArch
from qiling.arch.arm_const import *
from qiling.exception import QlErrorArch
from qiling.arch import arm_const
from qiling.arch.register import QlRegisterManager
from qiling.const import QL_ARCH, QL_ENDIAN
class QlArchARM(QlArch):
def __init__(self, ql: Qiling):
type = QL_ARCH.ARM
bits = 32
def __init__(self, ql: Qiling, endian: QL_ENDIAN, thumb: bool):
super().__init__(ql)
reg_maps = (
reg_map,
)
for reg_maper in reg_maps:
self.ql.reg.expand_mapping(reg_maper)
self.ql.reg.register_sp(reg_map["sp"])
self.ql.reg.register_pc(reg_map["pc"])
self._init_endian = endian
self._init_thumb = thumb
self.arm_get_tls_addr = 0xFFFF0FE0
# get initialized unicorn engine
def get_init_uc(self) -> Uc:
if self.ql.archendian == QL_ENDIAN.EB:
mode = UC_MODE_ARM + UC_MODE_BIG_ENDIAN
@cached_property
def uc(self) -> Uc:
mode = UC_MODE_ARM
elif self.ql.archtype == QL_ARCH.ARM_THUMB:
mode = UC_MODE_THUMB
if self._init_endian == QL_ENDIAN.EB:
mode += UC_MODE_BIG_ENDIAN
elif self.ql.archtype == QL_ARCH.ARM:
mode = UC_MODE_ARM
else:
raise QlErrorArch(f'unsupported arch type {self.ql.archtype}')
if self._init_thumb:
mode += UC_MODE_THUMB
return Uc(UC_ARCH_ARM, mode)
@cached_property
def regs(self) -> QlRegisterManager:
regs_map = arm_const.reg_map
pc_reg = 'pc'
sp_reg = 'sp'
# get PC
def get_pc(self) -> int:
return self.ql.reg.pc | self.thumb
return QlRegisterManager(self.uc, regs_map, pc_reg, sp_reg)
@property
def thumb(self) -> int:
""" Thumb execution state bit """
return (self.ql.reg.cpsr >> 5) & 1
def is_thumb(self) -> bool:
return bool(self.regs.cpsr & (1 << 5))
def create_disassembler(self) -> Cs:
@property
def endian(self) -> QL_ENDIAN:
# FIXME: ARM is a bi-endian architecture which allows flipping core endianess
# while running. endianess is tested in runtime through CPSR[9], however unicorn
# doesn't reflect the endianess correctly through that bit.
# @see: https://github.com/unicorn-engine/unicorn/issues/1542
#
# we work around this by using the initial endianess configuration, even though
# it might have been changed since.
#
# return QL_ENDIAN.EB if self.regs.cpsr & (1 << 9) else QL_ENDIAN.EL
return self._init_endian
@property
def effective_pc(self) -> int:
"""Get effective PC value, taking Thumb mode into account.
"""
# append 1 to pc if in thumb mode, or 0 otherwise
return self.regs.pc + int(self.is_thumb)
@property
def disassembler(self) -> Cs:
# note: we do not cache the disassembler instance; rather we refresh it
# each time to make sure thumb mode is taken into account
# each time to make sure current endianess and thumb mode are taken into
# account
if self.ql.archtype == QL_ARCH.ARM:
# FIXME: mode should take endianess into account
mode = CS_MODE_THUMB if self.thumb else CS_MODE_ARM
mode = CS_MODE_ARM
elif self.ql.archtype == QL_ARCH.ARM_THUMB:
mode = CS_MODE_THUMB
if self.endian == QL_ENDIAN.EB:
mode += CS_MODE_BIG_ENDIAN
else:
raise QlErrorArch(f'unexpected arch type {self.ql.archtype}')
if self.is_thumb:
mode += CS_MODE_THUMB
return Cs(CS_ARCH_ARM, mode)
def create_assembler(self) -> Ks:
@property
def assembler(self) -> Ks:
# note: we do not cache the assembler instance; rather we refresh it
# each time to make sure thumb mode is taken into account
# each time to make sure current endianess and thumb mode are taken into
# account
if self.ql.archtype == QL_ARCH.ARM:
# FIXME: mode should take endianess into account
mode = KS_MODE_THUMB if self.thumb else KS_MODE_ARM
mode = KS_MODE_ARM
elif self.ql.archtype == QL_ARCH.ARM_THUMB:
mode = KS_MODE_THUMB
if self.endian == QL_ENDIAN.EB:
mode += KS_MODE_BIG_ENDIAN
else:
raise QlErrorArch(f'unexpected arch type {self.ql.archtype}')
if self.is_thumb:
mode += KS_MODE_THUMB
return Ks(KS_ARCH_ARM, mode)
def enable_vfp(self) -> None:
self.ql.reg.c1_c0_2 = self.ql.reg.c1_c0_2 | (0xf << 20)
# set full access to cp10 and cp11
self.regs.c1_c0_2 = self.regs.c1_c0_2 | (0b11 << 20) | (0b11 << 22)
if self.ql.archendian == QL_ENDIAN.EB:
self.ql.reg.fpexc = 0x40000000
#self.ql.reg.fpexc = 0x00000040
else:
self.ql.reg.fpexc = 0x40000000
"""
set_tls
"""
def init_get_tls(self):
self.ql.mem.map(0xFFFF0000, 0x1000, info="[arm_tls]")
"""
'adr r0, data; ldr r0, [r0]; mov pc, lr; data:.ascii "\x00\x00"'
"""
sc = b'\x04\x00\x8f\xe2\x00\x00\x90\xe5\x0e\xf0\xa0\xe1\x00\x00\x00\x00'
# if ql.archendian == QL_ENDIAN.EB:
# sc = swap_endianess(sc)
self.ql.mem.write(self.ql.arch.arm_get_tls_addr, sc)
self.ql.log.debug("Set init_kernel_get_tls")
def swap_endianess(self, s: bytes, blksize=4) -> bytes:
blocks = (s[i:i + blksize] for i in range(0, len(s), blksize))
return b''.join(bytes(reversed(b)) for b in blocks)
self.regs.fpexc = (1 << 30)
+31 -27
View File
@@ -3,44 +3,48 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from functools import cached_property
from unicorn import Uc, UC_ARCH_ARM64, UC_MODE_ARM
from capstone import Cs, CS_ARCH_ARM64, CS_MODE_ARM
from keystone import Ks, KS_ARCH_ARM64, KS_MODE_LITTLE_ENDIAN
from keystone import Ks, KS_ARCH_ARM64, KS_MODE_ARM
from qiling import Qiling
from qiling.arch.arch import QlArch
from qiling.arch.arm64_const import *
from qiling.arch import arm64_const
from qiling.arch.register import QlRegisterManager
from qiling.const import QL_ARCH, QL_ENDIAN
class QlArchARM64(QlArch):
def __init__(self, ql: Qiling):
super().__init__(ql)
type = QL_ARCH.ARM64
bits = 64
reg_maps = (
reg_map,
reg_map_w
)
for reg_maper in reg_maps:
self.ql.reg.expand_mapping(reg_maper)
self.ql.reg.register_sp(reg_map["sp"])
self.ql.reg.register_pc(reg_map["pc"])
# get initialized unicorn engine
def get_init_uc(self) -> Uc:
@cached_property
def uc(self) -> Uc:
return Uc(UC_ARCH_ARM64, UC_MODE_ARM)
def create_disassembler(self) -> Cs:
if self._disasm is None:
self._disasm = Cs(CS_ARCH_ARM64, CS_MODE_ARM)
@cached_property
def regs(self) -> QlRegisterManager:
regs_map = dict(
**arm64_const.reg_map,
**arm64_const.reg_map_w
)
return self._disasm
pc_reg = 'pc'
sp_reg = 'sp'
def create_assembler(self) -> Ks:
if self._asm is None:
self._asm = Ks(KS_ARCH_ARM64, KS_MODE_LITTLE_ENDIAN)
return QlRegisterManager(self.uc, regs_map, pc_reg, sp_reg)
return self._asm
@property
def endian(self) -> QL_ENDIAN:
return QL_ENDIAN.EL
@cached_property
def disassembler(self) -> Cs:
return Cs(CS_ARCH_ARM64, CS_MODE_ARM)
@cached_property
def assembler(self) -> Ks:
return Ks(KS_ARCH_ARM64, KS_MODE_ARM)
def enable_vfp(self):
self.ql.reg.cpacr_el1 = self.ql.reg.cpacr_el1 | 0x300000
self.regs.cpacr_el1 = self.regs.cpacr_el1 | 0x300000
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env python3
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from qiling import Qiling
def init_get_tls(ql: Qiling, address: int) -> None:
# adr r0, data
# ldr r0, [r0]
# mov pc, lr
#
# data:
# .ascii "\x00\x00"
code = bytes.fromhex('''
04 00 8f e2
00 00 90 e5
0e f0 a0 e1
00 00 00 00
''')
# if endian == QL_ENDIAN.EB:
# code = swap_endianess(code)
base = ql.mem.align(address)
size = ql.mem.align_up(len(code))
ql.mem.map(base, size, info="[arm_tls]")
ql.mem.write(address, code)
ql.log.debug('Set kernel get_tls')
# def swap_endianess(s: bytes, blksize: int = 4) -> bytes:
# blocks = (s[i:i + blksize] for i in range(0, len(s), blksize))
#
# return b''.join(bytes(reversed(b)) for b in blocks)
+53 -44
View File
@@ -3,94 +3,103 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from functools import cached_property
from contextlib import ContextDecorator
from unicorn import Uc, UC_ARCH_ARM, UC_MODE_ARM, UC_MODE_MCLASS, UC_MODE_THUMB
from capstone import Cs, CS_ARCH_ARM, CS_MODE_ARM, CS_MODE_MCLASS, CS_MODE_THUMB
from keystone import Ks, KS_ARCH_ARM, KS_MODE_ARM, KS_MODE_THUMB
from contextlib import ContextDecorator
from qiling.const import QL_VERBOSE
from qiling import Qiling
from qiling.arch.arm import QlArchARM
from qiling.arch import cortex_m_const
from qiling.arch.register import QlRegisterManager
from qiling.arch.cortex_m_const import IRQ, EXC_RETURN, CONTROL, EXCP
from qiling.const import QL_ARCH, QL_ENDIAN, QL_VERBOSE
from qiling.exception import QlErrorNotImplemented
from .arm import QlArchARM
from .cortex_m_const import IRQ, EXC_RETURN, CONTROL, EXCP, reg_map
class QlInterruptContext(ContextDecorator):
def __init__(self, ql):
def __init__(self, ql: Qiling):
self.ql = ql
self.reg_context = ['xpsr', 'pc', 'lr', 'r12', 'r3', 'r2', 'r1', 'r0']
def __enter__(self):
for reg in self.reg_context:
val = self.ql.reg.read(reg)
val = self.ql.arch.regs.read(reg)
self.ql.arch.stack_push(val)
if self.ql.verbose >= QL_VERBOSE.DISASM:
self.ql.log.info(f'Enter into interrupt')
def __exit__(self, *exc):
retval = self.ql.arch.get_pc()
retval = self.ql.arch.effective_pc
if retval & EXC_RETURN.MASK != EXC_RETURN.MASK:
self.ql.log.warning('Interrupt Crash')
self.ql.stop()
else:
# Exit handler mode
self.ql.reg.write('ipsr', 0)
self.ql.arch.regs.write('ipsr', 0)
# switch the stack accroding exc_return
old_ctrl = self.ql.reg.read('control')
old_ctrl = self.ql.arch.regs.read('control')
if retval & EXC_RETURN.RETURN_SP:
self.ql.reg.write('control', old_ctrl | CONTROL.SPSEL)
self.ql.arch.regs.write('control', old_ctrl | CONTROL.SPSEL)
else:
self.ql.reg.write('control', old_ctrl & ~CONTROL.SPSEL)
self.ql.arch.regs.write('control', old_ctrl & ~CONTROL.SPSEL)
# Restore stack
for reg in reversed(self.reg_context):
val = self.ql.arch.stack_pop()
if reg == 'xpsr':
self.ql.reg.write('XPSR_NZCVQG', val)
self.ql.arch.regs.write('XPSR_NZCVQG', val)
else:
self.ql.reg.write(reg, val)
self.ql.arch.regs.write(reg, val)
if self.ql.verbose >= QL_VERBOSE.DISASM:
self.ql.log.info('Exit from interrupt')
class QlArchCORTEX_M(QlArchARM):
def __init__(self, ql):
super().__init__(ql)
type = QL_ARCH.ARM
bits = 32
reg_maps = (
reg_map,
)
def __init__(self, ql: Qiling):
super().__init__(ql, endian=QL_ENDIAN.EL, thumb=True)
for reg_maper in reg_maps:
self.ql.reg.expand_mapping(reg_maper)
def get_init_uc(self):
@cached_property
def uc(self):
return Uc(UC_ARCH_ARM, UC_MODE_ARM + UC_MODE_MCLASS + UC_MODE_THUMB)
def create_disassembler(self) -> Cs:
@cached_property
def regs(self) -> QlRegisterManager:
regs_map = cortex_m_const.reg_map
pc_reg = 'pc'
sp_reg = 'sp'
return QlRegisterManager(self.uc, regs_map, pc_reg, sp_reg)
@cached_property
def disassembler(self) -> Cs:
return Cs(CS_ARCH_ARM, CS_MODE_ARM + CS_MODE_MCLASS + CS_MODE_THUMB)
def create_assembler(self) -> Ks:
@cached_property
def assembler(self) -> Ks:
return Ks(KS_ARCH_ARM, KS_MODE_ARM + KS_MODE_THUMB)
@property
def thumb(self):
return 1
def is_thumb(self):
return True
def is_handler_mode(self):
return self.ql.reg.read('ipsr') > 1
return self.regs.ipsr > 1
def using_psp(self):
return not self.is_handler_mode() and (self.ql.reg.read('control') & CONTROL.SPSEL) > 0
return not self.is_handler_mode() and (self.regs.control & CONTROL.SPSEL) > 0
def init_context(self):
self.ql.reg.write('lr', 0xffffffff)
self.ql.reg.write('msp', self.ql.mem.read_ptr(0x0))
self.ql.reg.write('pc' , self.ql.mem.read_ptr(0x4))
self.regs.lr = 0xffffffff
self.regs.msp = self.ql.mem.read_ptr(0x0)
self.regs.pc = self.ql.mem.read_ptr(0x4)
def unicorn_exception_handler(self, ql, intno):
forward_mapper = {
@@ -124,28 +133,28 @@ class QlArchCORTEX_M(QlArchARM):
raise QlErrorNotImplemented(f'Unhandled interrupt number ({intno})')
def interrupt_handler(self, ql, intno):
basepri = self.ql.reg.read('basepri') & 0xf0
basepri = self.regs.basepri & 0xf0
if basepri and basepri <= ql.hw.nvic.get_priority(intno):
return
if intno > IRQ.HARD_FAULT and (ql.reg.read('primask') & 0x1):
if intno > IRQ.HARD_FAULT and (self.regs.primask & 0x1):
return
if intno != IRQ.NMI and (ql.reg.read('faultmask') & 0x1):
if intno != IRQ.NMI and (self.regs.faultmask & 0x1):
return
if ql.verbose >= QL_VERBOSE.DISASM:
ql.log.debug(f'Handle the intno: {intno}')
with QlInterruptContext(ql):
isr = intno + 16
offset = isr * 4
entry = ql.mem.read_ptr(offset)
exc_return = 0xFFFFFFFD if self.ql.arch.using_psp() else 0xFFFFFFF9
exc_return = 0xFFFFFFFD if self.using_psp() else 0xFFFFFFF9
self.ql.reg.write('ipsr', isr)
self.ql.reg.write('pc', entry)
self.ql.reg.write('lr', exc_return)
self.regs.write('ipsr', isr)
self.regs.write('pc', entry)
self.regs.write('lr', exc_return)
self.ql.emu_start(self.ql.arch.get_pc(), 0, count=0xffffff)
self.ql.emu_start(self.effective_pc, 0, count=0xffffff)
+9 -1
View File
@@ -9,6 +9,9 @@ from .vm.evm import QlArchEVMEmulator
class QlArchEVM(QlArch):
type = QL_ARCH.EVM
bits = 1
def __init__(self, ql) -> None:
super(QlArchEVM, self).__init__(ql)
self.evm = QlArchEVMEmulator(self.ql)
@@ -28,5 +31,10 @@ class QlArchEVM(QlArch):
def stack_write(self, offset, data):
return None
def get_init_uc(self):
@property
def uc(self):
return None
@property
def endian(self) -> QL_ENDIAN:
return QL_ENDIAN.EL
+41 -32
View File
@@ -3,57 +3,66 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from functools import cached_property
from unicorn import Uc, UC_ARCH_MIPS, UC_MODE_MIPS32, UC_MODE_BIG_ENDIAN, UC_MODE_LITTLE_ENDIAN
from capstone import Cs, CS_ARCH_MIPS, CS_MODE_MIPS32, CS_MODE_BIG_ENDIAN, CS_MODE_LITTLE_ENDIAN
from keystone import Ks, KS_ARCH_MIPS, KS_MODE_MIPS32, KS_MODE_BIG_ENDIAN, KS_MODE_LITTLE_ENDIAN
from qiling import Qiling
from qiling.const import QL_ENDIAN
from qiling.arch.arch import QlArch
from qiling.arch.mips_const import *
from qiling.arch import mips_const
from qiling.arch.register import QlRegisterManager
from qiling.const import QL_ARCH, QL_ENDIAN
class QlArchMIPS(QlArch):
def __init__(self, ql: Qiling):
type = QL_ARCH.MIPS
bits = 32
def __init__(self, ql: Qiling, endian: QL_ENDIAN):
super().__init__(ql)
reg_maps = (
reg_map,
reg_map_afpr128
)
self._init_endian = endian
for reg_maper in reg_maps:
self.ql.reg.expand_mapping(reg_maper)
self.ql.reg.register_sp(reg_map["sp"])
self.ql.reg.register_pc(reg_map["pc"])
# get initialized unicorn engine
def get_init_uc(self) -> Uc:
@cached_property
def uc(self) -> Uc:
endian = {
QL_ENDIAN.EB: UC_MODE_BIG_ENDIAN,
QL_ENDIAN.EL: UC_MODE_LITTLE_ENDIAN
}[self.ql.archendian]
}[self.endian]
return Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + endian)
def create_disassembler(self) -> Cs:
if self._disasm is None:
endian = {
QL_ENDIAN.EL : CS_MODE_LITTLE_ENDIAN,
QL_ENDIAN.EB : CS_MODE_BIG_ENDIAN
}[self.ql.archendian]
@cached_property
def regs(self) -> QlRegisterManager:
regs_map = dict(
**mips_const.reg_map,
**mips_const.reg_map_afpr128
)
self._disasm = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + endian)
pc_reg = 'pc'
sp_reg = 'sp'
return self._disasm
return QlRegisterManager(self.uc, regs_map, pc_reg, sp_reg)
def create_assembler(self) -> Ks:
if self._asm is None:
endian = {
QL_ENDIAN.EL : KS_MODE_LITTLE_ENDIAN,
QL_ENDIAN.EB : KS_MODE_BIG_ENDIAN
}[self.ql.archendian]
@cached_property
def disassembler(self) -> Cs:
endian = {
QL_ENDIAN.EL : CS_MODE_LITTLE_ENDIAN,
QL_ENDIAN.EB : CS_MODE_BIG_ENDIAN
}[self.endian]
self._asm = Ks(KS_ARCH_MIPS, KS_MODE_MIPS32 + endian)
return Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + endian)
return self._asm
@cached_property
def assembler(self) -> Ks:
endian = {
QL_ENDIAN.EL : KS_MODE_LITTLE_ENDIAN,
QL_ENDIAN.EB : KS_MODE_BIG_ENDIAN
}[self.endian]
return Ks(KS_ARCH_MIPS, KS_MODE_MIPS32 + endian)
@property
def endian(self) -> QL_ENDIAN:
return self._init_endian
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env python3
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from unicorn import Uc
class QlMsrManager:
"""Enables access to Intel MSR.
"""
def __init__(self, uc: Uc) -> None:
self.uc = uc
def read(self, msr: int) -> int:
"""Read a model-specific register value.
"""
return self.uc.msr_read(msr)
def write(self, msr: int, value: int):
"""Write a model-specific register value.
"""
self.uc.msr_write(msr, value)
+25 -59
View File
@@ -5,31 +5,37 @@
from typing import Any, Mapping, MutableMapping, Union
from qiling import Qiling
from unicorn import Uc
class QlRegisterManager:
"""This class exposes the ql.reg features that allows you to directly access
"""This class exposes the ql.arch.regs features that allows you to directly access
or assign values to CPU registers of a particular architecture.
Registers exposed are listed in the *_const.py files in the respective
arch directories and are mapped to Unicorn Engine's definitions
"""
def __init__(self, ql: Qiling):
def __init__(self, uc: Uc, regs_map: Mapping[str, int], pc_reg: str, sp_reg: str):
"""Initialize the registers manager.
Args:
uc: initialized unicorn instance
regs_map: registers names mapped to their corresponding unicorn definitions
pc_reg: name of the architectural program counter register
sp_reg: name of the architectural stack pointer register
"""
# this funny way of initialization is used to avoid calling self setattr and
# getattr upon init. if it did, it would go into an endless recursion
self.register_mapping: MutableMapping[str, int]
super().__setattr__('register_mapping', {})
self.register_mapping: Mapping[str, int]
super().__setattr__('register_mapping', regs_map)
self.ql = ql
self.uc_pc = 0
self.uc_sp = 0
self.uc = uc
self.uc_pc = self.register_mapping[pc_reg]
self.uc_sp = self.register_mapping[sp_reg]
def __getattr__(self, name: str) -> Any:
name = name.lower()
if name in self.register_mapping:
return self.ql.uc.reg_read(self.register_mapping[name])
return self.uc.reg_read(self.register_mapping[name])
else:
return super().__getattribute__(name)
@@ -39,19 +45,12 @@ class QlRegisterManager:
name = name.lower()
if name in self.register_mapping:
self.ql.uc.reg_write(self.register_mapping[name], value)
self.uc.reg_write(self.register_mapping[name], value)
else:
super().__setattr__(name, value)
def expand_mapping(self, extra: Mapping[str, int]) -> None:
"""Expand registers mapping with additional ones.
"""
self.register_mapping.update(extra)
# read register
def read(self, register: Union[str, int]):
"""Read a register value.
@@ -60,7 +59,7 @@ class QlRegisterManager:
if type(register) is str:
register = self.register_mapping[register.lower()]
return self.ql.uc.reg_read(register)
return self.uc.reg_read(register)
def write(self, register: Union[str, int], value: int) -> None:
@@ -70,18 +69,7 @@ class QlRegisterManager:
if type(register) is str:
register = self.register_mapping[register.lower()]
return self.ql.uc.reg_write(register, value)
def msr(self, msr: int, value: int = None):
"""Read or write a model-specific register (MSR) value.
Intel architecture only
"""
if value is None:
return self.ql.uc.msr_read(msr)
self.ql.uc.msr_write(msr, value)
return self.uc.reg_write(register, value)
def save(self) -> MutableMapping[str, Any]:
@@ -99,34 +87,12 @@ class QlRegisterManager:
self.write(reg, val)
# TODO: This needs to be implemented for all archs
def bit(self, reg: Union[str, int]) -> int:
"""Get register size in bits.
"""
if type(reg) is str:
reg = self.register_mapping[reg]
return self.ql.arch.get_reg_bit(reg)
# Generic methods to get SP and IP across Arch's #
# These functions should only be used if the #
# caller is dealing with multiple Arch's #
def register_sp(self, sp_id: int):
self.uc_sp = sp_id
def register_pc(self, pc_id: int):
self.uc_pc = pc_id
@property
def arch_pc(self) -> int:
"""Get the value of the architectural program counter register.
"""
return self.ql.uc.reg_read(self.uc_pc)
return self.uc.reg_read(self.uc_pc)
@arch_pc.setter
@@ -134,7 +100,7 @@ class QlRegisterManager:
"""Set the value of the architectural program counter register.
"""
return self.ql.uc.reg_write(self.uc_pc, value)
return self.uc.reg_write(self.uc_pc, value)
@property
def arch_pc_name(self) -> str:
@@ -148,7 +114,7 @@ class QlRegisterManager:
"""Get the value of the architectural stack pointer register.
"""
return self.ql.uc.reg_read(self.uc_sp)
return self.uc.reg_read(self.uc_sp)
@arch_sp.setter
@@ -156,4 +122,4 @@ class QlRegisterManager:
"""Set the value of the architectural stack pointer register.
"""
return self.ql.uc.reg_write(self.uc_sp, value)
return self.uc.reg_write(self.uc_sp, value)
+35 -23
View File
@@ -3,54 +3,66 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from functools import cached_property
from unicorn import Uc, UC_ARCH_RISCV, UC_MODE_RISCV32
from capstone import Cs
from keystone import Ks
from qiling import Qiling
from qiling.arch.arch import QlArch
from qiling.arch.register import QlRegisterManager
from qiling.arch import riscv_const
from qiling.arch.riscv_const import *
from qiling.const import QL_ARCH, QL_ENDIAN
from qiling.exception import QlErrorNotImplemented
class QlArchRISCV(QlArch):
def __init__(self, ql: Qiling):
super().__init__(ql)
type = QL_ARCH.RISCV
bits = 32
reg_maps = (
reg_map,
reg_csr_map,
reg_float_map,
)
for reg_maper in reg_maps:
self.ql.reg.expand_mapping(reg_maper)
self.ql.reg.register_sp(reg_map["sp"])
self.ql.reg.register_pc(reg_map["pc"])
# get initialized unicorn engine
def get_init_uc(self) -> Uc:
@cached_property
def uc(self) -> Uc:
return Uc(UC_ARCH_RISCV, UC_MODE_RISCV32)
def create_disassembler(self) -> Cs:
@cached_property
def regs(self) -> QlRegisterManager:
regs_map = dict(
**riscv_const.reg_map,
**riscv_const.reg_csr_map,
**riscv_const.reg_float_map,
)
pc_reg = 'pc'
sp_reg = 'sp'
return QlRegisterManager(self.uc, regs_map, pc_reg, sp_reg)
@property
def endian(self) -> QL_ENDIAN:
return QL_ENDIAN.EL
@cached_property
def disassembler(self) -> Cs:
try:
from capstone import CS_ARCH_RISCV, CS_MODE_RISCV32, CS_MODE_RISCVC
return Cs(CS_ARCH_RISCV, CS_MODE_RISCV32 + CS_MODE_RISCVC)
except ImportError:
raise QlErrorNotImplemented("Capstone does not yet support riscv, upgrade to capstone 5.0")
else:
return Cs(CS_ARCH_RISCV, CS_MODE_RISCV32 + CS_MODE_RISCVC)
def create_assembler(self) -> Ks:
@cached_property
def assembler(self) -> Ks:
raise QlErrorNotImplemented("Keystone does not yet support riscv")
def enable_float(self):
self.ql.reg.mstatus = self.ql.reg.mstatus | MSTATUS.FS_DIRTY
self.regs.mstatus = self.regs.mstatus | MSTATUS.FS_DIRTY
def init_context(self):
self.ql.reg.pc = 0x08000000
self.regs.pc = 0x08000000
def unicorn_exception_handler(self, ql, intno):
if intno == 2:
ql.log.warning(f'[{hex(self.get_pc())}] Illegal instruction')
ql.log.warning(f'[{hex(self.regs.arch_pc)}] Illegal instruction')
else:
raise QlErrorNotImplemented(f'Unhandled interrupt number ({intno})')
+13 -9
View File
@@ -3,31 +3,35 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from functools import cached_property
from unicorn import Uc, UC_ARCH_RISCV, UC_MODE_RISCV64
from capstone import Cs
from keystone import Ks
from qiling import Qiling
from qiling.arch.riscv_const import *
from qiling.const import QL_ARCH
from qiling.exception import QlErrorNotImplemented
from .riscv import QlArchRISCV
class QlArchRISCV64(QlArchRISCV):
def __init__(self, ql: Qiling):
super().__init__(ql)
type = QL_ARCH.RISCV64
bits = 64
# get initialized unicorn engine
def get_init_uc(self) -> Uc:
@cached_property
def uc(self) -> Uc:
return Uc(UC_ARCH_RISCV, UC_MODE_RISCV64)
def create_disassembler(self) -> Cs:
@cached_property
def disassembler(self) -> Cs:
try:
from capstone import CS_ARCH_RISCV, CS_MODE_RISCV64, CS_MODE_RISCVC
return Cs(CS_ARCH_RISCV, CS_MODE_RISCV64 + CS_MODE_RISCVC)
except ImportError:
raise QlErrorNotImplemented("Capstone does not yet support riscv, upgrade to capstone 5.0")
else:
return Cs(CS_ARCH_RISCV, CS_MODE_RISCV64 + CS_MODE_RISCVC)
def create_assembler(self) -> Ks:
@cached_property
def assembler(self) -> Ks:
raise QlErrorNotImplemented("Keystone does not yet support riscv")
+43 -25
View File
@@ -9,6 +9,7 @@ This module is intended for general purpose functions that are only used in qili
from typing import Tuple
from os.path import basename
from functools import lru_cache
from keystone import (Ks, KS_ARCH_ARM, KS_ARCH_ARM64, KS_ARCH_MIPS, KS_ARCH_X86,
KS_MODE_ARM, KS_MODE_THUMB, KS_MODE_MIPS32, KS_MODE_16, KS_MODE_32, KS_MODE_64,
@@ -24,52 +25,68 @@ class QlArchUtils:
self._disasm_hook = None
self._block_hook = None
def get_offset_and_name(self, addr: int) -> Tuple[int, str]:
@lru_cache(maxsize=64)
def get_base_and_name(self, addr: int) -> Tuple[int, str]:
for begin, end, _, name, _ in self.ql.mem.map_info:
if begin <= addr < end:
return addr - begin, basename(name)
return begin, basename(name)
return addr, '-'
def disassembler(self, ql: Qiling, address: int, size: int):
tmp = ql.mem.read(address, size)
qd = ql.arch.create_disassembler()
data = ql.mem.read(address, size)
offset, name = self.get_offset_and_name(address)
log_data = f'{address:0{ql.archbit // 4}x} [{name:20s} + {offset:#08x}] {tmp.hex(" "):30s}'
log_insn = '\n> '.join(f'{insn.mnemonic:20s} {insn.op_str}' for insn in qd.disasm(tmp, address))
# knowing that all binary sections are aligned to page boundary allows
# us to 'cheat' and search for the containing image using the aligned
# address instead of the actual one.
#
# also, the locality property determines that consequent instructions
# are most likely to reside at the same page in memory, so the containing
# page of the current instruction is probably the same as the previous
# one.
#
# both assumptions make it possible to cache the search results and pull
# them off by lru, which provides about 20% speed-up in this case
ba, name = self.get_base_and_name(ql.mem.align(address))
ql.log.info(log_data + log_insn)
anibbles = ql.arch.bits // 4
for insn in ql.arch.disassembler.disasm(data, address):
offset = insn.address - ba
ql.log.info(f'{insn.address:0{anibbles}x} [{name:20s} + {offset:#08x}] {insn.bytes.hex(" "):20s} {insn.mnemonic:20s} {insn.op_str}')
if ql.verbose >= QL_VERBOSE.DUMP:
for reg in ql.reg.register_mapping:
if type(reg) is str:
ql.log.debug(f'{reg}\t: {ql.reg.read(reg):#x}')
for reg in ql.arch.regs.register_mapping:
ql.log.info(f'{reg:10s} : {ql.arch.regs.read(reg):#x}')
def setup_output(self):
def ql_hook_block_disasm(ql, address, size):
self.ql.log.info("\nTracing basic block at 0x%x" % (address))
def setup_output(self, verbosity: QL_VERBOSE):
def ql_hook_block_disasm(ql: Qiling, address: int, size: int):
self.ql.log.info(f'\nTracing basic block at {address:#x}')
if self._disasm_hook:
self._disasm_hook.remove()
self._disasm_hook = None
if self._block_hook:
self._block_hook.remove()
self._block_hook = None
if self.ql.verbose >= QL_VERBOSE.DISASM:
if self.ql.verbose >= QL_VERBOSE.DUMP:
self._block_hook = self.ql.hook_block(ql_hook_block_disasm)
if verbosity >= QL_VERBOSE.DISASM:
self._disasm_hook = self.ql.hook_code(self.disassembler)
if verbosity >= QL_VERBOSE.DUMP:
self._block_hook = self.ql.hook_block(ql_hook_block_disasm)
# used by qltool prior to ql instantiation. to get an assembler object
# after ql instantiation, use the appropriate ql.arch method
def assembler(arch: QL_ARCH, endianess: QL_ENDIAN) -> Ks:
def assembler(arch: QL_ARCH, endianess: QL_ENDIAN, is_thumb: bool) -> Ks:
"""Instantiate an assembler object for a specified architecture.
Args:
arch: architecture type
endianess: architecture endianess
is_thumb: thumb mode for ARM (ignored otherwise)
Returns: an assembler object
"""
@@ -79,14 +96,15 @@ def assembler(arch: QL_ARCH, endianess: QL_ENDIAN) -> Ks:
QL_ENDIAN.EB : KS_MODE_BIG_ENDIAN
}[endianess]
thumb = KS_MODE_THUMB if is_thumb else 0
asm_map = {
QL_ARCH.ARM : (KS_ARCH_ARM, KS_MODE_ARM),
QL_ARCH.ARM64 : (KS_ARCH_ARM64, KS_MODE_LITTLE_ENDIAN),
QL_ARCH.ARM_THUMB : (KS_ARCH_ARM, KS_MODE_THUMB),
QL_ARCH.MIPS : (KS_ARCH_MIPS, KS_MODE_MIPS32 + endian),
QL_ARCH.A8086 : (KS_ARCH_X86, KS_MODE_16),
QL_ARCH.X86 : (KS_ARCH_X86, KS_MODE_32),
QL_ARCH.X8664 : (KS_ARCH_X86, KS_MODE_64)
QL_ARCH.ARM : (KS_ARCH_ARM, KS_MODE_ARM + endian + thumb),
QL_ARCH.ARM64 : (KS_ARCH_ARM64, KS_MODE_ARM),
QL_ARCH.MIPS : (KS_ARCH_MIPS, KS_MODE_MIPS32 + endian),
QL_ARCH.A8086 : (KS_ARCH_X86, KS_MODE_16),
QL_ARCH.X86 : (KS_ARCH_X86, KS_MODE_32),
QL_ARCH.X8664 : (KS_ARCH_X86, KS_MODE_64)
}
if arch in asm_map:
+132 -247
View File
@@ -3,271 +3,156 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from struct import pack
from functools import cached_property
from typing import Union
from unicorn import Uc, UC_ARCH_X86, UC_MODE_16, UC_MODE_32, UC_MODE_64
from capstone import Cs, CS_ARCH_X86, CS_MODE_16, CS_MODE_32, CS_MODE_64
from keystone import Ks, KS_ARCH_X86, KS_MODE_16, KS_MODE_32, KS_MODE_64
from qiling import Qiling
from unicorn.x86_const import UC_X86_REG_EFLAGS
from qiling.arch.arch import QlArch
from qiling.arch.x86_const import *
from qiling.exception import QlGDTError
from qiling.arch.msr import QlMsrManager
from qiling.arch.register import QlRegisterManager
from qiling.arch import x86_const
from qiling.const import QL_ARCH, QL_ENDIAN
class QlArchIntel(QlArch):
@property
def endian(self) -> QL_ENDIAN:
return QL_ENDIAN.EL
@cached_property
def msr(self) -> QlMsrManager:
"""Model-Specific Registers.
"""
return QlMsrManager(self.uc)
class QlArchA8086(QlArchIntel):
type = QL_ARCH.A8086
bits = 16
@cached_property
def uc(self) -> Uc:
return Uc(UC_ARCH_X86, UC_MODE_16)
@cached_property
def regs(self) -> QlRegisterManager:
regs_map = dict(
**x86_const.reg_map_8,
**x86_const.reg_map_16,
**x86_const.reg_map_misc
)
pc_reg = 'ip'
sp_reg = 'sp'
return QlRegisterManager(self.uc, regs_map, pc_reg, sp_reg)
@cached_property
def disassembler(self) -> Cs:
return Cs(CS_ARCH_X86, CS_MODE_16)
@cached_property
def assembler(self) -> Ks:
return Ks(KS_ARCH_X86, KS_MODE_16)
class QlArchX86(QlArchIntel):
type = QL_ARCH.X86
bits = 32
@cached_property
def uc(self) -> Uc:
return Uc(UC_ARCH_X86, UC_MODE_32)
@cached_property
def regs(self) -> QlRegisterManager:
regs_map = dict(
**x86_const.reg_map_8,
**x86_const.reg_map_16,
**x86_const.reg_map_32,
**x86_const.reg_map_cr,
**x86_const.reg_map_st,
**x86_const.reg_map_misc
)
pc_reg = 'eip'
sp_reg = 'esp'
return QlRegisterManager(self.uc, regs_map, pc_reg, sp_reg)
@cached_property
def disassembler(self) -> Cs:
return Cs(CS_ARCH_X86, CS_MODE_32)
@cached_property
def assembler(self) -> Ks:
return Ks(KS_ARCH_X86, KS_MODE_32)
class QlArchX8664(QlArchIntel):
type = QL_ARCH.X8664
bits = 64
@cached_property
def uc(self) -> Uc:
return Uc(UC_ARCH_X86, UC_MODE_64)
@cached_property
def regs(self) -> QlRegisterManager:
regs_map = dict(
**x86_const.reg_map_8,
**x86_const.reg_map_16,
**x86_const.reg_map_32,
**x86_const.reg_map_64,
**x86_const.reg_map_cr,
**x86_const.reg_map_st,
**x86_const.reg_map_misc,
**x86_const.reg_map_64_b,
**x86_const.reg_map_64_w,
**x86_const.reg_map_64_d,
**x86_const.reg_map_seg_base
)
pc_reg = 'rip'
sp_reg = 'rsp'
return QlRegisterManager(self.uc, regs_map, pc_reg, sp_reg)
@cached_property
def disassembler(self) -> Cs:
return Cs(CS_ARCH_X86, CS_MODE_64)
@cached_property
def assembler(self) -> Ks:
return Ks(KS_ARCH_X86, KS_MODE_64)
# TODO: generalize this
def get_reg_bit(self, register: int) -> int:
def __reg_bits(self, register: int) -> int:
# all regs in reg_map_misc are 16 bits except of eflags
if register == UC_X86_REG_EFLAGS:
return 32
regmaps = (
(reg_map_8, 8),
(reg_map_16, 16),
(reg_map_32, 32),
(reg_map_64, 64),
(reg_map_misc, 16),
(reg_map_cr, 64 if self.ql.archbit == 64 else 32),
(reg_map_st, 32),
(reg_map_seg_base, 64 if self.ql.archbit == 64 else 32),
(x86_const.reg_map_8, 8),
(x86_const.reg_map_16, 16),
(x86_const.reg_map_32, 32),
(x86_const.reg_map_64, 64),
(x86_const.reg_map_misc, 16),
(x86_const.reg_map_cr, 64), # 32 bits for x86
(x86_const.reg_map_st, 32),
(x86_const.reg_map_seg_base, 64), # 32 bits for x86
)
return next((rsize for rmap, rsize in regmaps if register in rmap.values()), 0)
class QlArchA8086(QlArchIntel):
def __init__(self, ql: Qiling):
super().__init__(ql)
# note: this method was not generalized for all archs since it requires a bookkeeping
# of all registers, while it is used only by gdb and only for x86-64
def reg_bits(self, reg: Union[str, int]) -> int:
"""Get register size in bits.
"""
reg_maps = (
reg_map_8,
reg_map_16,
reg_map_misc
)
if type(reg) is str:
reg = self.regs.register_mapping[reg]
for reg_maper in reg_maps:
self.ql.reg.expand_mapping(reg_maper)
self.ql.reg.register_pc(reg_map_16["sp"])
self.ql.reg.register_sp(reg_map_16["ip"])
def get_init_uc(self) -> Uc:
return Uc(UC_ARCH_X86, UC_MODE_16)
def create_disassembler(self) -> Cs:
if not self._disasm:
self._disasm = Cs(CS_ARCH_X86, CS_MODE_16)
return self._disasm
def create_assembler(self) -> Ks:
if not self._asm:
self._asm = Ks(KS_ARCH_X86, KS_MODE_16)
return self._asm
class QlArchX86(QlArchIntel):
def __init__(self, ql: Qiling):
super().__init__(ql)
reg_maps = (
reg_map_8,
reg_map_16,
reg_map_32,
reg_map_cr,
reg_map_st,
reg_map_misc
)
for reg_maper in reg_maps:
self.ql.reg.expand_mapping(reg_maper)
self.ql.reg.register_sp(reg_map_32["esp"])
self.ql.reg.register_pc(reg_map_32["eip"])
def get_init_uc(self) -> Uc:
return Uc(UC_ARCH_X86, UC_MODE_32)
def create_disassembler(self) -> Cs:
if not self._disasm:
self._disasm = Cs(CS_ARCH_X86, CS_MODE_32)
return self._disasm
def create_assembler(self) -> Ks:
if not self._asm:
self._asm = Ks(KS_ARCH_X86, KS_MODE_32)
return self._asm
class QlArchX8664(QlArchIntel):
def __init__(self, ql: Qiling):
super().__init__(ql)
reg_maps = (
reg_map_8,
reg_map_16,
reg_map_32,
reg_map_64,
reg_map_cr,
reg_map_st,
reg_map_misc,
reg_map_64_b,
reg_map_64_w,
reg_map_64_d,
reg_map_seg_base
)
for reg_maper in reg_maps:
self.ql.reg.expand_mapping(reg_maper)
self.ql.reg.register_sp(reg_map_64["rsp"])
self.ql.reg.register_pc(reg_map_64["rip"])
def get_init_uc(self) -> Uc:
return Uc(UC_ARCH_X86, UC_MODE_64)
def create_disassembler(self) -> Cs:
if not self._disasm:
self._disasm = Cs(CS_ARCH_X86, CS_MODE_64)
return self._disasm
def create_assembler(self) -> Ks:
if not self._asm:
self._asm = Ks(KS_ARCH_X86, KS_MODE_64)
return self._asm
class GDTManager:
# Added GDT management module.
def __init__(self, ql: Qiling, GDT_ADDR = QL_X86_GDT_ADDR, GDT_LIMIT = QL_X86_GDT_LIMIT, GDT_ENTRY_ENTRIES = 16):
ql.log.debug(f"Map GDT at {hex(GDT_ADDR)} with GDT_LIMIT={GDT_LIMIT}")
if not ql.mem.is_mapped(GDT_ADDR, GDT_LIMIT):
ql.mem.map(GDT_ADDR, GDT_LIMIT, info="[GDT]")
# setup GDT by writing to GDTR
ql.reg.write(UC_X86_REG_GDTR, (0, GDT_ADDR, GDT_LIMIT, 0x0))
self.ql = ql
self.gdt_number = GDT_ENTRY_ENTRIES
# self.gdt_used = [False] * GDT_ENTRY_ENTRIES
self.gdt_addr = GDT_ADDR
self.gdt_limit = GDT_LIMIT
def register_gdt_segment(self, index: int, SEGMENT_ADDR: int, SEGMENT_SIZE: int, SPORT, RPORT):
# FIXME: Temp fix for FS and GS
if index in (14, 15):
if not self.ql.mem.is_mapped(SEGMENT_ADDR, SEGMENT_ADDR):
self.ql.mem.map(SEGMENT_ADDR, SEGMENT_ADDR, info="[FS/GS]")
if index < 0 or index >= self.gdt_number:
raise QlGDTError("Ql GDT register index error!")
# create GDT entry, then write GDT entry into GDT table
gdt_entry = self._create_gdt_entry(SEGMENT_ADDR, SEGMENT_SIZE, SPORT, QL_X86_F_PROT_32)
self.ql.mem.write(self.gdt_addr + (index << 3), gdt_entry)
# self.gdt_used[index] = True
self.ql.log.debug(f"Write to {hex(self.gdt_addr + (index << 3))} for new entry {gdt_entry}")
def get_gdt_buf(self, start: int, end: int) -> bytearray:
return self.ql.mem.read(self.gdt_addr + (start << 3), (end << 3) - (start << 3))
def set_gdt_buf(self, start: int, end: int, buf: bytes) -> None:
self.ql.mem.write(self.gdt_addr + (start << 3), buf[ : (end << 3) - (start << 3)])
def get_free_idx(self, start: int = 0, end: int = -1) -> int:
# The Linux kernel determines whether the segment is empty by judging whether the content in the current GDT segment is 0.
if end == -1:
end = self.gdt_number
for i in range(start, end):
if self.ql.unpack64(self.ql.mem.read(self.gdt_addr + (i << 3), 8)) == 0:
return i
return -1
def _create_gdt_entry(self, base, limit, access, flags):
to_ret = limit & 0xffff
to_ret |= (base & 0xffffff) << 16
to_ret |= (access & 0xff) << 40
to_ret |= ((limit >> 16) & 0xf) << 48
to_ret |= (flags & 0xff) << 52
to_ret |= ((base >> 24) & 0xff) << 56
return pack('<Q', to_ret)
def _create_selector(self, idx, flags):
to_ret = flags
to_ret |= idx << 3
return to_ret
def create_selector(self, idx, flags):
return self._create_selector(idx, flags)
def ql_x86_register_cs(self):
# While debugging the linux kernel segment, the cs segment was found on the third segment of gdt.
self.gdtm.register_gdt_segment(3, 0, 0xfffff000, QL_X86_A_PRESENT | QL_X86_A_CODE | QL_X86_A_CODE_READABLE | QL_X86_A_PRIV_3 | QL_X86_A_EXEC | QL_X86_A_DIR_CON_BIT, QL_X86_S_GDT | QL_X86_S_PRIV_3)
self.ql.reg.cs = self.gdtm.create_selector(3, QL_X86_S_GDT | QL_X86_S_PRIV_3)
def ql_x8664_register_cs(self):
# While debugging the linux kernel segment, the cs segment was found on the sixth segment of gdt.
self.gdtm.register_gdt_segment(6, 0, 0xfffffffffffff000, QL_X86_A_PRESENT | QL_X86_A_CODE | QL_X86_A_CODE_READABLE | QL_X86_A_PRIV_3 | QL_X86_A_EXEC | QL_X86_A_DIR_CON_BIT, QL_X86_S_GDT | QL_X86_S_PRIV_3)
self.ql.reg.cs = self.gdtm.create_selector(6, QL_X86_S_GDT | QL_X86_S_PRIV_3)
def ql_x86_register_ds_ss_es(self):
# TODO : The section permission here should be QL_X86_A_PRIV_3, but I do n’t know why it can only be set to QL_X86_A_PRIV_0.
# While debugging the Linux kernel segment, I found that the three segments DS, SS, and ES all point to the same location in the GDT table.
# This position is the fifth segment table of GDT.
self.gdtm.register_gdt_segment(5, 0, 0xfffff000, QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_0 | QL_X86_A_DIR_CON_BIT, QL_X86_S_GDT | QL_X86_S_PRIV_0)
self.ql.reg.ds = self.gdtm.create_selector(5, QL_X86_S_GDT | QL_X86_S_PRIV_0)
self.ql.reg.ss = self.gdtm.create_selector(5, QL_X86_S_GDT | QL_X86_S_PRIV_0)
self.ql.reg.es = self.gdtm.create_selector(5, QL_X86_S_GDT | QL_X86_S_PRIV_0)
def ql_x8664_register_ds_ss_es(self):
# TODO : The section permission here should be QL_X86_A_PRIV_3, but I do n’t know why it can only be set to QL_X86_A_PRIV_0.
# When I debug the Linux kernel, I find that only the SS is set to the fifth segment table, and the rest are not set.
self.gdtm.register_gdt_segment(5, 0, 0xfffff000, QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_0 | QL_X86_A_DIR_CON_BIT, QL_X86_S_GDT | QL_X86_S_PRIV_0)
# ql.reg.write(UC_X86_REG_DS, ql.os.gdtm.create_selector(5, QL_X86_S_GDT | QL_X86_S_PRIV_0))
self.ql.reg.ss = self.gdtm.create_selector(5, QL_X86_S_GDT | QL_X86_S_PRIV_0)
# ql.reg.write(UC_X86_REG_ES, ql.os.gdtm.create_selector(5, QL_X86_S_GDT | QL_X86_S_PRIV_0))
def ql_x86_register_gs(self):
self.gdtm.register_gdt_segment(15, GS_SEGMENT_ADDR, GS_SEGMENT_SIZE, QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_3 | QL_X86_A_DIR_CON_BIT, QL_X86_S_GDT | QL_X86_S_PRIV_3)
self.ql.reg.gs = self.gdtm.create_selector(15, QL_X86_S_GDT | QL_X86_S_PRIV_0)
def ql_x86_register_fs(self):
self.gdtm.register_gdt_segment(14, FS_SEGMENT_ADDR, FS_SEGMENT_SIZE, QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_3 | QL_X86_A_DIR_CON_BIT, QL_X86_S_GDT | QL_X86_S_PRIV_3)
self.ql.reg.fs = self.gdtm.create_selector(14, QL_X86_S_GDT | QL_X86_S_PRIV_3)
def ql_x8664_set_gs(ql: Qiling):
if not ql.mem.is_mapped(GS_SEGMENT_ADDR, GS_SEGMENT_SIZE):
ql.mem.map(GS_SEGMENT_ADDR, GS_SEGMENT_SIZE, info="[GS]")
ql.reg.msr(GSMSR, GS_SEGMENT_ADDR)
def ql_x8664_get_gs(ql: Qiling):
return ql.reg.msr(GSMSR)
def ql_x8664_set_fs(ql: Qiling, addr: int):
ql.reg.msr(FSMSR, addr)
def ql_x8664_get_fs(ql: Qiling):
return ql.reg.msr(FSMSR)
return self.__reg_bits(reg)
+3 -2
View File
@@ -39,8 +39,9 @@ QL_X86_GDT_LIMIT = 0x1000
QL_X86_GDT_ENTRY_SIZE = 0x8
# These msr registers are x86 specific
FSMSR = 0xC0000100
GSMSR = 0xC0000101
IA32_FS_BASE_MSR = 0xC0000100
IA32_GS_BASE_MSR = 0xC0000101
IA32_APIC_BASE_MSR = 0x1B
# WINDOWS SETUP VALUE
# Linux also needs these
+187
View File
@@ -0,0 +1,187 @@
from abc import abstractmethod
from qiling import Qiling
from qiling.arch.x86 import QlArchIntel
from qiling.arch.x86_const import *
from qiling.exception import QlGDTError
from qiling.os.memory import QlMemoryManager
class GDTArray:
entsize = QL_X86_GDT_ENTRY_SIZE
def __init__(self, mem: QlMemoryManager, base: int, num_entries: int):
self.mem = mem
self.base = base
self.num_entries = num_entries
def __in_bounds(self, index: int) -> bool:
return (0 < index < self.num_entries)
def __getitem__(self, index: int) -> bytes:
if not self.__in_bounds(index):
raise QlGDTError('invalid GDT entry index')
return bytes(self.mem.read(self.base + (index * self.entsize), self.entsize))
def __setitem__(self, index: int, data: bytes) -> None:
assert len(data) == self.entsize
if not self.__in_bounds(index):
raise QlGDTError('invalid GDT entry index')
self.mem.write(self.base + (index * self.entsize), data)
def get_next_free(self, start: int = None, end: int = None) -> int:
# The Linux kernel determines whether the segment is empty by judging whether the content in the current GDT segment is 0.
null_entry = b'\x00' * self.entsize
# first gdt entry is always null, start from 1
if start is None:
start = 1
if end is None:
end = self.num_entries
return next((i for i in range(start, end) if self[i] == null_entry), -1)
class GDTManager:
def __init__(self, ql: Qiling, base = QL_X86_GDT_ADDR, limit = QL_X86_GDT_LIMIT, num_entries = 16):
ql.log.debug(f'Mapping GDT at {base:#x} with limit {limit:#x}')
if not ql.mem.is_mapped(base, limit):
ql.mem.map(base, limit, info="[GDT]")
# setup GDT by writing to GDTR
ql.arch.regs.write(UC_X86_REG_GDTR, (0, base, limit, 0x0))
self.array = GDTArray(ql.mem, base, num_entries)
@staticmethod
def make_entry(base: int, limit: int, access: int, flags: int) -> bytes:
"""Encode specified arguments into a new GDT entry.
"""
maxbits = lambda val, nbits: val & ~((1 << nbits) - 1) == 0
assert maxbits(base, 32)
assert maxbits(limit, 20)
assert maxbits(access, 8)
assert maxbits(flags, 4)
# base: 8 + 24 bits
base_hi = (base >> 24) & 0xff
base_lo = base & ((1 << 24) - 1)
# limit: 4 + 16 bits
limit_hi = (limit >> 16) & 0xf
limit_lo = limit & ((1 << 16) - 1)
entry = base_hi << 56 | flags << 52 | limit_hi << 48 | access << 40 | base_lo << 16 | limit_lo
return entry.to_bytes(8, 'little', signed=False)
@staticmethod
def make_selector(idx: int, rpl: int) -> int:
assert rpl & ~0b11 == 0
return (idx << 3) | QL_X86_S_GDT | rpl
def register_gdt_segment(self, index: int, seg_base: int, seg_limit: int, access: int) -> int:
flags = QL_X86_F_PROT_32
# is this a huge segment?
if seg_limit > (1 << 16):
# on 4K granularity the lower 12 bits are implicitly all set
assert seg_limit & ((1 << 12) - 1) == 0xfff
seg_limit >>= 12
flags |= QL_X86_F_GRANULARITY
# create GDT entry, then write GDT entry into GDT table
self.array[index] = GDTManager.make_entry(seg_base, seg_limit, access, flags)
return GDTManager.make_selector(index, (access >> 5) & 0b11)
def get_entry(self, index: int) -> bytes:
return self.array[index]
def set_entry(self, index: int, data: bytes) -> None:
self.array[index] = data
def get_free_idx(self, start: int = None, end: int = None) -> int:
return self.array.get_next_free(start, end)
class SegmentManager:
def __init__(self, arch: QlArchIntel, gdtm: GDTManager):
self.arch = arch
self.gdtm = gdtm
@abstractmethod
def setup_cs_ds_ss_es(self, base: int, size: int) -> None:
pass
@abstractmethod
def setup_fs(self, base: int, size: int) -> None:
pass
@abstractmethod
def setup_gs(self, base: int, size: int) -> None:
pass
class SegmentManager86(SegmentManager):
def setup_cs_ds_ss_es(self, base: int, size: int) -> None:
# While debugging the linux kernel segment, the cs segment was found on the third segment of gdt.
access = QL_X86_A_PRESENT | QL_X86_A_CODE | QL_X86_A_CODE_READABLE | QL_X86_A_PRIV_3 | QL_X86_A_EXEC | QL_X86_A_DIR_CON_BIT
selector = self.gdtm.register_gdt_segment(3, base, size - 1, access)
self.arch.regs.cs = selector
# TODO : The section permission here should be QL_X86_A_PRIV_3, but I do n’t know why it can only be set to QL_X86_A_PRIV_0.
# While debugging the Linux kernel segment, I found that the three segments DS, SS, and ES all point to the same location in the GDT table.
# This position is the fifth segment table of GDT.
access = QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_0 | QL_X86_A_DIR_CON_BIT
selector = self.gdtm.register_gdt_segment(5, base, size - 1, access)
self.arch.regs.ds = selector
self.arch.regs.ss = selector
self.arch.regs.es = selector
def setup_fs(self, base: int, size: int) -> None:
access = QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_3 | QL_X86_A_DIR_CON_BIT
selector = self.gdtm.register_gdt_segment(14, base, size - 1, access)
self.arch.regs.fs = selector
def setup_gs(self, base: int, size: int) -> None:
access = QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_3 | QL_X86_A_DIR_CON_BIT
selector = self.gdtm.register_gdt_segment(15, base, size - 1, access)
self.arch.regs.gs = selector
class SegmentManager64(SegmentManager):
def setup_cs_ds_ss_es(self, base: int, size: int) -> None:
# While debugging the linux kernel segment, the cs segment was found on the sixth segment of gdt.
access = QL_X86_A_PRESENT | QL_X86_A_CODE | QL_X86_A_CODE_READABLE | QL_X86_A_PRIV_3 | QL_X86_A_EXEC | QL_X86_A_DIR_CON_BIT
selector = self.gdtm.register_gdt_segment(6, base, size - 1, access)
self.arch.regs.cs = selector
# TODO : The section permission here should be QL_X86_A_PRIV_3, but I do n’t know why it can only be set to QL_X86_A_PRIV_0.
# When I debug the Linux kernel, I find that only the SS is set to the fifth segment table, and the rest are not set.
access = QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_0 | QL_X86_A_DIR_CON_BIT
selector = self.gdtm.register_gdt_segment(5, base, size - 1, access)
# self.arch.regs.ds = selector
self.arch.regs.ss = selector
# self.arch.regs.es = selector
def setup_fs(self, base: int, size: int) -> None:
self.arch.msr.write(IA32_FS_BASE_MSR, base)
def setup_gs(self, base: int, size: int) -> None:
self.arch.msr.write(IA32_GS_BASE_MSR, base)
+12 -12
View File
@@ -4,20 +4,20 @@
from typing import Callable, Tuple
from qiling import Qiling
from qiling.arch.arch import QlArch
class QlCC:
"""Calling convention base class.
"""
def __init__(self, ql: Qiling) -> None:
def __init__(self, arch: QlArch) -> None:
"""Initialize a calling convention instance.
Args:
ql: qiling instance
arch: underlying architecture instance
"""
self.ql = ql
self.arch = arch
@staticmethod
def getNumSlots(argbits: int) -> int:
@@ -111,11 +111,11 @@ class QlCommonBaseCC(QlCC):
_shadow = 0
_retaddr_on_stack = True
def __init__(self, ql: Qiling, retreg: int):
super().__init__(ql)
def __init__(self, arch: QlArch, retreg: int):
super().__init__(arch)
# native address size in bytes
self._asize = self.ql.pointersize
self._asize = self.arch.pointersize
# return value register
self._retreg = retreg
@@ -151,24 +151,24 @@ class QlCommonBaseCC(QlCC):
return reg_access, reg
def getRawParam(self, index: int, argbits: int = None) -> int:
read, loc = self.__access_param(index, self.ql.stack_read, self.ql.reg.read)
read, loc = self.__access_param(index, self.arch.stack_read, self.arch.regs.read)
mask = (0 if argbits is None else (1 << argbits)) - 1
return read(loc) & mask
def setRawParam(self, index: int, value: int, argbits: int = None) -> None:
write, loc = self.__access_param(index, self.ql.stack_write, self.ql.reg.write)
write, loc = self.__access_param(index, self.arch.stack_write, self.arch.regs.write)
mask = (0 if argbits is None else (1 << argbits)) - 1
write(loc, value & mask)
def getReturnValue(self) -> int:
return self.ql.reg.read(self._retreg)
return self.arch.regs.read(self._retreg)
def setReturnValue(self, value: int) -> None:
self.ql.reg.write(self._retreg, value)
self.arch.regs.write(self._retreg, value)
def reserve(self, nslots: int) -> None:
assert nslots < len(self._argregs), 'too many slots'
@@ -176,4 +176,4 @@ class QlCommonBaseCC(QlCC):
# count how many slots should be reserved on the stack
si = self._argregs[:nslots].count(None)
self.ql.reg.arch_sp -= (self._shadow + si) * self._asize
self.arch.regs.arch_sp -= (self._shadow + si) * self._asize
+8 -8
View File
@@ -8,8 +8,8 @@ from unicorn.arm64_const import (
UC_ARM64_REG_X4, UC_ARM64_REG_X5, UC_ARM64_REG_X6, UC_ARM64_REG_X7
)
from qiling import Qiling
from . import QlCommonBaseCC
from qiling.arch.arch import QlArch
from qiling.cc import QlCommonBaseCC
class QlArmBaseCC(QlCommonBaseCC):
"""Calling convention base class for ARM-based systems.
@@ -22,20 +22,20 @@ class QlArmBaseCC(QlCommonBaseCC):
def setReturnAddress(self, addr: int) -> None:
# TODO: do we need to update LR?
self.ql.arch.stack_push(addr)
self.arch.stack_push(addr)
def unwind(self, nslots: int) -> int:
# TODO: cleanup?
return self.ql.arch.stack_pop()
return self.arch.stack_pop()
class aarch64(QlArmBaseCC):
_argregs = (UC_ARM64_REG_X0, UC_ARM64_REG_X1, UC_ARM64_REG_X2, UC_ARM64_REG_X3, UC_ARM64_REG_X4, UC_ARM64_REG_X5, UC_ARM64_REG_X6, UC_ARM64_REG_X7) + (None, ) * 8
def __init__(self, ql: Qiling) -> None:
super().__init__(ql, UC_ARM64_REG_X0)
def __init__(self, arch: QlArch) -> None:
super().__init__(arch, UC_ARM64_REG_X0)
class aarch32(QlArmBaseCC):
_argregs = (UC_ARM_REG_R0, UC_ARM_REG_R1, UC_ARM_REG_R2, UC_ARM_REG_R3) + (None, ) * 12
def __init__(self, ql: Qiling) -> None:
super().__init__(ql, UC_ARM_REG_R0)
def __init__(self, arch: QlArch) -> None:
super().__init__(arch, UC_ARM_REG_R0)
+8 -8
View File
@@ -8,29 +8,29 @@ from unicorn.x86_const import (
UC_X86_REG_R9, UC_X86_REG_R10
)
from qiling import Qiling
from . import QlCommonBaseCC
from qiling.arch.arch import QlArch
from qiling.cc import QlCommonBaseCC
class QlIntelBaseCC(QlCommonBaseCC):
"""Calling convention base class for Intel-based systems.
Supports arguments passing over registers and stack.
"""
def __init__(self, ql: Qiling):
def __init__(self, arch: QlArch):
retreg = {
16: UC_X86_REG_AX,
32: UC_X86_REG_EAX,
64: UC_X86_REG_RAX
}[ql.archbit]
}[arch.bits]
super().__init__(ql, retreg)
super().__init__(arch, retreg)
def setReturnAddress(self, addr: int) -> None:
self.ql.arch.stack_push(addr)
self.arch.stack_push(addr)
def unwind(self, nslots: int) -> int:
# no cleanup; just pop out the return address
return self.ql.arch.stack_pop()
return self.arch.stack_pop()
class QlIntel64(QlIntelBaseCC):
"""Calling convention base class for Intel-based 64-bit systems.
@@ -107,6 +107,6 @@ class stdcall(QlIntel32):
def unwind(self, nslots: int) -> int:
retaddr = super().unwind(nslots)
self.ql.reg.arch_sp += (nslots * self._asize)
self.arch.regs.arch_sp += (nslots * self._asize)
return retaddr
+5 -5
View File
@@ -4,16 +4,16 @@
from unicorn.mips_const import UC_MIPS_REG_V0, UC_MIPS_REG_A0, UC_MIPS_REG_A1, UC_MIPS_REG_A2, UC_MIPS_REG_A3
from qiling import Qiling
from . import QlCommonBaseCC
from qiling.arch.arch import QlArch
from qiling.cc import QlCommonBaseCC
class mipso32(QlCommonBaseCC):
_argregs = (UC_MIPS_REG_A0, UC_MIPS_REG_A1, UC_MIPS_REG_A2, UC_MIPS_REG_A3) + (None, ) * 12
_shadow = 4
_retaddr_on_stack = False
def __init__(self, ql: Qiling):
super().__init__(ql, UC_MIPS_REG_V0)
def __init__(self, arch: QlArch):
super().__init__(arch, UC_MIPS_REG_V0)
@staticmethod
def getNumSlots(argbits: int):
@@ -21,4 +21,4 @@ class mipso32(QlCommonBaseCC):
def unwind(self, nslots: int) -> int:
# TODO: stack frame unwiding?
return self.ql.reg.ra
return self.arch.regs.ra
+4 -4
View File
@@ -2,8 +2,8 @@
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
from qiling import Qiling
from . import QlCommonBaseCC
from qiling.arch.arch import QlArch
from qiling.cc import QlCommonBaseCC
from unicorn.riscv_const import (
UC_RISCV_REG_A0, UC_RISCV_REG_A1, UC_RISCV_REG_A2,
@@ -18,8 +18,8 @@ class riscv(QlCommonBaseCC):
_argregs = (UC_RISCV_REG_A0, UC_RISCV_REG_A1, UC_RISCV_REG_A2, UC_RISCV_REG_A3, UC_RISCV_REG_A4, UC_RISCV_REG_A5) + (None, ) * 10
def __init__(self, ql: Qiling):
super().__init__(ql, UC_RISCV_REG_A0)
def __init__(self, arch: QlArch):
super().__init__(arch, UC_RISCV_REG_A0)
@staticmethod
def getNumSlots(argbits: int):
+10 -14
View File
@@ -3,7 +3,7 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from enum import Enum, IntEnum
from enum import Enum, Flag, IntEnum
from typing import Any, Mapping, Type
class QL_ENDIAN(IntEnum):
@@ -14,7 +14,6 @@ class QL_ARCH(IntEnum):
X86 = 101
X8664 = 102
ARM = 103
ARM_THUMB = 104
ARM64 = 105
MIPS = 106
A8086 = 107
@@ -53,17 +52,13 @@ class QL_INTERCEPT(IntEnum):
ENTER = 2
EXIT = 3
QL_DEBUGGER_ALL = (QL_DEBUGGER.IDAPRO, QL_DEBUGGER.GDB, QL_DEBUGGER.QDB)
QL_ARCH_ENDIAN = (QL_ARCH.MIPS, QL_ARCH.ARM)
QL_ARCH_1BIT = (QL_ARCH.EVM,)
QL_ARCH_16BIT = (QL_ARCH.A8086,)
QL_ARCH_32BIT = (QL_ARCH.ARM, QL_ARCH.ARM_THUMB, QL_ARCH.MIPS, QL_ARCH.X86, QL_ARCH.CORTEX_M, QL_ARCH.RISCV)
QL_ARCH_64BIT = (QL_ARCH.ARM64, QL_ARCH.X8664, QL_ARCH.RISCV64)
QL_OS_NONPID = (QL_OS.DOS, QL_OS.UEFI)
QL_OS_POSIX = (QL_OS.LINUX, QL_OS.FREEBSD, QL_OS.MACOS, QL_OS.QNX)
class QL_STOP(Flag):
NONE = 0
STACK_POINTER = (1 << 0)
EXIT_TRAP = (1 << 1)
QL_OS_NONPID = (QL_OS.DOS, QL_OS.UEFI)
QL_OS_POSIX = (QL_OS.LINUX, QL_OS.FREEBSD, QL_OS.MACOS, QL_OS.QNX)
QL_OS_BAREMETAL = (QL_OS.MCU,)
QL_OS_INTERPRETER = (QL_OS.EVM,)
@@ -74,7 +69,7 @@ def __reverse_enum(e: Type[Enum]) -> Mapping[str, Any]:
'''Create a reverse mapping for an enum.
'''
return dict((v.name.lower(), v.value) for v in e.__members__.values())
return dict((v.name.lower(), v) for v in e.__members__.values())
debugger_map: Mapping[str, QL_DEBUGGER] = __reverse_enum(QL_DEBUGGER)
arch_map : Mapping[str, QL_ARCH] = __reverse_enum(QL_ARCH)
@@ -95,5 +90,6 @@ loader_map = {
}
arch_os_map = {
QL_ARCH.EVM: QL_OS.EVM,
QL_ARCH.EVM : QL_OS.EVM,
QL_ARCH.CORTEX_M : QL_OS.MCU
}
+188 -359
View File
@@ -4,98 +4,78 @@
#
from configparser import ConfigParser
import ntpath, os, pickle, platform
import os, pickle
# See https://stackoverflow.com/questions/39740632/python-type-hinting-without-cyclic-imports
from typing import Callable, Dict, List, Union
from typing import AnyStr, List, Mapping, MutableMapping, Sequence, Union
from typing import TYPE_CHECKING
from unicorn.unicorn import Uc
if TYPE_CHECKING:
from .arch.register import QlRegisterManager
from .arch.arch import QlArch
from .os.os import QlOs
from .os.memory import QlMemoryManager
from .hw.hw import QlHwManager
from .loader.loader import QlLoader
from .const import QL_ARCH_ENDIAN, QL_ENDIAN, QL_VERBOSE, QL_OS_INTERPRETER, QL_OS_BAREMETAL
from .const import QL_ARCH, QL_ENDIAN, QL_OS, QL_STOP, QL_VERBOSE, QL_OS_INTERPRETER, QL_OS_BAREMETAL
from .exception import QlErrorFileNotFound, QlErrorArch, QlErrorOsType
from .host import QlHost
from .utils import *
from .core_struct import QlCoreStructs
from .core_hooks import QlCoreHooks
from .__version__ import __version__
# Mixin Pattern
class Qiling(QlCoreHooks, QlCoreStructs):
class Qiling(QlCoreHooks, QlCoreStructs):
def __init__(
self,
argv=None,
rootfs=None,
env=None,
code=None,
ostype=None,
archtype=None,
bigendian=False,
verbose=QL_VERBOSE.DEFAULT,
profile=None,
argv: Sequence[str] = None,
rootfs: str = None,
env: MutableMapping[AnyStr, AnyStr] = {},
code: bytes = None,
ostype: Union[str, QL_OS] = None,
archtype: Union[str, QL_ARCH] = None,
verbose: QL_VERBOSE = QL_VERBOSE.DEFAULT,
profile: str = None,
console=True,
log_file=None,
log_override=None,
log_plain=False,
libcache = False,
multithread = False,
filter = None,
stop_on_stackpointer = False,
stop_on_exit_trap = False,
stdin=None,
stdout=None,
stderr=None,
stop: QL_STOP = QL_STOP.NONE,
*,
endian: QL_ENDIAN = None,
thumb: bool = False,
libcache: bool = False
):
""" Create a Qiling instance.
For each argument or property, please refer to its docstring. e.g. Qiling.multithread.__doc__
The only exception is "bigendian" parameter, see Qiling.archendian.__doc__ for details.
"""
##################################
# Definition during ql=Qiling() #
##################################
self._argv = argv
self._rootfs = rootfs
self._env = env if env else {}
self._env = env
self._code = code
self._ostype = ostype
self._archtype = archtype
self._archendian = QL_ENDIAN.EL
self._archbit = None
self._pointersize = None
self._profile = profile
self._console = console
self._log_file = log_file
self._multithread = multithread
self._log_file_fd = None
self._log_filter = None
self._log_override = log_override
self._log_plain = log_plain
self._filter = filter
self._platform_os = ostype_convert(platform.system().lower())
self._platform_arch = arch_convert(platform.machine().lower())
self._internal_exception = None
self._uc = None
self._stop_options = QlStopOptions(stackpointer=stop_on_stackpointer, exit_trap=stop_on_exit_trap)
self._stop_options = stop
##################################
# Definition after ql=Qiling() #
##################################
self._verbose = verbose
self._libcache = libcache
self._patch_bin = []
self._patch_lib = []
self._debug_stop = False
self._debugger = None
self._debugger = False
###############################
# Properties configured later #
@@ -112,112 +92,106 @@ class Qiling(QlCoreHooks, QlCoreStructs):
##############
# argv setup #
##############
if self._argv is None:
self._argv = ["qilingcode"]
if argv is None:
argv = ['qilingcode']
elif not os.path.exists(str(self._argv[0])):
raise QlErrorFileNotFound("Target binary not found: %s" % (self._argv[0]))
elif not os.path.exists(argv[0]):
raise QlErrorFileNotFound(f'Target binary not found: "{argv[0]}"')
self._argv = argv
self._path = self.argv[0]
self._targetname = os.path.basename(self.path)
################
# rootfs setup #
################
if self._rootfs is None:
self._rootfs = "."
elif not os.path.exists(self._rootfs):
raise QlErrorFileNotFound("Target rootfs not found: %s" % (self._rootfs))
self._path = self._argv[0]
self._targetname = ntpath.basename(self.path)
if rootfs is None:
rootfs = '.'
elif not os.path.exists(rootfs):
raise QlErrorFileNotFound(f'Target rootfs not found: "{rootfs}"')
self._rootfs = rootfs
#################
# arch os setup #
#################
if type(self._archtype) is str:
self._archtype = arch_convert(self._archtype)
if type(self._ostype) is str:
self._ostype = ostype_convert(self._ostype)
self._host = QlHost()
if self._archtype is None:
guessed_archtype, guessed_ostype, guessed_archendian = ql_guess_emu_env(self._path)
self._archtype = guessed_archtype
self._archendian = guessed_archendian
if self._ostype is None:
self._ostype = guessed_ostype
if type(archtype) is str:
archtype = arch_convert(archtype)
elif self._ostype == None:
self._ostype = arch_os_convert(self._archtype)
if not ql_is_valid_ostype(self._ostype):
raise QlErrorOsType("Invalid OS: %s" % (self._ostype))
if type(ostype) is str:
ostype = ostype_convert(ostype)
if not ql_is_valid_arch(self._archtype):
raise QlErrorArch("Invalid ARCH: %s" % (self._archtype))
# if provided arch was invalid or not provided, guess arch and os
if archtype is None:
guessed_archtype, guessed_ostype, guessed_archendian = ql_guess_emu_env(self.path)
archtype = guessed_archtype
########################
# Archbit & Endianness #
########################
self._archbit = ql_get_arch_bits(self._archtype)
self._pointersize = (self.archbit // 8)
if bigendian == True and self._archtype in QL_ARCH_ENDIAN:
self._archendian = QL_ENDIAN.EB
if ostype is None:
ostype = guessed_ostype
# Once we finish setting up archendian and arcbit, we can init QlCoreStructs.
QlCoreStructs.__init__(self, self._archendian, self._archbit)
if endian is None:
endian = guessed_archendian
#######################################
# Loader and General Purpose OS check #
#######################################
self._loader = loader_setup(self._ostype, self)
# if arch was set but os was not, try to guess it by arch
elif ostype is None:
ostype = arch_os_convert(archtype)
#####################
# Profile & Logging #
#####################
self._profile, debugmsg = profile_setup(self)
# arch should have been determined by now; fail if not
if archtype is None or not ql_is_valid_arch(archtype):
raise QlErrorArch(f'Uknown or unsupported architecture: "{archtype}"')
# Log's configuration
self._log_file_fd, self._log_filter = ql_setup_logger(self,
self._log_file,
self._console,
self._filter,
self._log_override,
self._log_plain)
# os should have been determined by now; fail if not
if ostype is None or not ql_is_valid_ostype(ostype):
raise QlErrorOsType(f'Unknown or unsupported operating system: "{ostype}"')
self.log.setLevel(ql_resolve_logger_level(self._verbose))
# if endianess is still undetermined, set it to little-endian.
# this setting is ignored for architectures with predfined endianess
if endian is None:
endian = QL_ENDIAN.EL
# Now that the logger is configured, we can log profile debug msg:
self.log.debug(debugmsg)
# make sure args were canonicalized successfully
assert type(archtype) is QL_ARCH
assert type(ostype) is QL_OS
assert type(endian) is QL_ENDIAN
self._arch = arch_setup(archtype, endian, thumb, self)
self._ostype = ostype
self.uc = self.arch.uc
# Once we finish setting up arch, we can init QlCoreStructs and QlCoreHooks
QlCoreStructs.__init__(self, self.arch.endian, self.arch.bits)
if not self.interpreter:
QlCoreHooks.__init__(self, self.uc)
##########
# Logger #
##########
self._log_file_fd, self._log_filter = ql_setup_logger(self, log_file, console, self._filter, log_override, log_plain)
self.verbose = verbose
###########
# Profile #
###########
self._profile = profile_setup(self, self.ostype, profile)
##########
# Loader #
##########
self._loader = loader_setup(self, self.ostype, libcache)
##############
# Components #
##############
if not self.interpreter:
self._mem = component_setup("os", "memory", self)
self._reg = component_setup("arch", "register", self)
self._arch = arch_setup(self.archtype, self)
# Once we finish setting up arch layer, we can init QlCoreHooks.
if not self.interpreter:
self.uc = self.arch.init_uc
QlCoreHooks.__init__(self, self.uc)
self.arch.utils.setup_output()
self._os = os_setup(self.archtype, self.ostype, self)
if stdin is not None:
self._os.stdin = stdin
if stdout is not None:
self._os.stdout = stdout
if stderr is not None:
self._os.stderr = stderr
self._os = os_setup(self.ostype, self)
# Run the loader
self.loader.run()
@@ -235,14 +209,6 @@ class Qiling(QlCoreHooks, QlCoreStructs):
"""
return self._mem
@property
def reg(self) -> "QlRegisterManager":
""" Qiling register manager.
Example: ql.reg.eax = 1
"""
return self._reg
@property
def hw(self) -> "QlHwManager":
""" Qiling hardware manager.
@@ -293,24 +259,6 @@ class Qiling(QlCoreHooks, QlCoreStructs):
# If an option doesn't have a setter, it means that it can be only set during Qiling.__init__
@property
def console(self) -> bool:
""" Specify whether enabling console output.
Type: bool
Example: Qiling(console=True)
"""
return self._console
@property
def log_file(self) -> str:
""" Log to a file.
Type: str
Example: Qiling(log_file="./ql.log")
"""
return self._log_file
@property
def multithread(self) -> bool:
""" Specify whether multithread has been enabled.
@@ -335,10 +283,9 @@ class Qiling(QlCoreHooks, QlCoreStructs):
return self._profile
@property
def argv(self) -> List[str]:
def argv(self) -> Sequence[str]:
""" The program argv.
Type: List[str]
Example: Qiling(argv=['/bin/ls', '-a'])
"""
return self._argv
@@ -353,10 +300,9 @@ class Qiling(QlCoreHooks, QlCoreStructs):
return self._rootfs
@property
def env(self) -> Dict[str, str]:
def env(self) -> MutableMapping[AnyStr, AnyStr]:
""" The program environment variables.
Type: Dict[str, str]
Example: Qiling(env={"LC_ALL" : "en_US.UTF-8"})
"""
return self._env
@@ -376,58 +322,10 @@ class Qiling(QlCoreHooks, QlCoreStructs):
- "windows" : Windows
- "uefi" : UEFI
- "dos" : DOS
Example: Qiling(code=b"\x90", ostype="macos", archtype="x8664", bigendian=False)
Example: Qiling(code=b"\x90", ostype="macos", archtype="x8664")
"""
return self._ostype
@property
def archtype(self) -> QL_ARCH:
""" The emulated architecture type.
Note: Please pass None or one of the strings below to Qiling.__init__.
If you use shellcode, you must specify ostype and archtype manually.
Type: int
Values:
- "x86" : x86_32
- "x8664" : x86_64
- "mips" : MIPS
- "arm" : ARM
- "arm_thumb" : ARM with thumb mode.
- "arm64" : ARM64
- "a8086" : 8086
Example: Qiling(code=b"\x90", ostype="macos", archtype="x8664", bigendian=False)
"""
return self._archtype
@property
def archendian(self) -> QL_ENDIAN:
""" The architecure endian.
Note: Please pass "bigendian=True" or "bingendian=False" to set this property.
This option only takes effect for shellcode.
Type: int
Example: Qiling(code=b"\x90", ostype="macos", archtype="x8664", bigendian=False)
"""
return self._archendian
@property
def archbit(self) -> int:
""" The bits of the current architecutre.
Type: int
"""
return self._archbit
@property
def pointersize(self) -> int:
""" The pointer size of current architecture.
Type: int
"""
return self._pointersize
@property
def code(self) -> bytes:
""" The shellcode to execute.
@@ -435,7 +333,7 @@ class Qiling(QlCoreHooks, QlCoreStructs):
Note: It can't be used with "argv" parameter.
Type: bytes
Example: Qiling(code=b"\x90", ostype="macos", archtype="x8664", bigendian=False)
Example: Qiling(code=b"\x90", ostype="macos", archtype="x8664")
"""
return self._code
@@ -475,39 +373,14 @@ class Qiling(QlCoreHooks, QlCoreStructs):
return self.ostype in QL_OS_BAREMETAL
@property
def platform_os(self):
""" Specify current platform os where Qiling runs on.
Type: int
Values: All possible values from platform.system()
def host(self) -> QlHost:
"""Provide an interface to the hosting platform where Qiling runs on.
"""
return self._platform_os
@platform_os.setter
def platform_os(self, value):
if type(value) is str:
self._platform_os = ostype_convert(value.lower())
else:
self._platform_os = value
return self._host
@property
def platform_arch(self):
""" Specify current platform arch where Qiling runs on.
Type: int
Values: All possible values from platform.system()
"""
return self._platform_arch
@platform_arch.setter
def platform_arch(self, value):
if type(value) is str:
self._platform_arch = arch_convert(value.lower())
else:
self._platform_arch = value
@property
def internal_exception(self) -> Exception:
def internal_exception(self) -> Optional[Exception]:
""" Internal exception catched during Unicorn callback. Not intended for regular users.
Type: Exception
@@ -515,44 +388,28 @@ class Qiling(QlCoreHooks, QlCoreStructs):
return self._internal_exception
@property
def libcache(self) -> bool:
""" Whether cache dll files. Only take effect in Windows emulation.
def verbose(self) -> QL_VERBOSE:
"""Set verbosity level.
Type: bool
Example: - ql = Qiling(libcache=False)
- ql.libcache = True
"""
return self._libcache
@libcache.setter
def libcache(self, lc):
self._libcache = lc
@property
def verbose(self):
""" Set the verbose level.
Type: int
Values:
- -1 : logging.NOTSET, turn off all the output.
- 0 : logging.WARNING, almost no additional logs except the program output.
- >=1: logging.INFO, the default logging level.
- >=4: logging.DEBUG.
- >=10: Disasm each executed instruction.
- >=20: The most verbose output, dump registers and disasm the function blocks.
Example: - ql = Qiling(verbose=5)
- ql.verbose = 0
Values:
`QL_VERBOSE.DISABLED`: turn off logging
`QL_VERBOSE.OFF` : mask off anything below warnings, errors and critical severity
`QL_VERBOSE.DEFAULT` : info logging level: default verbosity
`QL_VERBOSE.DEBUG` : debug logging level: higher verbosity
`QL_VERBOSE.DISASM` : debug verbosity along with disassembly trace (slow!)
`QL_VERBOSE.DUMP` : disassembly trace along with cpu context dump
"""
return self._verbose
@verbose.setter
def verbose(self, v):
def verbose(self, v: QL_VERBOSE):
self._verbose = v
self.log.setLevel(ql_resolve_logger_level(self._verbose))
self.arch.utils.setup_output()
self.log.setLevel(ql_resolve_logger_level(v))
self.arch.utils.setup_output(v)
@property
def patch_bin(self) -> list:
def patch_bin(self) -> List[Tuple[int, bytes]]:
""" Return the patches for binary.
Type: list
@@ -560,7 +417,7 @@ class Qiling(QlCoreHooks, QlCoreStructs):
return self._patch_bin
@property
def patch_lib(self) -> list:
def patch_lib(self) -> List[Tuple[int, bytes, str]]:
""" Return the patches for library.
Type: list
@@ -583,23 +440,23 @@ class Qiling(QlCoreHooks, QlCoreStructs):
self._debug_stop = ds
@property
def debugger(self) -> Union[str, bool]:
def debugger(self) -> bool:
return bool(self._debugger)
@debugger.setter
def debugger(self, dbger: Union[str, bool]):
""" Enable debugger.
Type: debugger instance
Values:
- "gdb": enable gdb.
- True : an alias to "gdb".
- "gdb:0.0.0.0:1234" : gdb which listens on 0.0.0.0:1234
- "qdb": enable qdb.
- "qdb:rr": enable qdb with reverse debugging support.
Example: ql.debugger = True
ql.debugger = "qdb"
"""
return self._debugger
@debugger.setter
def debugger(self, dbger):
self._debugger = dbger
@property
@@ -633,14 +490,12 @@ class Qiling(QlCoreHooks, QlCoreStructs):
self._uc = u
@property
def stop_options(self) -> "QlStopOptions":
""" The stop options configured:
- stackpointer: Stop execution on a negative stackpointer
- exit_trap: Stop execution when the ip enters a guarded region
- any: Is any of the options enabled?
def stop_options(self) -> QL_STOP:
""" The stop options configured (multiple options apply):
- `QL_STOP.STACK_POINTER` : Stop execution on a negative stackpointer
- `QL_STOP.EXIT_TRAP` : Stop execution when the pc value enters a guarded region
Returns:
QlStopOptions: What stop options are configured
Returns: configured options
"""
return self._stop_options
@@ -658,42 +513,43 @@ class Qiling(QlCoreHooks, QlCoreStructs):
raise RuntimeError("Fail to patch %s at address 0x%x" % (filename, addr))
def _init_stop_guard(self):
if not self.stop_options.any:
if not self.stop_options:
return
# Allocate a guard page, we need this in both cases
# On a negative stack pointer, we still need a return address (otherwise we end up at 0)
# Make sure it is not close to the heap (PE), otherwise the heap cannot grow
self._exit_trap_addr = self.mem.find_free_space(0x1000, minaddr=0x9000000, align=0x10)
self.mem.map(self._exit_trap_addr, 0x1000, info='[Stop guard]')
self.mem.map(self._exit_trap_addr, 0x1000, info='[Stop guard page]')
# Stop on a negative stack pointer
if self.stop_options.stackpointer:
def _check_sp(ql, address, size):
if QL_STOP.STACK_POINTER in self.stop_options:
def _check_sp(ql: Qiling, address: int, size: int):
if not ql.loader.skip_exit_check:
sp = ql._initial_sp - ql.reg.arch_sp
if sp < 0:
if ql._initial_sp < ql.arch.regs.arch_sp:
self.log.info('Process returned from entrypoint (stackpointer)!')
ql.emu_stop()
self.hook_code(_check_sp)
# Stop when running to exit trap address
if self.stop_options.exit_trap:
def _exit_trap(ql):
if QL_STOP.EXIT_TRAP in self.stop_options:
def _exit_trap(ql: Qiling):
self.log.info('Process returned from entrypoint (exit_trap)!')
ql.emu_stop()
self.hook_address(_exit_trap, self._exit_trap_addr)
def write_exit_trap(self):
self._initial_sp = self.reg.arch_sp
if self.stop_options.any:
self._initial_sp = self.arch.regs.arch_sp
if self.stop_options:
if not self.loader.skip_exit_check:
self.log.debug(f'Setting up exit trap at 0x{hex(self._exit_trap_addr)}')
self.log.debug(f'Setting up exit trap at {self._exit_trap_addr:#x}')
self.stack_write(0, self._exit_trap_addr)
elif self.stop_options.exit_trap:
self.log.debug(f'Loader {self.loader} requested to skip exit_trap!')
elif QL_STOP.EXIT_TRAP in self.stop_options:
self.log.debug(f'Loader requested to skip exit_trap!')
###############
@@ -709,67 +565,75 @@ class Qiling(QlCoreHooks, QlCoreStructs):
self.timeout = timeout
self.count = count
# init debugger
if self._debugger != False and self._debugger != None and not self.interpreter:
self._debugger = debugger_setup(self._debugger, self)
if self.interpreter:
return self.arch.run(code)
else:
self.write_exit_trap()
# patch binary
self.__enable_bin_patch()
# emulate the binary
self.os.run()
# init debugger (if set)
debugger = debugger_setup(self._debugger, self)
# patch binary
self.__enable_bin_patch()
self.write_exit_trap()
# emulate the binary
self.os.run()
# run debugger
if self._debugger != False and self._debugger != None:
self._debugger.run()
if debugger and self.debugger:
debugger.run()
# patch code to memory address
def patch(self, addr, code, file_name=b''):
if file_name == b'':
self.patch_bin.append((addr, code))
def patch(self, offset: int, data: bytes, target: str = None) -> None:
"""Volatilely patch binary and libraries with arbitrary content.
Patching may be done prior to emulation start.
Args:
offset: offset in target to patch
data: patch data
target: target library name to patch (or `None` for the main executable binary)
"""
if target is None:
self.patch_bin.append((offset, data))
else:
self.patch_lib.append((addr, code, file_name.decode()))
self.patch_lib.append((offset, data, target))
# save all qiling instance states
def save(self, reg=True, mem=True, fd=False, cpu_context=False, os_context=False, loader=False, snapshot=None):
def save(self, reg=True, mem=True, fd=False, cpu_context=False, os=False, loader=False, *, snapshot: str = None):
saved_states = {}
if reg == True:
saved_states.update({"reg": self.reg.save()})
if reg:
saved_states["reg"] = self.arch.regs.save()
if mem == True:
saved_states.update({"mem": self.mem.save()})
if mem:
saved_states["mem"] = self.mem.save()
if fd == True:
saved_states.update({"fd": self.os.fd.save()})
if fd:
saved_states["fd"] = self.os.fd.save()
if cpu_context == True:
saved_states.update({"cpu_context": self.arch.context_save()})
if cpu_context:
saved_states["cpu_context"] = self.arch.save()
if os_context == True:
saved_states.update({"os_context": self.os.save()})
if os:
saved_states["os"] = self.os.save()
if loader == True:
saved_states.update({"loader": self.loader.save()})
if loader:
saved_states["loader"] = self.loader.save()
if snapshot != None:
if snapshot is not None:
with open(snapshot, "wb") as save_state:
pickle.dump(saved_states, save_state)
else:
return saved_states
return saved_states
# restore states qiling instance from saved_states
def restore(self, saved_states=None, snapshot=None):
def restore(self, saved_states: Mapping[str, Any] = {}, *, snapshot: str = None):
# snapshot will be ignored if saved_states is set
if saved_states == None and snapshot != None:
if (not saved_states) and (snapshot is not None):
with open(snapshot, "rb") as load_state:
saved_states = pickle.load(load_state)
@@ -777,10 +641,10 @@ class Qiling(QlCoreHooks, QlCoreStructs):
self.mem.restore(saved_states["mem"])
if "cpu_context" in saved_states:
self.arch.context_restore(saved_states["cpu_context"])
self.arch.restore(saved_states["cpu_context"])
if "reg" in saved_states:
self.reg.restore(saved_states["reg"])
self.arch.regs.restore(saved_states["reg"])
if "fd" in saved_states:
self.os.fd.restore(saved_states["fd"])
@@ -792,24 +656,6 @@ class Qiling(QlCoreHooks, QlCoreStructs):
self.loader.restore(saved_states["loader"])
# Either hook or replace syscall/api with custom api/syscall
# - if intercept is None, replace syscall with custom function
# - if intercept is ENTER/EXIT, hook syscall at enter/exit with custom function
# If replace function name is needed, first syscall must be available
# - ql.set_syscall(0x04, my_syscall_write)
# - ql.set_syscall("write", my_syscall_write)
# TODO: Add correspoinding API in ql.os!
def set_syscall(self, target_syscall, intercept_function, intercept = None):
self.os.set_syscall(target_syscall, intercept_function, intercept)
# Either replace or hook API
# - if intercept is None, replace API with custom function
# - if intercept is ENTER/EXIT, hook API at enter/exit with custom function
def set_api(self, api_name, intercept_function, intercept = None):
self.os.set_api(api_name, intercept_function, intercept)
# Map "ql_path" to any objects which implements QlFsMappedObject.
def add_fs_mapper(self, ql_path, real_dest):
self.os.fs_mapper.add_fs_mapping(ql_path, real_dest)
@@ -836,23 +682,6 @@ class Qiling(QlCoreHooks, QlCoreStructs):
def stack_write(self, offset, data):
return self.arch.stack_write(offset, data)
# Assembler/Diassembler API
@property
def assembler(self):
return self.create_assembler()
@property
def disassembler(self):
return self.create_disassembler()
def create_disassembler(self):
return self.arch.create_disassembler()
def create_assembler(self):
return self.arch.create_assembler()
# stop emulation
def emu_stop(self):
+2 -2
View File
@@ -67,7 +67,7 @@ class QlCoreHooks:
hooks_list = self._insn_hook[hook_type]
for hook in hooks_list:
if hook.bound_check(ql.reg.arch_pc):
if hook.bound_check(ql.arch.regs.arch_pc):
ret = hook.call(ql, *args[:-1])
if type(ret) is tuple:
@@ -87,7 +87,7 @@ class QlCoreHooks:
hooks_list = self._hook[hook_type]
for hook in hooks_list:
if hook.bound_check(ql.reg.arch_pc):
if hook.bound_check(ql.arch.regs.arch_pc):
ret = hook.call(ql, addr, size)
if type(ret) is int and ret & QL_HOOK_BLOCK:
+3 -2
View File
@@ -24,12 +24,13 @@ class QlDisassember():
def disasm_elf(self, seg_name='.text'):
def disasm(ql, address, size):
md = ql.create_disassembler()
md = ql.arch.disassembler
md.detail = True
return md.disasm(ql.mem.read(address, size), address)
disasm_result = []
if self.ql._archtype == QL_ARCH.X86:
if self.ql.arch.type == QL_ARCH.X86:
BASE = int(self.ql.profile.get("OS32", "load_address"), 16)
seg_start = 0x0
seg_end = 0x0
+69 -69
View File
@@ -88,7 +88,7 @@ class QlGdb(QlDebugger, object):
def addr_to_str(self, addr: int, short: bool = False, endian: Literal['little', 'big'] = 'big') -> str:
# a hacky way to divide archbits by 2 if short, and leave it unchanged if not
nbits = self.ql.archbit // (int(short) + 1)
nbits = self.ql.arch.bits // (int(short) + 1)
if nbits == 64:
s = f'{int.from_bytes(self.ql.pack64(addr), byteorder=endian):016x}'
@@ -179,22 +179,22 @@ class QlGdb(QlDebugger, object):
}
return adapter.get(arch)
idhex, spid, pcid = gdbqmark_converter(self.ql.archtype)
sp = self.addr_to_str(self.ql.reg.arch_sp)
pc = self.addr_to_str(self.ql.reg.arch_pc)
nullfill = "0" * int(self.ql.archbit / 4)
idhex, spid, pcid = gdbqmark_converter(self.ql.arch.type)
sp = self.addr_to_str(self.ql.arch.regs.arch_sp)
pc = self.addr_to_str(self.ql.arch.regs.arch_pc)
nullfill = "0" * int(self.ql.arch.bits / 4)
if self.ql.archtype== QL_ARCH.MIPS:
if self.ql.archendian == QL_ENDIAN.EB:
sp = self.addr_to_str(self.ql.reg.arch_sp, endian ="little")
pc = self.addr_to_str(self.ql.reg.arch_pc, endian ="little")
if self.ql.arch.type == QL_ARCH.MIPS:
if self.ql.arch.endian == QL_ENDIAN.EB:
sp = self.addr_to_str(self.ql.arch.regs.arch_sp, endian ="little")
pc = self.addr_to_str(self.ql.arch.regs.arch_pc, endian ="little")
self.send('T%.2x%.2x:%s;%.2x:%s;' %(GDB_SIGNAL_TRAP, idhex, sp, pcid, pc))
else:
self.send('T%.2x%.2x:%s;%.2x:%s;%.2x:%s;' %(GDB_SIGNAL_TRAP, idhex, nullfill, spid, sp, pcid, pc))
def handle_c(subcmd):
self.gdb.resume_emu(self.ql.reg.arch_pc)
self.gdb.resume_emu(self.ql.arch.regs.arch_pc)
if self.gdb.bp_list == [self.entry_point]:
self.send("W00")
@@ -208,48 +208,48 @@ class QlGdb(QlDebugger, object):
def handle_g(subcmd):
s = ''
if self.ql.archtype== QL_ARCH.A8086:
if self.ql.arch.type == QL_ARCH.A8086:
for reg in self.tables[QL_ARCH.A8086][:16]:
r = self.ql.reg.read(reg)
r = self.ql.arch.regs.read(reg)
tmp = self.addr_to_str(r)
s += tmp
elif self.ql.archtype== QL_ARCH.X86:
elif self.ql.arch.type == QL_ARCH.X86:
for reg in self.tables[QL_ARCH.X86][:16]:
r = self.ql.reg.read(reg)
r = self.ql.arch.regs.read(reg)
tmp = self.addr_to_str(r)
s += tmp
elif self.ql.archtype== QL_ARCH.X8664:
elif self.ql.arch.type == QL_ARCH.X8664:
for reg in self.tables[QL_ARCH.X8664][:24]:
r = self.ql.reg.read(reg)
if self.ql.reg.bit(reg) == 64:
r = self.ql.arch.regs.read(reg)
if self.ql.arch.reg_bits(reg) == 64:
tmp = self.addr_to_str(r)
elif self.ql.reg.bit(reg) == 32:
elif self.ql.arch.reg_bits(reg) == 32:
tmp = self.addr_to_str(r, short = True)
s += tmp
elif self.ql.archtype == QL_ARCH.ARM:
elif self.ql.arch.type == QL_ARCH.ARM:
# r0-r12,sp,lr,pc,cpsr ,see https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/arch/arm.h;h=fa589fd0582c0add627a068e6f4947a909c45e86;hb=HEAD#l127
for reg in self.tables[QL_ARCH.ARM][:16] + [self.tables[QL_ARCH.ARM][25]]:
# if reg is pc, make sure to take thumb mode into account
r = self.ql.arch.get_pc() if reg == "pc" else self.ql.reg.read(reg)
r = self.ql.arch.effective_pc if reg == "pc" else self.ql.arch.regs.read(reg)
tmp = self.addr_to_str(r)
s += tmp
elif self.ql.archtype == QL_ARCH.ARM64:
elif self.ql.arch.type == QL_ARCH.ARM64:
for reg in self.tables[QL_ARCH.ARM64][:33]:
r = self.ql.reg.read(reg)
r = self.ql.arch.regs.read(reg)
tmp = self.addr_to_str(r)
s += tmp
elif self.ql.archtype == QL_ARCH.MIPS:
elif self.ql.arch.type == QL_ARCH.MIPS:
for reg in self.tables[QL_ARCH.MIPS][:38]:
r = self.ql.reg.read(reg)
if self.ql.archendian == QL_ENDIAN.EL:
r = self.ql.arch.regs.read(reg)
if self.ql.arch.endian == QL_ENDIAN.EL:
tmp = self.addr_to_str(r, endian ="little")
else:
tmp = self.addr_to_str(r)
@@ -261,52 +261,52 @@ class QlGdb(QlDebugger, object):
def handle_G(subcmd):
count = 0
if self.ql.archtype == QL_ARCH.A8086:
if self.ql.arch.type == QL_ARCH.A8086:
for i in range(0, len(subcmd), 8):
reg_data = subcmd[i:i+7]
reg_data = int(reg_data, 16)
self.ql.reg.write(self.tables[QL_ARCH.A8086][count], reg_data)
self.ql.arch.regs.write(self.tables[QL_ARCH.A8086][count], reg_data)
count += 1
elif self.ql.archtype == QL_ARCH.X86:
elif self.ql.arch.type == QL_ARCH.X86:
for i in range(0, len(subcmd), 8):
reg_data = subcmd[i:i+7]
reg_data = int(reg_data, 16)
self.ql.reg.write(self.tables[QL_ARCH.X86][count], reg_data)
self.ql.arch.regs.write(self.tables[QL_ARCH.X86][count], reg_data)
count += 1
elif self.ql.archtype == QL_ARCH.X8664:
elif self.ql.arch.type == QL_ARCH.X8664:
for i in range(0, 17*16, 16):
reg_data = subcmd[i:i+15]
reg_data = int(reg_data, 16)
self.ql.reg.write(self.tables[QL_ARCH.X8664][count], reg_data)
self.ql.arch.regs.write(self.tables[QL_ARCH.X8664][count], reg_data)
count += 1
for j in range(17*16, 17*16+15*8, 8):
reg_data = subcmd[j:j+7]
reg_data = int(reg_data, 16)
self.ql.reg.write(self.tables[QL_ARCH.X8664][count], reg_data)
self.ql.arch.regs.write(self.tables[QL_ARCH.X8664][count], reg_data)
count += 1
elif self.ql.archtype == QL_ARCH.ARM:
elif self.ql.arch.type == QL_ARCH.ARM:
for i in range(0, len(subcmd), 8):
reg_data = subcmd[i:i + 7]
reg_data = int(reg_data, 16)
self.ql.reg.write(self.tables[QL_ARCH.ARM][count], reg_data)
self.ql.arch.regs.write(self.tables[QL_ARCH.ARM][count], reg_data)
count += 1
elif self.ql.archtype == QL_ARCH.ARM64:
elif self.ql.arch.type == QL_ARCH.ARM64:
for i in range(0, len(subcmd), 16):
reg_data = subcmd[i:i+15]
reg_data = int(reg_data, 16)
self.ql.reg.write(self.tables[QL_ARCH.ARM64][count], reg_data)
self.ql.arch.regs.write(self.tables[QL_ARCH.ARM64][count], reg_data)
count += 1
elif self.ql.archtype == QL_ARCH.MIPS:
elif self.ql.arch.type == QL_ARCH.MIPS:
for i in range(0, len(subcmd), 8):
reg_data = subcmd[i:i+7]
reg_data = int(reg_data, 16)
self.ql.reg.write(self.tables[QL_ARCH.MIPS][count], reg_data)
self.ql.arch.regs.write(self.tables[QL_ARCH.MIPS][count], reg_data)
count += 1
self.send('OK')
@@ -353,23 +353,23 @@ class QlGdb(QlDebugger, object):
reg_index = int(subcmd, 16)
reg_value = None
try:
if self.ql.archtype== QL_ARCH.A8086:
if self.ql.arch.type == QL_ARCH.A8086:
if reg_index <= 9:
reg_value = self.ql.reg.read(self.tables[QL_ARCH.A8086][reg_index-1])
reg_value = self.ql.arch.regs.read(self.tables[QL_ARCH.A8086][reg_index-1])
else:
reg_value = 0
reg_value = self.addr_to_str(reg_value)
elif self.ql.archtype== QL_ARCH.X86:
elif self.ql.arch.type == QL_ARCH.X86:
if reg_index <= 24:
reg_value = self.ql.reg.read(self.tables[QL_ARCH.X86][reg_index-1])
reg_value = self.ql.arch.regs.read(self.tables[QL_ARCH.X86][reg_index-1])
else:
reg_value = 0
reg_value = self.addr_to_str(reg_value)
elif self.ql.archtype== QL_ARCH.X8664:
elif self.ql.arch.type == QL_ARCH.X8664:
if reg_index <= 32:
reg_value = self.ql.reg.read(self.tables[QL_ARCH.X8664][reg_index-1])
reg_value = self.ql.arch.regs.read(self.tables[QL_ARCH.X8664][reg_index-1])
else:
reg_value = 0
if reg_index <= 17:
@@ -377,26 +377,26 @@ class QlGdb(QlDebugger, object):
elif 17 < reg_index:
reg_value = self.addr_to_str(reg_value, short = True)
elif self.ql.archtype== QL_ARCH.ARM:
elif self.ql.arch.type == QL_ARCH.ARM:
if reg_index < 26:
reg_value = self.ql.reg.read(self.tables[QL_ARCH.ARM][reg_index - 1])
reg_value = self.ql.arch.regs.read(self.tables[QL_ARCH.ARM][reg_index - 1])
else:
reg_value = 0
reg_value = self.addr_to_str(reg_value)
elif self.ql.archtype== QL_ARCH.ARM64:
elif self.ql.arch.type == QL_ARCH.ARM64:
if reg_index <= 32:
reg_value = self.ql.reg.read(self.tables[QL_ARCH.ARM64][reg_index - 1])
reg_value = self.ql.arch.regs.read(self.tables[QL_ARCH.ARM64][reg_index - 1])
else:
reg_value = 0
reg_value = self.addr_to_str(reg_value)
elif self.ql.archtype== QL_ARCH.MIPS:
elif self.ql.arch.type == QL_ARCH.MIPS:
if reg_index <= 37:
reg_value = self.ql.reg.read(self.tables[QL_ARCH.MIPS][reg_index - 1])
reg_value = self.ql.arch.regs.read(self.tables[QL_ARCH.MIPS][reg_index - 1])
else:
reg_value = 0
if self.ql.archendian == QL_ENDIAN.EL:
if self.ql.arch.endian == QL_ENDIAN.EL:
reg_value = self.addr_to_str(reg_value, endian="little")
else:
reg_value = self.addr_to_str(reg_value)
@@ -413,50 +413,50 @@ class QlGdb(QlDebugger, object):
def handle_P(subcmd):
reg_index, reg_data = subcmd.split('=')
reg_index = int(reg_index, 16)
reg_name = self.tables[self.ql.archtype][reg_index]
reg_name = self.tables[self.ql.arch.type][reg_index]
if self.ql.archtype== QL_ARCH.A8086:
if self.ql.arch.type == QL_ARCH.A8086:
reg_data = int(reg_data, 16)
reg_data = int.from_bytes(struct.pack('<I', reg_data), byteorder='big')
self.ql.reg.write(reg_name, reg_data)
self.ql.arch.regs.write(reg_name, reg_data)
elif self.ql.archtype== QL_ARCH.X86:
elif self.ql.arch.type == QL_ARCH.X86:
reg_data = int(reg_data, 16)
reg_data = int.from_bytes(struct.pack('<I', reg_data), byteorder='big')
self.ql.reg.write(reg_name, reg_data)
self.ql.arch.regs.write(reg_name, reg_data)
elif self.ql.archtype== QL_ARCH.X8664:
elif self.ql.arch.type == QL_ARCH.X8664:
if reg_index <= 17:
reg_data = int(reg_data, 16)
reg_data = int.from_bytes(struct.pack('<Q', reg_data), byteorder='big')
self.ql.reg.write(reg_name, reg_data)
self.ql.arch.regs.write(reg_name, reg_data)
else:
reg_data = int(reg_data[:8], 16)
reg_data = int.from_bytes(struct.pack('<I', reg_data), byteorder='big')
self.ql.reg.write(reg_name, reg_data)
self.ql.arch.regs.write(reg_name, reg_data)
elif self.ql.archtype== QL_ARCH.ARM:
elif self.ql.arch.type == QL_ARCH.ARM:
reg_data = int(reg_data, 16)
reg_data = int.from_bytes(struct.pack('<I', reg_data), byteorder='big')
self.ql.reg.write(reg_name, reg_data)
self.ql.arch.regs.write(reg_name, reg_data)
elif self.ql.archtype== QL_ARCH.ARM64:
elif self.ql.arch.type == QL_ARCH.ARM64:
reg_data = int(reg_data, 16)
reg_data = int.from_bytes(struct.pack('<Q', reg_data), byteorder='big')
self.ql.reg.write(reg_name, reg_data)
self.ql.arch.regs.write(reg_name, reg_data)
elif self.ql.archtype== QL_ARCH.MIPS:
elif self.ql.arch.type == QL_ARCH.MIPS:
reg_data = int(reg_data, 16)
if self.ql.archendian == QL_ENDIAN.EL:
if self.ql.arch.endian == QL_ENDIAN.EL:
reg_data = int.from_bytes(struct.pack('<I', reg_data), byteorder='little')
else:
reg_data = int.from_bytes(struct.pack('<I', reg_data), byteorder='big')
self.ql.reg.write(reg_name, reg_data)
self.ql.arch.regs.write(reg_name, reg_data)
if reg_name == self.ql.reg.arch_pc_name:
if reg_name == self.ql.arch.regs.arch_pc_name:
self.gdb.current_address = reg_data
self.ql.log.info("gdb> Write to register %s with %x\n" % (self.tables[self.ql.archtype][reg_index], reg_data))
self.ql.log.info("gdb> Write to register %s with %x\n" % (self.tables[self.ql.arch.type][reg_index], reg_data))
self.send('OK')
@@ -492,7 +492,7 @@ class QlGdb(QlDebugger, object):
elif subcmd.startswith('Xfer:features:read'):
xfercmd_file = subcmd.split(':')[3]
xfercmd_abspath = os.path.dirname(os.path.abspath(__file__))
xml_folder = arch_convert_str(self.ql.archtype).lower()
xml_folder = arch_convert_str(self.ql.arch.type).lower()
xfercmd_file = os.path.join(xfercmd_abspath,"xml",xml_folder, xfercmd_file)
if os.path.exists(xfercmd_file) and self.ql.ostype is not QL_OS.WINDOWS:
+53 -44
View File
@@ -3,52 +3,59 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from unicorn import *
from qiling.const import *
from qiling import Qiling
from qiling.const import QL_ARCH
PROMPT = r'gdb>'
class QlGdbUtils(object):
class QlGdbUtils:
def __init__(self):
self.ql: Qiling
self.current_address = 0x0
self.current_address_size = 0x0
self.last_bp = 0x0
self.ql = None
self.entry_point = None
self.exit_point = None
self.soft_bp = False
self.has_soft_bp = False
self.bp_list = []
self.mapping = []
# self.mapping = []
self.breakpoint_count = 0x0
self.skip_bp_count = 0x0
self._tmp_hook = None
def initialize(self, ql, hook_address, exit_point=None, mappings=None):
def initialize(self, ql: Qiling, hook_address: int, exit_point: int, mappings=[]):
self.ql = ql
if self.ql.baremetal:
if ql.baremetal:
self.current_address = self.entry_point
else:
self.current_address = self.entry_point = self.ql.os.entry_point
self.current_address = self.entry_point = ql.os.entry_point
self.exit_point = exit_point
self.mapping = mappings
self._tmp_hook = self.ql.hook_address(self.entry_point_hook, hook_address)
# self.mapping = mappings
def entry_point_hook(self, ql, *args, **kwargs):
self.ql.hook_del(self._tmp_hook)
self.ql.hook_code(self.dbg_hook)
self.ql.stop()
self.ql.log.info("gdb> Stop at entry point: %#x" % self.ql.reg.arch_pc)
def __entry_point_hook(ql: Qiling):
ql.hook_del(ep_hret)
ql.hook_code(self.dbg_hook)
ql.stop()
def dbg_hook(self, ql, address, size):
"""
Modified this function for qiling.gdbserver by kabeor from https://github.com/iGio90/uDdbg
ql.log.info(f'{PROMPT} Stop at entry point: {ql.arch.regs.arch_pc:#x}')
ep_hret = ql.hook_address(__entry_point_hook, hook_address)
def dbg_hook(self, ql: Qiling, address: int, size: int):
"""Modified this function for qiling.gdbserver by kabeor from https://github.com/iGio90/uDdbg
"""
try:
if self.ql.archtype == QL_ARCH.ARM:
address = address | self.arch.thumb
if ql.arch.type == QL_ARCH.ARM:
if ql.arch.is_thumb:
address += 1
self.mapping.append([(hex(address))])
# self.mapping.append([(hex(address))])
self.current_address = address
hit_soft_bp = False
@@ -62,49 +69,51 @@ class QlGdbUtils(object):
self.skip_bp_count -= 1
else:
self.breakpoint_count += 1
self.ql.stop()
ql.stop()
self.last_bp = address
ql.log.info("gdb> Breakpoint found, stop at address: 0x%x" % address)
ql.log.info(f'{PROMPT} Breakpoint found, stop at address: {address:#x}')
elif address == self.last_bp:
self.last_bp = 0x0
self.has_soft_bp = hit_soft_bp
if self.current_address + size == self.exit_point:
ql.log.debug("gdb> emulation entrypoint at 0x%x" % (self.entry_point))
ql.log.debug("gdb> emulation exitpoint at 0x%x" % (self.exit_point))
except KeyboardInterrupt as ex:
ql.log.info("gdb> Paused at 0x%x, instruction size = %u" % (address, size))
self.ql.stop()
ql.log.debug(f'{PROMPT} emulation entrypoint at {self.entry_point:#x}')
ql.log.debug(f'{PROMPT} emulation exitpoint at {self.exit_point:#x}')
except KeyboardInterrupt:
ql.log.info(f'{PROMPT} Paused at {address:#x}, instruction size = {size:d}')
ql.stop()
except:
raise
raise
def bp_insert(self, addr):
def bp_insert(self, addr: int):
if addr not in self.bp_list:
self.bp_list.append(addr)
self.ql.log.info('gdb> Breakpoint added at: 0x%x' % addr)
self.ql.log.info(f'{PROMPT} Breakpoint added at: {addr:#x}')
def bp_remove(self, addr, type = None, len = None):
def bp_remove(self, addr: int, type = None, len = None):
self.bp_list.remove(addr)
self.ql.log.info('gdb> Breakpoint removed at: 0x%x' % addr)
self.ql.log.info(f'{PROMPT} Breakpoint removed at: {addr:#x}')
def resume_emu(self, address=None, skip_bp=0):
"""
Modified this function for qiling.gdbserver by kabeor from https://github.com/iGio90/uDdbg
def resume_emu(self, address: int = None, skip_bp: int = 0):
"""Modified this function for qiling.gdbserver by kabeor from https://github.com/iGio90/uDdbg
"""
if address is not None:
if self.ql.archtype == QL_ARCH.ARM:
address |= self.ql.arch.thumb
if self.ql.arch.type == QL_ARCH.ARM:
if self.ql.arch.is_thumb:
address += 1
self.current_address = address
self.skip_bp_count = skip_bp
if self.exit_point is not None:
self.ql.log.info('gdb> Resume at: 0x%x' % self.current_address)
self.ql.log.info(f'{PROMPT} Resume at: {self.current_address:#x}')
self.ql.emu_start(self.current_address, self.exit_point)
+18 -24
View File
@@ -54,10 +54,10 @@ def examine_mem(ql: Qiling, line: str) -> Union[bool, (str, int, int)]:
else:
rest = _args
if ql.archtype in (QL_ARCH.ARM, QL_ARCH.ARM_THUMB):
if ql.arch.type == QL_ARCH.ARM:
rest = rest.replace("fp", "r11")
elif ql.archtype == QL_ARCH.MIPS:
elif ql.arch.type == QL_ARCH.MIPS:
rest = rest.replace("fp", "s8")
# for supporting addition of register with constant value
@@ -66,8 +66,8 @@ def examine_mem(ql: Qiling, line: str) -> Union[bool, (str, int, int)]:
items = []
for elem in elems:
if elem in ql.reg.register_mapping.keys():
items.append(getattr(ql.reg, elem, None))
if elem in ql.arch.regs.register_mapping.keys():
items.append(getattr(ql.arch.regs, elem, None))
else:
items.append(read_int(elem))
@@ -108,8 +108,8 @@ def examine_mem(ql: Qiling, line: str) -> Union[bool, (str, int, int)]:
offset = line * sz * 4
print(f"0x{addr+offset:x}:\t", end="")
idx = line * ql.pointersize
for each in mem_read[idx:idx+ql.pointersize]:
idx = line * ql.arch.pointersize
for each in mem_read[idx:idx+ql.arch.pointersize]:
data = unpack(each, sz)
prefix = "0x" if ft in ("x", "a") else ""
pad = '0' + str(sz*2) if ft in ('x', 'a', 't') else ''
@@ -121,11 +121,6 @@ def examine_mem(ql: Qiling, line: str) -> Union[bool, (str, int, int)]:
return True
# get terminal window height and width
def get_terminal_size() -> Iterable:
return map(int, os.popen('stty size', 'r').read().split())
# try to read data from ql memory
def _try_read(ql: Qiling, address: int, size: int) -> Optional[bytes]:
@@ -145,23 +140,23 @@ def _try_read(ql: Qiling, address: int, size: int) -> Optional[bytes]:
"""
Context Manager for rendering UI
"""
COLORS = (color.DARKCYAN, color.BLUE, color.RED, color.YELLOW, color.GREEN, color.PURPLE, color.CYAN, color.WHITE)
# decorator function for printing divider
def context_printer(field_name, ruler="─"):
def context_printer(title: str, *, footer: bool = False, ruler="─"):
def decorator(context_dumper):
def wrapper(*args, **kwargs):
height, width = get_terminal_size()
bar = (width - len(field_name)) // 2 - 1
print(ruler * bar, field_name, ruler * bar)
cols, _ = os.get_terminal_size()
print(title.center(cols, ruler))
context_dumper(*args, **kwargs)
if "DISASM" in field_name:
print(ruler * width)
if footer:
print(ruler * cols)
return wrapper
return decorator
@@ -173,7 +168,7 @@ def setup_ctx_manager(ql: Qiling) -> CtxManager:
QL_ARCH.ARM_THUMB: CtxManager_ARM,
QL_ARCH.CORTEX_M: CtxManager_ARM,
QL_ARCH.MIPS: CtxManager_MIPS,
}.get(ql.archtype)(ql)
}.get(ql.arch.type)(ql)
class CtxManager(object):
@@ -184,7 +179,7 @@ class CtxManager(object):
def print_asm(self, insn: CsInsn, to_jump: Optional[bool] = None, address: int = None) -> None:
opcode = "".join(f"{b:02x}" for b in insn.bytes)
if self.ql.archtype in (QL_ARCH.X86, QL_ARCH.X8664):
if self.ql.arch.type in (QL_ARCH.X86, QL_ARCH.X8664):
trace_line = f"0x{insn.address:08x} │ {opcode:20s} {insn.mnemonic:10} {insn.op_str:35s}"
else:
trace_line = f"0x{insn.address:08x} │ {opcode:10s} {insn.mnemonic:10} {insn.op_str:35s}"
@@ -231,7 +226,7 @@ class CtxManager(object):
print(f" ◂— 0x{self.ql.unpack(buf[0]):08x}", end="")
print()
@context_printer("[ DISASM ]")
@context_printer("[ DISASM ]", footer=True)
def context_asm(self):
# assembly before current location
past_list = []
@@ -424,7 +419,7 @@ class CtxManager_X86(CtxManager):
print(lines.format(*cur_regs.values()))
print(color.GREEN, "EFLAGS: [CF: {flags[CF]}, PF: {flags[PF]}, AF: {flags[AF]}, ZF: {flags[ZF]}, SF: {flags[SF]}, OF: {flags[OF]}]".format(flags=get_x86_eflags(self.ql.reg.ef)), color.END, sep="")
@context_printer("[ DISASM ]")
@context_printer("[ DISASM ]", footer=True)
def context_asm(self):
past_list = []
cur_addr = self.ql.reg.arch_pc
@@ -504,4 +499,3 @@ class CtxManager_CORTEX_M(CtxManager):
if __name__ == "__main__":
pass
+8 -8
View File
@@ -13,7 +13,7 @@ from qiling.const import QL_ARCH, QL_VERBOSE
from qiling.debugger import QlDebugger
from .frontend import examine_mem, setup_ctx_manager
from .utils import is_thumb, parse_int, setup_branch_predictor, disasm
from .utils import parse_int, setup_branch_predictor, disasm
from .utils import Breakpoint, TempBreakpoint, read_inst
from .const import color
@@ -68,7 +68,7 @@ class QlQdb(cmd.Cmd, QlDebugger):
self.cur_addr = self.ql.loader.entry_point
if self.ql.archtype == QL_ARCH.CORTEX_M:
if self.ql.arch.type == QL_ARCH.CORTEX_M:
self._run()
else:
@@ -83,7 +83,7 @@ class QlQdb(cmd.Cmd, QlDebugger):
getter for current address of qiling instance
"""
return self.ql.reg.arch_pc
return self.ql.arch.regs.arch_pc
@cur_addr.setter
def cur_addr(self: QlQdb, address: int) -> None:
@@ -91,7 +91,7 @@ class QlQdb(cmd.Cmd, QlDebugger):
setter for current address of qiling instance
"""
self.ql.reg.arch_pc = address
self.ql.arch.regs.arch_pc = address
def _save(self: QlQdb, *args) -> None:
"""
@@ -115,7 +115,7 @@ class QlQdb(cmd.Cmd, QlDebugger):
if not address:
address = self.cur_addr
if self.ql.archtype == QL_ARCH.CORTEX_M and self.ql.count != 0:
if self.ql.arch.type == QL_ARCH.CORTEX_M and self.ql.count != 0:
while self.ql.count:
@@ -132,7 +132,7 @@ class QlQdb(cmd.Cmd, QlDebugger):
return
if self.ql.archtype in (QL_ARCH.ARM, QL_ARCH.ARM_THUMB, QL_ARCH.CORTEX_M) and is_thumb(self.ql.reg.cpsr):
if self.ql.arch.type in (QL_ARCH.ARM, QL_ARCH.CORTEX_M) and self.ql.arch.is_thumb:
address |= 1
self.ql.emu_start(begin=address, end=end, count=count)
@@ -235,7 +235,7 @@ class QlQdb(cmd.Cmd, QlDebugger):
if prophecy.where is True:
return True
if self.ql.archtype == QL_ARCH.CORTEX_M:
if self.ql.arch.type == QL_ARCH.CORTEX_M:
self.ql.arch.step()
else:
self._run(count=1)
@@ -285,7 +285,7 @@ class QlQdb(cmd.Cmd, QlDebugger):
restore qiling instance context to initial state
"""
if self.ql.archtype != QL_ARCH.CORTEX_M:
if self.ql.arch.type != QL_ARCH.CORTEX_M:
self.ql.restore(self._init_state)
self.do_context()
+13 -19
View File
@@ -4,7 +4,11 @@
#
from __future__ import annotations
from typing import Callable, Optional, Mapping
from typing import Callable, Optional, Mapping, Tuple
from capstone import CsInsn
from qiling import Qiling
from qiling.const import *
from collections import namedtuple
@@ -37,7 +41,7 @@ def signed_val(val: int) -> int:
return (val-1 << 32) if is_negative(val) else val
def get_cpsr(bits: int) -> (bool, bool, bool, bool):
def get_cpsr(bits: int) -> Tuple[bool, bool, bool, bool]:
return (
bits & 0x10000000 != 0, # V, overflow flag
bits & 0x20000000 != 0, # C, carry flag
@@ -57,32 +61,22 @@ def get_x86_eflags(bits: int) -> Dict[str, bool]:
}
def is_thumb(bits: int) -> bool:
return bits & 0x00000020 != 0
def disasm(ql: Qiling, address: int, detail: bool = False) -> Optional[int]:
def disasm(ql: Qiling, address: int, detail: bool = False) -> Optional[CsInsn]:
"""
helper function for disassembling
"""
md = ql.disassembler
md = ql.arch.disassembler
md.detail = detail
try:
ret = next(md.disasm(read_inst(ql, address), address))
except StopIteration:
ret = None
return ret
return next(md.disasm(read_inst(ql, address), address), None)
def read_inst(ql: Qiling, addr: int) -> int:
result = ql.mem.read(addr, 4)
if ql.archtype in (QL_ARCH.ARM, QL_ARCH.ARM_THUMB, QL_ARCH.CORTEX_M):
if is_thumb(ql.reg.cpsr):
if ql.arch.type in (QL_ARCH.ARM, QL_ARCH.CORTEX_M):
if ql.arch.is_thumb:
first_two = ql.unpack16(ql.mem.read(addr, 2))
result = ql.pack16(first_two)
@@ -96,7 +90,7 @@ def read_inst(ql: Qiling, addr: int) -> int:
latter_two = ql.unpack16(ql.mem.read(addr+2, 2))
result += ql.pack16(latter_two)
elif ql.archtype in (QL_ARCH.X86, QL_ARCH.X8664):
elif ql.arch.type in (QL_ARCH.X86, QL_ARCH.X8664):
# due to the variadic lengh of x86 instructions ( 1~15 )
# always assume the maxium size for disassembler to tell
# what is it exactly.
@@ -115,7 +109,7 @@ def setup_branch_predictor(ql: Qiling) -> BranchPredictor:
QL_ARCH.ARM_THUMB: BranchPredictor_ARM,
QL_ARCH.CORTEX_M: BranchPredictor_CORTEX_M,
QL_ARCH.MIPS: BranchPredictor_MIPS,
}.get(ql.archtype)(ql)
}.get(ql.arch.type)(ql)
class Prophecy(object):
def __init__(self):
+39 -62
View File
@@ -51,7 +51,6 @@ from qiling.arch.x86_const import reg_map_st as x86_reg_map_st
from qiling.arch.arm_const import reg_map as arm_reg_map
from qiling.arch.arm64_const import reg_map as arm64_reg_map
from qiling.arch.mips_const import reg_map as mips_reg_map
from qiling.utils import ql_get_arch_bits
from qiling import __version__ as QLVERSION
from qiling.os.filestruct import ql_file
from keystone import *
@@ -488,10 +487,6 @@ class QlEmuRegView(simplecustviewer_t):
return True
def SetReg(self, addr, ql:Qiling):
arch = ql.archtype
if arch == "":
return
#clear
self.ClearLines()
@@ -509,7 +504,7 @@ class QlEmuRegView(simplecustviewer_t):
for regs in reglist:
for reg in regs:
line += COLSTR(" %4s: " % str(reg), SCOLOR_REG)
regvalue = ql.reg.read(reg)
regvalue = ql.arch.regs.read(reg)
if arch in [QL_ARCH.X8664, QL_ARCH.ARM64]:
value_format = "0x%.16X"
else:
@@ -547,16 +542,12 @@ class QlEmuStackView(simplecustviewer_t):
if ql is None:
return
sp = ql.reg.arch_sp
sp = ql.arch.regs.arch_sp
self.AddLine('')
self.AddLine(COLSTR(' Stack at 0x%X' % sp, SCOLOR_AUTOCMT))
self.AddLine('')
arch = ql.archtype
if arch == "":
return
reg_bit_size = ql_get_arch_bits(arch)
reg_bit_size = ql.arch.bits
reg_byte_size = reg_bit_size // 8
value_format = '% .16X' if reg_bit_size == 64 else '% .8X'
@@ -832,18 +823,7 @@ class QlEmuMisc:
QL_ARCH.MIPS : list({**mips_reg_map}.keys()),
}
if ql.archtype == QL_ARCH.X86:
return tables[QL_ARCH.X86]
elif ql.archtype == QL_ARCH.X8664:
return tables[QL_ARCH.X8664]
elif ql.archtype == QL_ARCH.ARM:
return tables[QL_ARCH.ARM]
elif ql.archtype == QL_ARCH.ARM64:
return tables[QL_ARCH.ARM64]
elif ql.archtype == QL_ARCH.MIPS:
return tables[QL_ARCH.MIPS]
else:
return []
return tables.get(ql.arch.type, [])
@staticmethod
def url_download(url):
@@ -898,22 +878,19 @@ class QlEmuQiling:
def __init__(self):
self.path = None
self.rootfs = None
self.ql = None
self.ql: Qiling
self.status = None
self.exit_addr = None
self.baseaddr = None
self.env = {}
def start(self, *args, **kwargs):
if sys.platform != 'win32':
qlstdin = QlEmuMisc.QLStdIO('stdin', sys.__stdin__.fileno())
qlstdout = QlEmuMisc.QLStdIO('stdout', sys.__stdout__.fileno())
qlstderr = QlEmuMisc.QLStdIO('stderr', sys.__stderr__.fileno())
self.ql = Qiling(argv=self.path, rootfs=self.rootfs, verbose=QL_VERBOSE.DEBUG, env=self.env, log_plain=True, *args, **kwargs)
if sys.platform != 'win32':
self.ql = Qiling(argv=self.path, rootfs=self.rootfs, verbose=QL_VERBOSE.DEBUG, env=self.env, stdin=qlstdin, stdout=qlstdout, stderr=qlstderr, log_plain=True, *args, **kwargs)
else:
self.ql = Qiling(argv=self.path, rootfs=self.rootfs, verbose=QL_VERBOSE.DEBUG, env=self.env, log_plain=True, *args, **kwargs)
self.ql.os.stdin = QlEmuMisc.QLStdIO('stdin', sys.__stdin__.fileno())
self.ql.os.stdout = QlEmuMisc.QLStdIO('stdout', sys.__stdout__.fileno())
self.ql.os.stderr = QlEmuMisc.QLStdIO('stderr', sys.__stderr__.fileno())
self.exit_addr = self.ql.os.exit_point
if self.ql.ostype == QL_OS.LINUX:
@@ -923,9 +900,9 @@ class QlEmuQiling:
if elf_header['e_type'] == 'ET_EXEC':
self.baseaddr = self.ql.os.elf_mem_start
elif elf_header['e_type'] == 'ET_DYN':
if self.ql.archbit == 32:
if self.ql.arch.bits == 32:
self.baseaddr = int(self.ql.os.profile.get("OS32", "load_address"), 16)
elif self.ql.archbit == 64:
elif self.ql.arch.bits == 64:
self.baseaddr = int(self.ql.os.profile.get("OS64", "load_address"), 16)
else:
self.baseaddr = 0x0
@@ -935,12 +912,12 @@ class QlEmuQiling:
def set_reg(self):
reglist = QlEmuMisc.get_reg_map(self.ql)
regs = [ [ row, int(self.ql.reg.read(row)), ql_get_arch_bits(self.ql.archtype) ] for row in reglist ]
regs = [ [ row, int(self.ql.arch.regs.read(row)), self.ql.arch.bits ] for row in reglist ]
regs_len = len(regs)
RegDig = QlEmuRegDialog(regs)
if RegDig.show():
for idx, val in enumerate(RegDig.items[0:regs_len-1]):
self.ql.reg.write(reglist[idx], val[1])
self.ql.arch.regs.write(reglist[idx], val[1])
return True
else:
return False
@@ -1090,7 +1067,7 @@ class QlEmuPlugin(plugin_t, UI_Hooks):
self.qlemu.ql.restore(self.qlemu.status)
show_wait_box("Qiling is processing ...")
try:
self.qlemu.run(begin=self.qlemu.ql.reg.arch_pc, end=self.qlemu.exit_addr)
self.qlemu.run(begin=self.qlemu.ql.arch.regs.arch_pc, end=self.qlemu.exit_addr)
finally:
hide_wait_box()
else:
@@ -1103,7 +1080,7 @@ class QlEmuPlugin(plugin_t, UI_Hooks):
if userhook and userhook is not None:
for hook in userhook:
self.qlemu.ql.hook_del(hook)
self.ql_update_views(self.qlemu.ql.reg.arch_pc, self.qlemu.ql)
self.ql_update_views(self.qlemu.ql.arch.regs.arch_pc, self.qlemu.ql)
else:
logging.error('Qiling should be setup firstly.')
@@ -1131,17 +1108,17 @@ class QlEmuPlugin(plugin_t, UI_Hooks):
for hook in userhook:
self.qlemu.ql.hook_del(hook)
self.qlemu.status = self.qlemu.ql.save()
self.ql_update_views(self.qlemu.ql.reg.arch_pc, self.qlemu.ql)
self.ql_update_views(self.qlemu.ql.arch.regs.arch_pc, self.qlemu.ql)
else:
logging.error('Qiling should be setup firstly.')
def ql_set_pc(self):
if self.qlinit:
ea = IDA.get_current_address()
self.qlemu.ql.reg.arch_pc = ea
self.qlemu.ql.arch.regs.arch_pc = ea
logging.info(f"QIling PC set to {hex(ea)}")
self.qlemu.status = self.qlemu.ql.save()
self.ql_update_views(self.qlemu.ql.reg.arch_pc, self.qlemu.ql)
self.ql_update_views(self.qlemu.ql.arch.regs.arch_pc, self.qlemu.ql)
else:
logging.error('Qiling should be setup firstly.')
@@ -1153,7 +1130,7 @@ class QlEmuPlugin(plugin_t, UI_Hooks):
self.qlemu.ql.restore(self.qlemu.status)
show_wait_box("Qiling is processing ...")
try:
self.qlemu.run(begin=self.qlemu.ql.reg.arch_pc, end=curr_addr+self.qlemu.baseaddr-get_imagebase())
self.qlemu.run(begin=self.qlemu.ql.arch.regs.arch_pc, end=curr_addr+self.qlemu.baseaddr-get_imagebase())
finally:
hide_wait_box()
else:
@@ -1166,7 +1143,7 @@ class QlEmuPlugin(plugin_t, UI_Hooks):
set_color(curr_addr, CIC_ITEM, 0x00B3CBFF)
self.qlemu.ql.hook_del(untillhook)
self.qlemu.status = self.qlemu.ql.save()
self.ql_update_views(self.qlemu.ql.reg.arch_pc, self.qlemu.ql)
self.ql_update_views(self.qlemu.ql.arch.regs.arch_pc, self.qlemu.ql)
else:
logging.error('Qiling should be setup firstly.')
@@ -1178,11 +1155,11 @@ class QlEmuPlugin(plugin_t, UI_Hooks):
self.stephook = self.qlemu.ql.hook_code(callback=self.ql_step_hook)
if self.userobj is not None:
userhook = self.userobj.custom_step(self.qlemu.ql)
self.qlemu.run(begin=self.qlemu.ql.reg.arch_pc, end=self.qlemu.exit_addr)
self.qlemu.run(begin=self.qlemu.ql.arch.regs.arch_pc, end=self.qlemu.exit_addr)
if userhook and userhook is not None:
for hook in userhook:
self.qlemu.ql.hook_del(hook)
self.ql_update_views(self.qlemu.ql.reg.arch_pc, self.qlemu.ql)
self.ql_update_views(self.qlemu.ql.arch.regs.arch_pc, self.qlemu.ql)
else:
logging.error('Qiling should be setup firstly.')
@@ -1203,7 +1180,7 @@ class QlEmuPlugin(plugin_t, UI_Hooks):
def ql_chang_reg(self):
if self.qlinit:
self.qlemu.set_reg()
self.ql_update_views(self.qlemu.ql.reg.arch_pc, self.qlemu.ql)
self.ql_update_views(self.qlemu.ql.arch.regs.arch_pc, self.qlemu.ql)
self.qlemu.status = self.qlemu.ql.save()
else:
logging.error('Qiling should be setup firstly.')
@@ -1235,7 +1212,7 @@ class QlEmuPlugin(plugin_t, UI_Hooks):
self.qlemuregview = QlEmuRegView(self)
QlEmuRegView(self)
self.qlemuregview.Create()
self.qlemuregview.SetReg(self.qlemu.ql.reg.arch_pc, self.qlemu.ql)
self.qlemuregview.SetReg(self.qlemu.ql.arch.regs.arch_pc, self.qlemu.ql)
self.qlemuregview.Show()
self.qlemuregview.Refresh()
else:
@@ -1461,9 +1438,9 @@ class QlEmuPlugin(plugin_t, UI_Hooks):
ins_list = list(IDA.micro_code_from_mbb(next_mbb))
first_ins = ins_list[0]
imm = first_ins.l.nnn.value
reg_name = ida_hexrays.get_mreg_name(first_ins.d.r, ql.pointersize)
reg_name = ida_hexrays.get_mreg_name(first_ins.d.r, ql.arch.pointersize)
logging.info(f"Froce set {reg_name} to {hex(imm)}")
ql.reg.__setattr__(reg_name, imm)
ql.arch.regs.__setattr__(reg_name, imm)
return True
def _ida_address_after_branch(self, ida_addr):
@@ -1489,9 +1466,9 @@ class QlEmuPlugin(plugin_t, UI_Hooks):
if "x86" in IDA.get_ql_arch_string(): # cmovlg eax, ebx
reg1 = IDA.print_operand(ida_addr, 0).lower()
reg2 = IDA.print_operand(ida_addr, 1).lower()
reg2_val = ql.reg.__getattribute__(reg2)
reg2_val = ql.arch.regs.__getattribute__(reg2)
logging.info(f"Force set {reg1} to {hex(reg2_val)}")
ql.reg.__setattr__(reg1, reg2_val)
ql.arch.regs.__setattr__(reg1, reg2_val)
return True
elif "arm" in IDA.get_ql_arch_string():
instr = IDA.get_instruction(ida_addr).lower()
@@ -1504,14 +1481,14 @@ class QlEmuPlugin(plugin_t, UI_Hooks):
reg = IDA.print_operand(ida_addr, 0).lower()
val = (high << 16) + low
logging.info(f"Force set {reg} to {hex(val)}")
ql.reg.__setattr__(reg, val)
ql.arch.regs.__setattr__(reg, val)
return True
elif "csel" in instr: # csel dst, src1, src2, cond
dst = IDA.print_operand(ida_addr, 0).lower()
src = IDA.print_operand(ida_addr, 2).lower()
src_val = ql.reg.__getattribute__(src)
src_val = ql.arch.regs.__getattribute__(src)
logging.info(f"Force set {dst} to {hex(src_val)}")
ql.reg.__setattr__(dst, src_val)
ql.arch.regs.__setattr__(dst, src_val)
return True
return False
@@ -1554,12 +1531,12 @@ class QlEmuPlugin(plugin_t, UI_Hooks):
else:
next_ida_addr = ida_addr + IDA.get_instruction_size(ida_addr)
logging.info(f"Goto {hex(next_ida_addr)} after branch...")
ql.reg.arch_pc = self.deflatqlemu.ql_addr_from_ida(next_ida_addr) + self.append
ql.arch.regs.arch_pc = self.deflatqlemu.ql_addr_from_ida(next_ida_addr) + self.append
ida_addr = next_ida_addr
# TODO: Maybe we can detect whether the program will access unmapped
# here so that we won't map the memory.
if self._has_call_insn(ida_addr):
ql.reg.arch_pc += IDA.get_instruction_size(ida_addr) + self.append
ql.arch.regs.arch_pc += IDA.get_instruction_size(ida_addr) + self.append
return
if start_bb_id == cur_bb.id:
return
@@ -1617,10 +1594,10 @@ class QlEmuPlugin(plugin_t, UI_Hooks):
def _log_verbose(self, ql, addr, size):
logging.debug(f"addr: {hex(addr)} ida_addr: {hex(self.deflatqlemu.ida_addr_from_ql_addr(addr))}")
registers = [ k for k in ql.reg.register_mapping.keys() if type(k) is str ]
registers = [ k for k in ql.arch.regs.register_mapping.keys() if type(k) is str ]
for idx in range(0, len(registers), 3):
regs = registers[idx:idx+3]
s = "\t".join(map(lambda v: f"{v:4}: {ql.reg.__getattribute__(v):016x}", regs))
s = "\t".join(map(lambda v: f"{v:4}: {ql.arch.regs.__getattribute__(v):016x}", regs))
logging.debug(s)
# Q: Why we need emulation to help us find real control flow considering there are some
@@ -1640,8 +1617,8 @@ class QlEmuPlugin(plugin_t, UI_Hooks):
if IDA.get_ql_arch_string() == "arm32":
if self._thumb_detect(first_block.start_ea):
logging.info(f"Thumb detected, enable it.")
self.deflatqlemu.start(archtype=QL_ARCH.ARM_THUMB)
self.deflatqlemu.ql.reg.cpsr |= 0x20
self.deflatqlemu.start(archtype=QL_ARCH.ARM, thumb=True)
self.deflatqlemu.ql.arch.regs.cpsr |= 0x20
self.append = 1
else:
self.deflatqlemu.start()
@@ -1693,7 +1670,7 @@ class QlEmuPlugin(plugin_t, UI_Hooks):
# assembler implmentation with keystone.
def _initialize_keystone(self):
if self.ks is None:
self.ks = self.deflatqlemu.ql.create_assembler()
self.ks = self.deflatqlemu.ql.arch.assembler
def _asm(self, *args, **kwargs):
@@ -2003,7 +1980,7 @@ class QlEmuPlugin(plugin_t, UI_Hooks):
addr = addr - self.qlemu.baseaddr + get_imagebase()
if self.stepflag:
set_color(addr, CIC_ITEM, 0x00FFD700)
self.ql_update_views(self.qlemu.ql.reg.arch_pc, ql)
self.ql_update_views(self.qlemu.ql.arch.regs.arch_pc, ql)
self.qlemu.status = ql.save()
ql.os.stop()
self.qlemu.ql.hook_del(self.stephook)
+2 -2
View File
@@ -12,11 +12,11 @@ class Report:
def __init__(self, ql):
self.filename = ql.argv
self.rootfs = ql.rootfs
self.arch = list(arch_map.keys())[list(arch_map.values()).index(ql.archtype)]
self.arch = list(arch_map.keys())[list(arch_map.values()).index(ql.arch.type)]
self.os = list(os_map.keys())[list(os_map.values()).index(ql.ostype)]
self.env = ql.env
self.strings = set()
for string in ql.os.utils.appeared_strings:
for string in ql.os.stats.appeared_strings:
strings = string.split(" ")
self.strings |= set(strings)
self.profile = {}
+3 -3
View File
@@ -59,7 +59,7 @@ def __get_trace_records(ql: Qiling, address: int, size: int, md: Cs) -> Iterator
for insn in md.disasm(buf, address):
# BUG: insn.regs_read doesn't work well, so we use insn.regs_access()[0]
state = tuple((reg, ql.reg.read(CS_UC_REGS[reg])) for reg in insn.regs_access()[0])
state = tuple((reg, ql.arch.regs.read(CS_UC_REGS[reg])) for reg in insn.regs_access()[0])
yield (insn, state)
@@ -152,7 +152,7 @@ def enable_full_trace(ql: Qiling):
"""
# enable detailed disassembly info
md = ql.create_disassembler()
md = ql.arch.disassembler
md.detail = True
assert md.arch == CS_ARCH_X86, 'currently available only for intel architecture'
@@ -189,7 +189,7 @@ def enable_history_trace(ql: Qiling, nrecords: int):
"""
# enable detailed disassembly info
md = ql.create_disassembler()
md = ql.arch.disassembler
md.detail = True
assert md.arch == CS_ARCH_X86, 'currently available only for intel architecture'
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env python3
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from functools import cached_property
from typing import Optional
import platform
from qiling import utils
from qiling.const import QL_OS, QL_ARCH
class QlHost:
"""Interface to the hosting platform.
"""
@cached_property
def os(self) -> Optional[QL_OS]:
"""Hosting platform OS type.
"""
system = platform.system()
return utils.ostype_convert(system.lower())
@cached_property
def arch(self) -> Optional[QL_ARCH]:
"""Hosting platform architecture type.
"""
machine = platform.machine()
return utils.arch_convert(machine.lower())
+2 -2
View File
@@ -36,7 +36,7 @@ class QlPeripheralUtils:
retval = func(self, offset, size)
if self.verbose:
self.ql.log.info(f'[{self.label.upper()}] [{hex(self.ql.reg.pc)}] [R] {self.field_description(offset, size):{width}s} = {hex(retval)}')
self.ql.log.info(f'[{self.label.upper()}] [{hex(self.ql.arch.regs.arch_pc)}] [R] {self.field_description(offset, size):{width}s} = {hex(retval)}')
return retval
@@ -49,7 +49,7 @@ class QlPeripheralUtils:
if field.startswith('DR') and value <= 255:
extra = f'({repr(chr(value))})'
self.ql.log.info(f'[{self.label.upper()}] [{hex(self.ql.reg.pc)}] [W] {field:{width}s} = {hex(value)} {extra}')
self.ql.log.info(f'[{self.label.upper()}] [{hex(self.ql.arch.regs.pc)}] [W] {field:{width}s} = {hex(value)} {extra}')
return func(self, offset, size, value)
+1 -1
View File
@@ -23,6 +23,6 @@ class QlLoaderBLOB(QlLoader):
heap_size = int(self.ql.os.profile.get("CODE", "heap_size"), 16)
self.ql.os.heap = QlMemoryHeap(self.ql, heap_address, heap_address + heap_size)
self.ql.reg.arch_sp = heap_address - 0x1000
self.ql.arch.regs.arch_sp = heap_address - 0x1000
return
+7 -7
View File
@@ -87,16 +87,16 @@ class QlLoaderDOS(QlLoader):
self.ql.os.fs_mapper.add_fs_mapping(0x80, QlDisk(path, 0x80))
# 0x80 -> first drive
self.ql.reg.dx = 0x80
self.ql.arch.regs.dx = 0x80
else:
raise NotImplementedError()
self.ql.reg.cs = cs
self.ql.reg.ds = cs
self.ql.reg.es = cs
self.ql.reg.ss = ss
self.ql.reg.ip = ip
self.ql.reg.sp = sp
self.ql.arch.regs.cs = cs
self.ql.arch.regs.ds = cs
self.ql.arch.regs.es = cs
self.ql.arch.regs.ss = ss
self.ql.arch.regs.ip = ip
self.ql.arch.regs.sp = sp
self.stack_address = (ss << 4) + sp
self.start_address = (cs << 4) + ip
+37 -36
View File
@@ -70,13 +70,13 @@ class QlLoaderELF(QlLoader):
self.ql.mem.map(self.ql.os.entry_point, self.ql.os.code_ram_size, info="[shellcode_stack]")
self.ql.os.entry_point = (self.ql.os.entry_point + 0x200000 - 0x1000)
self.ql.mem.write(self.ql.os.entry_point, self.ql.code)
self.ql.reg.arch_sp = self.ql.os.entry_point
self.ql.arch.regs.arch_sp = self.ql.os.entry_point
return
section = {
32 : 'OS32',
64 : 'OS64'
}[self.ql.archbit]
}[self.ql.arch.bits]
self.profile = self.ql.os.profile[section]
@@ -115,13 +115,13 @@ class QlLoaderELF(QlLoader):
self.is_driver = (elftype == 'ET_REL')
self.ql.reg.arch_sp = self.stack_address
self.ql.arch.regs.arch_sp = self.stack_address
# No idea why.
if self.ql.ostype == QL_OS.FREEBSD:
# self.ql.reg.rbp = self.stack_address + 0x40
self.ql.reg.rdi = self.stack_address
self.ql.reg.r14 = self.stack_address
# self.ql.arch.regs.rbp = self.stack_address + 0x40
self.ql.arch.regs.rdi = self.stack_address
self.ql.arch.regs.r14 = self.stack_address
@staticmethod
def seg_perm_to_uc_prot(perm: int) -> int:
@@ -216,7 +216,7 @@ class QlLoaderELF(QlLoader):
#
# this workaround unifies such "overlapping" segments, which may apply more permissive
# protection flags to that memory region.
if self.ql.archtype == QL_ARCH.ARM64:
if self.ql.arch.type == QL_ARCH.ARM64:
load_regions[-1] = (prev_lbound, ubound, prev_perms | perms)
continue
@@ -309,7 +309,7 @@ class QlLoaderELF(QlLoader):
"""
data = (s if isinstance(s, bytes) else s.encode("utf-8")) + b'\x00'
top = QlLoaderELF.align(top - len(data), self.ql.pointersize)
top = QlLoaderELF.align(top - len(data), self.ql.arch.pointersize)
self.ql.mem.write(top, data)
return top
@@ -343,12 +343,12 @@ class QlLoaderELF(QlLoader):
elf_phnum = elffile['e_phnum']
elf_entry = load_address + elffile['e_entry']
if self.ql.archbit == 64:
if self.ql.arch.bits == 64:
elf_hwcap = 0x078bfbfd
elif self.ql.archbit == 32:
elif self.ql.arch.bits == 32:
elf_hwcap = 0x1fb8d7
if self.ql.archendian == QL_ENDIAN.EB:
if self.ql.arch.endian == QL_ENDIAN.EB:
# FIXME: considering this is a 32 bits value, it is not a big-endian version of the
# value above like it is meant to be, since the one above has an implied leading zero
# byte (i.e. 0x001fb8d7) which the EB value didn't take into account
@@ -393,7 +393,7 @@ class QlLoaderELF(QlLoader):
self.stack_address = new_stack
self.load_address = load_address
self.images.append(Image(load_address, load_address + mem_end, self.path))
self.init_sp = self.ql.reg.arch_sp
self.init_sp = self.ql.arch.regs.arch_sp
self.ql.os.entry_point = self.entry_point = entry_point
self.ql.os.elf_mem_start = mem_start
@@ -404,7 +404,7 @@ class QlLoaderELF(QlLoader):
self.skip_exit_check = (self.elf_entry != self.entry_point)
# map vsyscall section for some specific needs
if self.ql.archtype == QL_ARCH.X8664 and self.ql.ostype == QL_OS.LINUX:
if self.ql.arch.type == QL_ARCH.X8664 and self.ql.ostype == QL_OS.LINUX:
_vsyscall_addr = int(self.profile.get('vsyscall_address'), 0)
_vsyscall_size = int(self.profile.get('vsyscall_size'), 0)
@@ -413,7 +413,7 @@ class QlLoaderELF(QlLoader):
# each syscall should be 1KiB away
self.ql.mem.map(_vsyscall_addr, _vsyscall_size, info="[vsyscall]")
self.ql.mem.write(_vsyscall_addr, _vsyscall_size * b'\xcc')
assembler = self.ql.create_assembler()
assembler = self.ql.arch.assembler
def __assemble(asm: str) -> bytes:
bs, _ = assembler.asm(asm)
@@ -506,19 +506,19 @@ class QlLoaderELF(QlLoader):
# we need to lookup from address to symbol, so we can find the right callback
# for sys_xxx handler for syscall, the address must be aligned to pointer size
if symbol_name.startswith('sys_'):
self.ql.os.hook_addr = QlLoaderELF.align_up(self.ql.os.hook_addr, self.ql.pointersize)
self.ql.os.hook_addr = QlLoaderELF.align_up(self.ql.os.hook_addr, self.ql.arch.pointersize)
self.import_symbols[self.ql.os.hook_addr] = symbol_name
# FIXME: this is for rootkit to scan for syscall table from page_offset_base
# write address of syscall table to this slot, so syscall scanner can quickly find it
if symbol_name == "page_offset_base":
ql.mem.write(self.ql.os.hook_addr, self.ql.pack(SYSCALL_MEM))
ql.mem.write_ptr(self.ql.os.hook_addr, SYSCALL_MEM)
# we also need to do reverse lookup from symbol to address
rev_reloc_symbols[symbol_name] = self.ql.os.hook_addr
sym_offset = self.ql.os.hook_addr - mem_start
self.ql.os.hook_addr += self.ql.pointersize
self.ql.os.hook_addr += self.ql.arch.pointersize
else:
# local symbol
_section = elffile.get_section(_symbol['st_shndx'])
@@ -539,48 +539,49 @@ class QlLoaderELF(QlLoader):
if rel['r_addend']:
val = sym_offset + rel['r_addend']
val += mem_start
ql.mem.write(loc, ql.pack32(val & 0xFFFFFFFF))
else:
ql.mem.write(loc, ql.pack32(rev_reloc_symbols[symbol_name] & 0xFFFFFFFF))
val = rev_reloc_symbols[symbol_name]
ql.mem.write_ptr(loc, (val & 0xFFFFFFFF), 4)
elif desc == 'R_X86_64_64':
val = sym_offset + rel['r_addend']
val += 0x2000000 # init_module position: FIXME
ql.mem.write(loc, ql.pack64(val))
ql.mem.write_ptr(loc, val, 8)
elif desc == 'R_X86_64_PC64':
val = rel['r_addend'] - loc
val += rev_reloc_symbols[symbol_name]
ql.mem.write(loc, ql.pack64(val))
ql.mem.write_ptr(loc, val, 8)
elif desc in ('R_X86_64_PC32', 'R_X86_64_PLT32'):
val = rel['r_addend'] - loc
val += rev_reloc_symbols[symbol_name]
ql.mem.write(loc, ql.pack32(val & 0xFFFFFFFF))
ql.mem.write_ptr(loc, (val & 0xFFFFFFFF), 4)
elif desc in ('R_386_PC32', 'R_386_PLT32'):
val = ql.mem.read_ptr(loc, 4)
val = rev_reloc_symbols[symbol_name] + val - loc
ql.mem.write(loc, ql.pack32(val & 0xFFFFFFFF))
ql.mem.write_ptr(loc, (val & 0xFFFFFFFF), 4)
elif desc in ('R_386_32', 'R_MIPS_32'):
val = ql.mem.read_ptr(loc, 4)
val = rev_reloc_symbols[symbol_name] + val
ql.mem.write(loc, ql.pack32(val & 0xFFFFFFFF))
ql.mem.write_ptr(loc, (val & 0xFFFFFFFF), 4)
elif desc == 'R_MIPS_HI16':
# actual relocation is done in R_MIPS_LO16
prev_mips_hi16_loc = loc
elif desc == 'R_MIPS_LO16':
val = ql.unpack16(ql.mem.read(prev_mips_hi16_loc + 2, 2)) << 16 | ql.unpack16(ql.mem.read(loc + 2, 2))
val = ql.mem.read_ptr(prev_mips_hi16_loc + 2, 2) << 16 | ql.mem.read_ptr(loc + 2, 2)
val = rev_reloc_symbols[symbol_name] + val
# *(word)(mips_lo16_loc + 2) is treated as signed
if (val & 0xFFFF) >= 0x8000:
val += (1 << 16)
ql.mem.write(prev_mips_hi16_loc + 2, ql.pack16(val >> 16))
ql.mem.write(loc + 2, ql.pack16(val & 0xFFFF))
ql.mem.write_ptr(prev_mips_hi16_loc + 2, (val >> 16), 2)
ql.mem.write_ptr(loc + 2, (val & 0xFFFF), 2)
else:
raise NotImplementedError(f'Relocation type {desc} not implemented')
@@ -629,7 +630,7 @@ class QlLoaderELF(QlLoader):
self.ql.os.entry_point = self.entry_point = entry_point
self.elf_entry = self.ql.os.elf_entry = self.ql.os.entry_point
self.stack_address = QlLoaderELF.align(stack_addr, self.ql.pointersize)
self.stack_address = QlLoaderELF.align(stack_addr, self.ql.arch.pointersize)
self.load_address = loadbase
# remember address of syscall table, so external tools can access to it
@@ -648,20 +649,20 @@ class QlLoaderELF(QlLoader):
if hasattr(SYSCALL_NR, tmp_sc):
syscall_id = getattr(SYSCALL_NR, tmp_sc).value
dest = SYSCALL_MEM + syscall_id * self.ql.pointersize
dest = SYSCALL_MEM + syscall_id * self.ql.arch.pointersize
self.ql.log.debug(f'Writing syscall {tmp_sc} to {dest:#x}')
self.ql.mem.write(dest, self.ql.pack(addr))
self.ql.mem.write_ptr(dest, addr)
# write syscall addresses into syscall table
self.ql.mem.write(SYSCALL_MEM + 0 * self.ql.pointersize, self.ql.pack(self.ql.os.hook_addr + 0 * self.ql.pointersize))
self.ql.mem.write(SYSCALL_MEM + 1 * self.ql.pointersize, self.ql.pack(self.ql.os.hook_addr + 1 * self.ql.pointersize))
self.ql.mem.write(SYSCALL_MEM + 2 * self.ql.pointersize, self.ql.pack(self.ql.os.hook_addr + 2 * self.ql.pointersize))
self.ql.mem.write_ptr(SYSCALL_MEM + 0 * self.ql.arch.pointersize, self.ql.os.hook_addr + 0 * self.ql.arch.pointersize)
self.ql.mem.write_ptr(SYSCALL_MEM + 1 * self.ql.arch.pointersize, self.ql.os.hook_addr + 1 * self.ql.arch.pointersize)
self.ql.mem.write_ptr(SYSCALL_MEM + 2 * self.ql.arch.pointersize, self.ql.os.hook_addr + 2 * self.ql.arch.pointersize)
# setup hooks for read/write/open syscalls
self.import_symbols[self.ql.os.hook_addr + 0 * self.ql.pointersize] = hook_sys_read
self.import_symbols[self.ql.os.hook_addr + 1 * self.ql.pointersize] = hook_sys_write
self.import_symbols[self.ql.os.hook_addr + 2 * self.ql.pointersize] = hook_sys_open
self.import_symbols[self.ql.os.hook_addr + 0 * self.ql.arch.pointersize] = hook_sys_read
self.import_symbols[self.ql.os.hook_addr + 1 * self.ql.arch.pointersize] = hook_sys_write
self.import_symbols[self.ql.os.hook_addr + 2 * self.ql.arch.pointersize] = hook_sys_open
def get_elfdata_mapping(self, elffile: ELFFile) -> bytes:
elfdata_mapping = bytearray()
+5 -5
View File
@@ -27,7 +27,7 @@ from qiling.os.macos.thread import QlMachoThreadManagement, QlMachoThread
# commpage is a shared mem space which is in a static address
def load_commpage(ql):
if ql.archtype == QL_ARCH.X8664:
if ql.arch.type == QL_ARCH.X8664:
COMM_PAGE_START_ADDRESS = X8664_COMM_PAGE_START_ADDRESS
else:
COMM_PAGE_START_ADDRESS = ARM64_COMM_PAGE_START_ADDRESS
@@ -105,7 +105,7 @@ class QlLoaderMACHO(QlLoader):
self.ql.mem.write(self.entry_point, self.ql.code)
self.ql.reg.arch_sp = self.ql.os.entry_point
self.ql.arch.regs.arch_sp = self.ql.os.entry_point
return
self.ql.os.macho_task = MachoTask()
@@ -149,8 +149,8 @@ class QlLoaderMACHO(QlLoader):
else:
self.loadMacho()
self.stack_address = (int(self.stack_sp))
self.ql.reg.arch_sp = self.stack_address # self.stack_sp
self.init_sp = self.ql.reg.arch_sp
self.ql.arch.regs.arch_sp = self.stack_address # self.stack_sp
self.init_sp = self.ql.arch.regs.arch_sp
self.ql.os.macho_task.min_offset = page_align_end(self.vm_end_addr, PAGE_SIZE)
def loadDriver(self, stack_addr, loadbase = -1, argv = [], env = {}):
@@ -420,7 +420,7 @@ class QlLoaderMACHO(QlLoader):
self.load_address = self.macho_entry
# load_commpage not wroking with ARM64, yet
if self.ql.archtype== QL_ARCH.X8664:
if self.ql.arch.type == QL_ARCH.X8664:
load_commpage(self.ql)
return self.proc_entry
+1 -1
View File
@@ -20,7 +20,7 @@ class MachoParser:
self.ql = ql
self.binary_file = self.readFile(path)
self.raw_data = self.binary_file
self.archtype = ql.archtype
self.archtype = ql.arch.type
self.parseFile()
self.page_zero_size = 0
self.header_address = 0x0
+1 -1
View File
@@ -104,7 +104,7 @@ class QlLoaderMCU(QlLoader):
self.ql.arch.init_context()
self.entry_point = self.ql.reg.read('pc')
self.entry_point = self.ql.arch.regs.read('pc')
def load_profile(self):
self.ql.env.update(self.ql.profile)
+54 -58
View File
@@ -210,19 +210,19 @@ class Process():
if name == b"_acmdln":
addr, packed_addr = self._alloc_cmdline(wide=False)
cmdline_entry = {"name": name, "address": address}
memory[address:address + self.ql.pointersize] = packed_addr
memory[address:address + self.ql.arch.pointersize] = packed_addr
self.ql.mem.write(addr, self.cmdline)
elif name == b"_wcmdln":
addr, packed_addr = self._alloc_cmdline(wide=True)
cmdline_entry = {"name": name, "address": address}
memory[address:address + self.ql.pointersize] = packed_addr
memory[address:address + self.ql.arch.pointersize] = packed_addr
encoded = self.cmdline.decode('ascii').encode('UTF-16LE')
self.ql.mem.write(addr, encoded)
return cmdline_entry
def init_tib(self):
if self.ql.archtype == QL_ARCH.X86:
if self.ql.arch.type == QL_ARCH.X86:
teb_addr = self.structure_last_addr
else:
gs = self.structure_last_addr
@@ -243,11 +243,11 @@ class Process():
self.ql.mem.write(teb_addr, teb_data.bytes())
self.structure_last_addr += teb_size
if self.ql.archtype == QL_ARCH.X8664:
if self.ql.arch.type == QL_ARCH.X8664:
# TEB
self.ql.mem.write(gs + 0x30, self.ql.pack64(teb_addr))
self.ql.mem.write_ptr(gs + 0x30, teb_addr)
# PEB
self.ql.mem.write(gs + 0x60, self.ql.pack64(teb_addr + teb_size))
self.ql.mem.write_ptr(gs + 0x60, teb_addr + teb_size)
self.TEB = self.ql.TEB = teb_data
@@ -277,16 +277,16 @@ class Process():
self.ql,
base=ldr_addr,
in_load_order_module_list={
'Flink': ldr_addr + 2 * self.ql.pointersize,
'Blink': ldr_addr + 2 * self.ql.pointersize
'Flink': ldr_addr + 2 * self.ql.arch.pointersize,
'Blink': ldr_addr + 2 * self.ql.arch.pointersize
},
in_memory_order_module_list={
'Flink': ldr_addr + 4 * self.ql.pointersize,
'Blink': ldr_addr + 4 * self.ql.pointersize
'Flink': ldr_addr + 4 * self.ql.arch.pointersize,
'Blink': ldr_addr + 4 * self.ql.arch.pointersize
},
in_initialization_order_module_list={
'Flink': ldr_addr + 6 * self.ql.pointersize,
'Blink': ldr_addr + 6 * self.ql.pointersize
'Flink': ldr_addr + 6 * self.ql.arch.pointersize,
'Blink': ldr_addr + 6 * self.ql.arch.pointersize
}
)
self.ql.mem.write(ldr_addr, ldr_data.bytes())
@@ -316,8 +316,8 @@ class Process():
ldr_table_entry.InInitializationOrderLinks['Flink'] = flink.InInitializationOrderModuleList['Flink']
flink.InLoadOrderModuleList['Flink'] = ldr_table_entry.base
flink.InMemoryOrderModuleList['Flink'] = ldr_table_entry.base + 2 * self.ql.pointersize
flink.InInitializationOrderModuleList['Flink'] = ldr_table_entry.base + 4 * self.ql.pointersize
flink.InMemoryOrderModuleList['Flink'] = ldr_table_entry.base + 2 * self.ql.arch.pointersize
flink.InInitializationOrderModuleList['Flink'] = ldr_table_entry.base + 4 * self.ql.arch.pointersize
else:
flink = self.ldr_list[-1]
@@ -326,8 +326,8 @@ class Process():
ldr_table_entry.InInitializationOrderLinks['Flink'] = flink.InInitializationOrderLinks['Flink']
flink.InLoadOrderLinks['Flink'] = ldr_table_entry.base
flink.InMemoryOrderLinks['Flink'] = ldr_table_entry.base + 2 * self.ql.pointersize
flink.InInitializationOrderLinks['Flink'] = ldr_table_entry.base + 4 * self.ql.pointersize
flink.InMemoryOrderLinks['Flink'] = ldr_table_entry.base + 2 * self.ql.arch.pointersize
flink.InInitializationOrderLinks['Flink'] = ldr_table_entry.base + 4 * self.ql.arch.pointersize
# Blink
blink = self.LDR
@@ -336,8 +336,8 @@ class Process():
ldr_table_entry.InInitializationOrderLinks['Blink'] = blink.InInitializationOrderModuleList['Blink']
blink.InLoadOrderModuleList['Blink'] = ldr_table_entry.base
blink.InMemoryOrderModuleList['Blink'] = ldr_table_entry.base + 2 * self.ql.pointersize
blink.InInitializationOrderModuleList['Blink'] = ldr_table_entry.base + 4 * self.ql.pointersize
blink.InMemoryOrderModuleList['Blink'] = ldr_table_entry.base + 2 * self.ql.arch.pointersize
blink.InInitializationOrderModuleList['Blink'] = ldr_table_entry.base + 4 * self.ql.arch.pointersize
self.ql.mem.write(flink.base, flink.bytes())
self.ql.mem.write(blink.base, blink.bytes())
@@ -370,9 +370,9 @@ class Process():
driver_object_addr = self.structure_last_addr
self.ql.log.info("Driver object addr is 0x%x" %driver_object_addr)
if self.ql.archtype == QL_ARCH.X86:
if self.ql.arch.type == QL_ARCH.X86:
self.driver_object = DRIVER_OBJECT32(self.ql, driver_object_addr)
elif self.ql.archtype == QL_ARCH.X8664:
elif self.ql.arch.type == QL_ARCH.X8664:
self.driver_object = DRIVER_OBJECT64(self.ql, driver_object_addr)
driver_object_size = ctypes.sizeof(self.driver_object)
@@ -386,9 +386,9 @@ class Process():
regitry_path_addr = self.structure_last_addr
self.ql.log.info("Registry path addr is 0x%x" %regitry_path_addr)
if self.ql.archtype == QL_ARCH.X86:
if self.ql.arch.type == QL_ARCH.X86:
regitry_path_data = UNICODE_STRING32(0, 0, regitry_path_addr)
elif self.ql.archtype == QL_ARCH.X8664:
elif self.ql.arch.type == QL_ARCH.X8664:
regitry_path_data = UNICODE_STRING64(0, 0, regitry_path_addr)
regitry_path_size = ctypes.sizeof(regitry_path_data)
@@ -402,9 +402,9 @@ class Process():
self.ql.log.info("EPROCESS is is 0x%x" %addr)
if self.ql.archtype == QL_ARCH.X86:
if self.ql.arch.type == QL_ARCH.X86:
self.eprocess_object = EPROCESS32(self.ql, addr)
elif self.ql.archtype == QL_ARCH.X8664:
elif self.ql.arch.type == QL_ARCH.X8664:
self.eprocess_object = EPROCESS64(self.ql, addr)
size = ctypes.sizeof(self.eprocess_object)
@@ -420,9 +420,9 @@ class Process():
struct information:
https://doxygen.reactos.org/d8/dae/modules_2rostests_2winetests_2ntdll_2time_8c_source.html
'''
if self.ql.archtype == QL_ARCH.X86:
if self.ql.arch.type == QL_ARCH.X86:
KI_USER_SHARED_DATA = 0xFFDF0000
elif self.ql.archtype == QL_ARCH.X8664:
elif self.ql.arch.type == QL_ARCH.X8664:
KI_USER_SHARED_DATA = 0xFFFFF78000000000
self.ql.log.info("KI_USER_SHARED_DATA is 0x%x" %KI_USER_SHARED_DATA)
@@ -435,17 +435,13 @@ class Process():
class QlLoaderPE(QlLoader, Process):
def __init__(self, ql: Qiling):
def __init__(self, ql: Qiling, libcache: bool):
super().__init__(ql)
self.ql = ql
self.path = self.ql.path
self.is_driver = False
if ql.libcache is True:
self.libcache = QlPeCache()
else:
self.libcache = ql.libcache or None
self.libcache = QlPeCache() if libcache else None
def run(self):
self.init_dlls = [b"ntdll.dll", b"kernel32.dll", b"user32.dll"]
@@ -460,7 +456,7 @@ class QlLoaderPE(QlLoader, Process):
self.init_dlls.append(b"ntoskrnl.exe")
self.sys_dlls.append(b"ntoskrnl.exe")
if self.ql.archtype == QL_ARCH.X86:
if self.ql.arch.type == QL_ARCH.X86:
self.stack_address = int(self.ql.os.profile.get("OS32", "stack_address"), 16)
self.stack_size = int(self.ql.os.profile.get("OS32", "stack_size"), 16)
self.image_address = int(self.ql.os.profile.get("OS32", "image_address"), 16)
@@ -469,7 +465,7 @@ class QlLoaderPE(QlLoader, Process):
self.ql.os.heap_base_address = int(self.ql.os.profile.get("OS32", "heap_address"), 16)
self.ql.os.heap_base_size = int(self.ql.os.profile.get("OS32", "heap_size"), 16)
self.structure_last_addr = FS_SEGMENT_ADDR
elif self.ql.archtype == QL_ARCH.X8664:
elif self.ql.arch.type == QL_ARCH.X8664:
self.stack_address = int(self.ql.os.profile.get("OS64", "stack_address"), 16)
self.stack_size = int(self.ql.os.profile.get("OS64", "stack_size"), 16)
self.image_address = int(self.ql.os.profile.get("OS64", "image_address"), 16)
@@ -535,9 +531,9 @@ class QlLoaderPE(QlLoader, Process):
# Stack should not init at the very bottom. Will cause errors with Dlls
sp = self.stack_address + self.stack_size - 0x1000
if self.ql.archtype == QL_ARCH.X86:
self.ql.reg.esp = sp
self.ql.reg.ebp = sp
if self.ql.arch.type == QL_ARCH.X86:
self.ql.arch.regs.esp = sp
self.ql.arch.regs.ebp = sp
if self.pe.is_dll():
self.ql.log.debug('Setting up DllMain args')
@@ -549,18 +545,18 @@ class QlLoaderPE(QlLoader, Process):
self.ql.log.debug('Writing 0x01 (DLL_PROCESS_ATTACH) to [ESP+8](0x%08X)' % (sp + 0x8))
self.ql.mem.write(sp + 0x8, int(1).to_bytes(length=4, byteorder='little'))
elif self.ql.archtype == QL_ARCH.X8664:
self.ql.reg.rsp = sp
self.ql.reg.rbp = sp
elif self.ql.arch.type == QL_ARCH.X8664:
self.ql.arch.regs.rsp = sp
self.ql.arch.regs.rbp = sp
if self.pe.is_dll():
self.ql.log.debug('Setting up DllMain args')
self.ql.log.debug('Setting RCX (arg1) to %16X (IMAGE_BASE)' % (self.pe_image_address))
self.ql.reg.rcx = self.pe_image_address
self.ql.arch.regs.rcx = self.pe_image_address
self.ql.log.debug('Setting RDX (arg2) to 1 (DLL_PROCESS_ATTACH)')
self.ql.reg.rdx = 1
self.ql.arch.regs.rdx = 1
else:
raise QlErrorArch("Unknown ql.arch")
@@ -574,24 +570,24 @@ class QlLoaderPE(QlLoader, Process):
super().init_ki_user_shared_data()
# setup IRQ Level in CR8 to PASSIVE_LEVEL (0)
self.ql.reg.write(UC_X86_REG_CR8, 0)
self.ql.arch.regs.write(UC_X86_REG_CR8, 0)
# setup CR4, some drivers may check this at initialized time
self.ql.reg.write(UC_X86_REG_CR4, 0x6f8)
self.ql.arch.regs.write(UC_X86_REG_CR4, 0x6f8)
self.ql.log.debug('Setting up DriverEntry args')
self.ql.stop_execution_pattern = 0xDEADC0DE
if self.ql.archtype == QL_ARCH.X86: # Win32
if not self.ql.stop_options.any:
if self.ql.arch.type == QL_ARCH.X86: # Win32
if not self.ql.stop_options:
# We know that a driver will return,
# so if the user did not configure stop options, write a sentinel return value
self.ql.mem.write(sp, self.ql.stop_execution_pattern.to_bytes(length=4, byteorder='little'))
self.ql.log.debug('Writing 0x%08X (PDRIVER_OBJECT) to [ESP+4](0x%08X)' % (self.ql.loader.driver_object_address, sp+0x4))
self.ql.log.debug('Writing 0x%08X (RegistryPath) to [ESP+8](0x%08X)' % (self.ql.loader.regitry_path_address, sp+0x8))
elif self.ql.archtype == QL_ARCH.X8664: # Win64
if not self.ql.stop_options.any:
elif self.ql.arch.type == QL_ARCH.X8664: # Win64
if not self.ql.stop_options:
# We know that a driver will return,
# so if the user did not configure stop options, write a sentinel return value
self.ql.mem.write(sp, self.ql.stop_execution_pattern.to_bytes(length=8, byteorder='little'))
@@ -613,9 +609,9 @@ class QlLoaderPE(QlLoader, Process):
# setup IMAGE_LOAD_CONFIG_DIRECTORY
if self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG']].VirtualAddress != 0:
SecurityCookie_rva = self.pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct.SecurityCookie - self.pe.OPTIONAL_HEADER.ImageBase
SecurityCookie_value = default_security_cookie_value = self.ql.mem.read(self.pe_image_address+SecurityCookie_rva, self.ql.pointersize)
SecurityCookie_value = default_security_cookie_value = self.ql.mem.read(self.pe_image_address+SecurityCookie_rva, self.ql.arch.pointersize)
while SecurityCookie_value == default_security_cookie_value:
SecurityCookie_value = secrets.token_bytes(self.ql.pointersize)
SecurityCookie_value = secrets.token_bytes(self.ql.arch.pointersize)
# rol rcx, 10h (rcx: cookie)
# test cx, 0FFFFh
SecurityCookie_value_array = bytearray(SecurityCookie_value)
@@ -653,7 +649,7 @@ class QlLoaderPE(QlLoader, Process):
else:
addr = self.import_address_table[dll_name][imp.ordinal]
if self.ql.archtype == QL_ARCH.X86:
if self.ql.arch.type == QL_ARCH.X86:
address = self.ql.pack32(addr)
else:
address = self.ql.pack64(addr)
@@ -665,12 +661,12 @@ class QlLoaderPE(QlLoader, Process):
elif self.ql.code:
self.filepath = b""
if self.ql.archtype == QL_ARCH.X86:
self.ql.reg.esp = self.stack_address + 0x3000
self.ql.reg.ebp = self.ql.reg.esp
elif self.ql.archtype == QL_ARCH.X8664:
self.ql.reg.rsp = self.stack_address + 0x3000
self.ql.reg.rbp = self.ql.reg.rsp
if self.ql.arch.type == QL_ARCH.X86:
self.ql.arch.regs.esp = self.stack_address + 0x3000
self.ql.arch.regs.ebp = self.ql.arch.regs.esp
elif self.ql.arch.type == QL_ARCH.X8664:
self.ql.arch.regs.rsp = self.stack_address + 0x3000
self.ql.arch.regs.rbp = self.ql.arch.regs.rsp
# load shellcode in
self.ql.mem.map(self.entry_point, self.ql.os.code_ram_size, info="[shellcode_base]")
@@ -687,4 +683,4 @@ class QlLoaderPE(QlLoader, Process):
# move entry_point to ql.os
self.ql.os.entry_point = self.entry_point
self.init_sp = self.ql.reg.arch_sp
self.init_sp = self.ql.arch.regs.arch_sp
+4 -4
View File
@@ -192,8 +192,8 @@ class QlLoaderPE_UEFI(QlLoader):
self.ql.os.heap = context.heap
# set stack and frame pointers
self.ql.reg.rsp = context.top_of_stack
self.ql.reg.rbp = context.top_of_stack
self.ql.arch.regs.rsp = context.top_of_stack
self.ql.arch.regs.rbp = context.top_of_stack
self.ql.os.fcall.call_native(entry_point, (
(POINTER, ImageHandle),
@@ -321,11 +321,11 @@ class QlLoaderPE_UEFI(QlLoader):
ql = self.ql
# intel architecture uefi implementation only
if ql.archtype not in (QL_ARCH.X86, QL_ARCH.X8664):
if ql.arch.type not in (QL_ARCH.X86, QL_ARCH.X8664):
raise QlErrorArch("Unsupported architecture")
# x86-64 arch only
if ql.archtype != QL_ARCH.X8664:
if ql.arch.type != QL_ARCH.X8664:
raise QlErrorArch("Only 64-bit modules are supported at the moment")
self.loaded_image_protocol_guid = ql.os.profile["LOADED_IMAGE_PROTOCOL"]["Guid"]
+1 -1
View File
@@ -28,7 +28,7 @@ class QlOsBlob(QlOs):
QL_ARCH.ARM : arm.aarch32,
QL_ARCH.ARM64 : arm.aarch64,
QL_ARCH.MIPS : mips.mipso32
}[ql.archtype](ql)
}[ql.arch.type](ql.arch)
self.fcall = QlFunctionCall(ql, cc)
+3 -3
View File
@@ -56,10 +56,10 @@ class QlOsDos(QlOs):
curses.endwin()
def set_flag_value(self, fl: Flags, val: int) -> None:
self.ql.reg.ef = self.ql.reg.ef & (~fl) | (fl * val)
self.ql.arch.regs.ef = self.ql.arch.regs.ef & (~fl) | (fl * val)
def test_flags(self, fl):
return self.ql.reg.ef & fl == fl
return self.ql.arch.regs.ef & fl == fl
def set_cf(self):
self.set_flag_value(Flags.CF, 0b1)
@@ -76,7 +76,7 @@ class QlOsDos(QlOs):
def hook_syscall(self):
def cb(ql: Qiling, intno: int):
ah = ql.reg.ah
ah = ql.arch.regs.ah
intinfo = (intno, ah)
func = self.user_defined_api[QL_INTERCEPT.CALL].get(intinfo) or handlers.get(intno)
+16 -16
View File
@@ -63,7 +63,7 @@ def __leaf_00(ql: Qiling):
except:
pass
al = ql.reg.al
al = ql.arch.regs.al
resolution = {
0x00 : (25, 40),
@@ -128,31 +128,31 @@ def __leaf_00(ql: Qiling):
def __leaf_01(ql: Qiling):
# limited support
ch = ql.reg.ch
ch = ql.arch.regs.ch
if (ch & 0x20):
curses.curs_set(0)
def __leaf_02(ql: Qiling):
# page number ignored
dh = ql.reg.dh # row
dl = ql.reg.dl # column
dh = ql.arch.regs.dh # row
dl = ql.arch.regs.dl # column
ql.os.stdscr.move(dh, dl)
def __leaf_05(ql: Qiling):
# No idea how to implement, do nothing here.
ql.reg.al = 0
ql.arch.regs.al = 0
def __leaf_06(ql: Qiling):
stdscr = ql.os.stdscr
al = ql.reg.al # lines to scroll
ch = ql.reg.ch # row of upper-left cornner
cl = ql.reg.cl # column of upper-left corner
dh = ql.reg.dh # row of lower right corner
dl = ql.reg.dl # column of lower righ corner
bh = ql.reg.bh # color
al = ql.arch.regs.al # lines to scroll
ch = ql.arch.regs.ch # row of upper-left cornner
cl = ql.arch.regs.cl # column of upper-left corner
dh = ql.arch.regs.dh # row of lower right corner
dl = ql.arch.regs.dl # column of lower righ corner
bh = ql.arch.regs.bh # color
y, x = stdscr.getmaxyx()
cy, cx = stdscr.getyx()
@@ -193,13 +193,13 @@ def __leaf_08(ql: Qiling):
stdscr = ql.os.stdscr
if stdscr is None:
ql.reg.ax = 0x0720
ql.arch.regs.ax = 0x0720
else:
cy, cx = stdscr.getyx()
inch = stdscr.inch(cy, cx)
attr = inch & curses.A_COLOR
ch = inch & 0xFF
ql.reg.al = ch
ql.arch.regs.al = ch
pair_number = curses.pair_number(attr)
fg, bg = curses.pair_content(pair_number)
@@ -209,10 +209,10 @@ def __leaf_08(ql: Qiling):
if attr & curses.A_BLINK:
orig_bg |= 0b1000
ql.reg.ah = ((orig_bg << 4) & orig_fg)
ql.arch.regs.ah = ((orig_bg << 4) & orig_fg)
def __leaf_0e(ql: Qiling):
al = ql.reg.al
al = ql.arch.regs.al
ql.log.debug(f'echo: {al:02x} -> {curses.ascii.unctrl(al)}')
@@ -247,7 +247,7 @@ def __leaf_0e(ql: Qiling):
# https://stanislavs.org/helppc/idx_interrupt.html
# implemented by curses
def handler(ql: Qiling):
ah = ql.reg.ah
ah = ql.arch.regs.ah
leaffunc = {
0x00 : __leaf_00,
+30 -30
View File
@@ -45,43 +45,43 @@ def __leaf_00(ql: Qiling):
ql.os.clear_cf()
def __leaf_02(ql: Qiling):
idx = ql.reg.dl
idx = ql.arch.regs.dl
if not ql.os.fs_mapper.has_mapping(idx):
ql.log.warning(f'Warning: No such disk: {idx:#x}')
ql.reg.ah = DiskError.BadCommand.value
ql.arch.regs.ah = DiskError.BadCommand.value
ql.os.set_cf()
return
cylinder = ((ql.reg.cx & 0xff00) >> 8) | ((ql.reg.cx & 0xC0) << 2)
head = ql.reg.dh
sector = ql.reg.cx & 63
cnt = ql.reg.al
cylinder = ((ql.arch.regs.cx & 0xff00) >> 8) | ((ql.arch.regs.cx & 0xC0) << 2)
head = ql.arch.regs.dh
sector = ql.arch.regs.cx & 63
cnt = ql.arch.regs.al
disk = ql.os.fs_mapper.open(idx, None)
content = disk.read_chs(cylinder, head, sector, cnt)
ql.mem.write(utils.linaddr(ql.reg.es, ql.reg.bx), content)
ql.mem.write(utils.linaddr(ql.arch.regs.es, ql.arch.regs.bx), content)
ql.os.clear_cf()
ql.reg.ah = 0
ql.reg.al = sector
ql.arch.regs.ah = 0
ql.arch.regs.al = sector
# @see: https://stanislavs.org/helppc/int_13-8.html
def __leaf_08(ql: Qiling):
idx = ql.reg.dl
idx = ql.arch.regs.dl
if not ql.os.fs_mapper.has_mapping(idx):
ql.log.warning(f'Warning: No such disk: {idx:#x}')
ql.reg.ah = DiskError.BadCommand.value
ql.arch.regs.ah = DiskError.BadCommand.value
ql.os.set_cf()
return
disk = ql.os.fs_mapper.open(idx, None)
ql.reg.dl = ql.os.fs_mapper.mapping_count()
ql.reg.dh = disk.n_heads - 1
ql.reg.bl = 0x4
ql.reg.di = 0
ql.reg.ds = 0
ql.arch.regs.dl = ql.os.fs_mapper.mapping_count()
ql.arch.regs.dh = disk.n_heads - 1
ql.arch.regs.bl = 0x4
ql.arch.regs.di = 0
ql.arch.regs.ds = 0
n_sectors = min(disk.n_sectors, 63)
n_cylinders = min(disk.n_cylinders, 1023)
@@ -90,28 +90,28 @@ def __leaf_08(ql: Qiling):
cx |= ((n_cylinders & 0b11) << 6)
cx |= (((n_cylinders & 0b1111111100) >> 2) << 8)
ql.reg.cx = cx
ql.reg.ah = 0
ql.arch.regs.cx = cx
ql.arch.regs.ah = 0
ql.os.clear_cf()
def __leaf_41(ql: Qiling):
ql.reg.ah = 0
ql.arch.regs.ah = 0
# 1 -> Device Access using the packet structure.
# 2 -> Drive locking and ejecting.
# 4 -> Enhanced Disk Drive Support.
ql.reg.bx = 0xaa55
ql.reg.cx = 7
ql.arch.regs.bx = 0xaa55
ql.arch.regs.cx = 7
def __leaf_42(ql: Qiling):
idx = ql.reg.dl
idx = ql.arch.regs.dl
if not ql.os.fs_mapper.has_mapping(idx):
ql.log.warning(f'Warning: No such disk: {idx:#x}')
ql.reg.ah = DiskError.BadCommand.value
ql.arch.regs.ah = DiskError.BadCommand.value
ql.os.set_cf()
return
dapbs = ql.mem.read(utils.linaddr(ql.reg.ds, ql.reg.si), 16)
dapbs = ql.mem.read(utils.linaddr(ql.arch.regs.ds, ql.arch.regs.si), 16)
_, _, cnt, offset, segment, lba = parse_dap(dapbs)
ql.log.info(f'Reading {cnt} sectors from disk {idx:#x} with LBA {lba}')
@@ -120,18 +120,18 @@ def __leaf_42(ql: Qiling):
ql.mem.write(utils.linaddr(segment, offset), content)
ql.os.clear_cf()
ql.reg.ah = 0
ql.arch.regs.ah = 0
def __leaf_43(ql: Qiling):
idx = ql.reg.dl
idx = ql.arch.regs.dl
if not ql.os.fs_mapper.has_mapping(idx):
ql.log.info(f"Warning: No such disk: {hex(idx)}")
ql.reg.ah = DiskError.BadCommand.value
ql.arch.regs.ah = DiskError.BadCommand.value
ql.os.set_cf()
return
dapbs = ql.mem.read(utils.linaddr(ql.reg.ds, ql.reg.si), 16)
dapbs = ql.mem.read(utils.linaddr(ql.arch.regs.ds, ql.arch.regs.si), 16)
_, _, cnt, offset, segment, lba = parse_dap(dapbs)
ql.log.info(f'Writing {cnt} sectors to disk {idx:#x} with LBA {lba}')
@@ -140,11 +140,11 @@ def __leaf_43(ql: Qiling):
disk.write_sectors(lba, cnt, buffer)
ql.os.clear_cf()
ql.reg.ah = 0
ql.arch.regs.ah = 0
# @see: https://en.wikipedia.org/wiki/INT_13H
def handler(ql: Qiling):
ah = ql.reg.ah
ah = ql.arch.regs.ah
leaffunc = {
0x00 : __leaf_00,
+7 -7
View File
@@ -16,23 +16,23 @@ def __leaf_01(ql: Qiling):
pass
def __leaf_53(ql: Qiling):
al = ql.reg.al
al = ql.arch.regs.al
if al == 0x01:
ql.os.clear_cf()
elif al == 0x0e:
ql.reg.ax = 0x0102
ql.arch.regs.ax = 0x0102
ql.os.clear_cf()
elif al == 0x07:
if (ql.reg.bx == 1) and (ql.reg.cx == 3):
if (ql.arch.regs.bx == 1) and (ql.arch.regs.cx == 3):
ql.log.info("Emulation Stop")
ql.emu_stop()
else:
raise NotImplementedError()
def __leaf_86(ql: Qiling):
dx = ql.reg.dx
cx = ql.reg.cx
dx = ql.arch.regs.dx
cx = ql.arch.regs.cx
full_secs = ((cx << 16) + dx) / 1000000
ql.log.info(f"Goint to sleep for {full_secs} seconds")
@@ -41,10 +41,10 @@ def __leaf_86(ql: Qiling):
# Note: Since we are in a single thread environment, we assume
# that no one will wait at the same time.
ql.os.clear_cf()
ql.reg.ah = 0x80
ql.arch.regs.ah = 0x80
def handler(ql: Qiling):
ah = ql.reg.ah
ah = ql.arch.regs.ah
leaffunc = {
0x00 : __leaf_00,
+7 -7
View File
@@ -136,10 +136,10 @@ def __leaf_00(ql: Qiling):
key = parse_key(ql.os.stdscr.getch())
ql.log.debug(f"Get key: {hex(key)}")
if curses.ascii.isascii(key):
ql.reg.al = key
ql.arch.regs.al = key
else:
ql.reg.al = 0
ql.reg.ah = get_scan_code(key)
ql.arch.regs.al = 0
ql.arch.regs.ah = get_scan_code(key)
curses.nl()
def __leaf_01(ql: Qiling):
@@ -150,11 +150,11 @@ def __leaf_01(ql: Qiling):
if key == -1:
ql.os.set_zf()
ql.reg.ax = 0
ql.arch.regs.ax = 0
else:
ql.log.debug(f"Has key: {hex(key)} ({curses.ascii.unctrl(key)})")
ql.reg.al = key
ql.reg.ah = get_scan_code(key)
ql.arch.regs.al = key
ql.arch.regs.ah = get_scan_code(key)
ql.os.clear_zf()
# Buffer shouldn't be removed in this interrupt.
curses.ungetch(key)
@@ -163,7 +163,7 @@ def __leaf_01(ql: Qiling):
curses.nl()
def handler(ql: Qiling):
ah = ql.reg.ah
ah = ql.arch.regs.ah
leaffunc = {
0x00 : __leaf_00,
+3 -3
View File
@@ -7,7 +7,7 @@ from qiling import Qiling
def handler(ql: Qiling):
# Note: Memory is not cleaned.
dl = ql.reg.dl
dl = ql.arch.regs.dl
if ql.os.fs_mapper.has_mapping(dl):
disk = ql.os.fs_mapper.open(dl, None)
@@ -19,5 +19,5 @@ def handler(ql: Qiling):
ql.mem.write(0x7C00, mbr)
ql.reg.cs = 0x07C0
ql.reg.ip = 0x0000
ql.arch.regs.cs = 0x07C0
ql.arch.regs.ip = 0x0000
+13 -13
View File
@@ -13,13 +13,13 @@ def __set_elapsed_ticks(ql: Qiling):
now = datetime.now()
ticks = int((now - ql.os.start_time).total_seconds() * ql.os.ticks_per_second)
ql.reg.cx = (ticks >> 16) & 0xffff
ql.reg.dx = (ticks >> 0) & 0xffff
ql.arch.regs.cx = (ticks >> 16) & 0xffff
ql.arch.regs.dx = (ticks >> 0) & 0xffff
def __leaf_00(ql: Qiling):
__set_elapsed_ticks(ql)
ql.reg.al = 0
ql.arch.regs.al = 0
def __leaf_01(ql: Qiling):
__set_elapsed_ticks(ql)
@@ -27,10 +27,10 @@ def __leaf_01(ql: Qiling):
def __leaf_02_03(ql: Qiling):
now = datetime.now()
ql.reg.ch = utils.BIN2BCD(now.hour)
ql.reg.cl = utils.BIN2BCD(now.minute)
ql.reg.dh = utils.BIN2BCD(now.second)
ql.reg.dl = 0
ql.arch.regs.ch = utils.BIN2BCD(now.hour)
ql.arch.regs.cl = utils.BIN2BCD(now.minute)
ql.arch.regs.dh = utils.BIN2BCD(now.second)
ql.arch.regs.dl = 0
ql.os.clear_cf()
@@ -38,10 +38,10 @@ def __leaf_04_05(ql: Qiling):
now = datetime.now()
# See https://sites.google.com/site/liangweiqiang/Home/e5006/e5006classnote/jumptiming/int1ahclockservice
ql.reg.ch = utils.BIN2BCD((now.year - 1) // 100)
ql.reg.cl = utils.BIN2BCD(now.year % 100)
ql.reg.dh = utils.BIN2BCD(now.month)
ql.reg.dl = utils.BIN2BCD(now.day)
ql.arch.regs.ch = utils.BIN2BCD((now.year - 1) // 100)
ql.arch.regs.cl = utils.BIN2BCD(now.year % 100)
ql.arch.regs.dh = utils.BIN2BCD(now.month)
ql.arch.regs.dl = utils.BIN2BCD(now.day)
ql.os.clear_cf()
@@ -55,13 +55,13 @@ def __leaf_08(ql: Qiling):
def __leaf_0a(ql: Qiling):
now = datetime.now()
ql.reg.cx = (now - datetime(1980, 1, 1)).days
ql.arch.regs.cx = (now - datetime(1980, 1, 1)).days
def __leaf_0b(ql: Qiling):
pass
def handler(ql: Qiling):
ah = ql.reg.ah
ah = ql.arch.regs.ah
leaffunc = {
0x00 : __leaf_00,
+1 -1
View File
@@ -9,7 +9,7 @@ def __leaf_13(self):
pass
def handler(ql: Qiling):
ah = ql.reg.ah
ah = ql.arch.regs.ah
leaffunc = {
0x13 : __leaf_13
+20 -20
View File
@@ -16,8 +16,8 @@ def __leaf_4c(ql: Qiling):
# write a character to screen
def __leaf_02(ql: Qiling):
ch = ql.reg.dl
ql.reg.al = ch
ch = ql.arch.regs.dl
ql.arch.regs.al = ch
print(f'{ch:c}', end='')
@@ -39,7 +39,7 @@ def __leaf_26(ql: Qiling):
# get dos version
def __leaf_30(ql: Qiling):
ql.reg.ax = ql.os.dos_ver
ql.arch.regs.ax = ql.os.dos_ver
# get or set ctrl-break
def __leaf_33(ql: Qiling):
@@ -56,7 +56,7 @@ def __leaf_3c(ql: Qiling):
fpath = ql.os.path.transform_to_real_path(fname)
ql.os.handles[ql.os.handle_next] = open(fpath, "wb")
ql.reg.ax = ql.os.handle_next
ql.arch.regs.ax = ql.os.handle_next
ql.os.handle_next += 1
ql.os.clear_cf()
@@ -66,13 +66,13 @@ def __leaf_3d(ql: Qiling):
fpath = ql.os.path.transform_to_real_path(fname)
ql.os.handles[ql.os.handle_next] = open(fpath, "rb")
ql.reg.ax = ql.os.handle_next
ql.arch.regs.ax = ql.os.handle_next
ql.os.handle_next += 1
ql.os.clear_cf()
# close file
def __leaf_3e(ql: Qiling):
hd = ql.reg.bx
hd = ql.arch.regs.bx
if hd in ql.os.handles:
f = ql.os.handles.pop(hd)
@@ -80,39 +80,39 @@ def __leaf_3e(ql: Qiling):
ql.os.clear_cf()
else:
ql.reg.ax = 0x06
ql.arch.regs.ax = 0x06
ql.os.set_cf()
# read from file
def __leaf_3f(ql: Qiling):
hd = ql.reg.bx
hd = ql.arch.regs.bx
if hd in ql.os.handles:
f = ql.os.handles[hd]
buffer = utils.linaddr(ql.reg.ds, ql.reg.dx)
sz = ql.reg.cx
buffer = utils.linaddr(ql.arch.regs.ds, ql.arch.regs.dx)
sz = ql.arch.regs.cx
rd = f.read(sz)
ql.mem.write(buffer, rd)
ql.os.clear_cf()
ql.reg.ax = len(rd)
ql.arch.regs.ax = len(rd)
else:
ql.reg.ax = 0x06
ql.arch.regs.ax = 0x06
ql.os.set_cf()
# write to file
def __leaf_40(ql: Qiling):
hd = ql.reg.bx
hd = ql.arch.regs.bx
if hd in ql.os.handles:
f = ql.os.handles[hd]
buffer = utils.linaddr(ql.reg.ds, ql.reg.dx)
sz = ql.reg.cx
buffer = utils.linaddr(ql.arch.regs.ds, ql.arch.regs.dx)
sz = ql.arch.regs.cx
rd = ql.mem.read(buffer, sz)
f.write(bytes(rd))
ql.os.clear_cf()
ql.reg.ax = len(rd)
ql.arch.regs.ax = len(rd)
else:
ql.reg.ax = 0x06
ql.arch.regs.ax = 0x06
ql.os.set_cf()
# delete file
@@ -124,15 +124,15 @@ def __leaf_41(ql: Qiling):
os.remove(fpath)
ql.os.clear_cf()
except OSError:
ql.reg.ax = 0x05
ql.arch.regs.ax = 0x05
ql.os.set_cf()
def __leaf_43(ql: Qiling):
ql.reg.cx = 0xffff
ql.arch.regs.cx = 0xffff
ql.os.clear_cf()
def handler(ql: Qiling):
ah = ql.reg.ah
ah = ql.arch.regs.ah
leaffunc = {
0x02 : __leaf_02,
+1 -1
View File
@@ -53,6 +53,6 @@ def read_dos_string(ql: Qiling, address: int):
return ba.decode('ascii')
def read_dos_string_from_ds_dx(ql: Qiling):
address = linaddr(ql.reg.ds, ql.reg.dx)
address = linaddr(ql.arch.regs.ds, ql.arch.regs.dx)
return read_dos_string(ql, address)
+5 -5
View File
@@ -131,7 +131,7 @@ class QlFunctionCall:
"""
ql = self.ql
pc = ql.reg.arch_pc
pc = ql.arch.regs.arch_pc
# if set, fire up the on-enter hook and let it override original args set
if hook_onenter:
@@ -187,11 +187,11 @@ class QlFunctionCall:
nslots = self.__count_slots(atype for atype, _ in args)
self.cc.reserve(nslots)
# set arguments values
self.writeParams(args)
if ret is not None:
self.cc.setReturnAddress(ret)
# set arguments values
self.writeParams(args)
# call
self.ql.reg.arch_pc = addr
self.ql.arch.regs.arch_pc = addr
+8 -5
View File
@@ -4,9 +4,9 @@
#
from unicorn import UcError
from unicorn.x86_const import UC_X86_INS_SYSCALL
from qiling.arch.x86 import GDTManager, ql_x86_register_cs, ql_x86_register_ds_ss_es
from qiling.arch.x86_const import UC_X86_INS_SYSCALL
from qiling.arch.x86_utils import GDTManager, SegmentManager86
from qiling.os.posix.posix import QlOsPosix
class QlOsFreebsd(QlOsPosix):
@@ -18,10 +18,13 @@ class QlOsFreebsd(QlOsPosix):
def load(self):
gdtm = GDTManager(self.ql)
# setup gdt and segments selectors
segm = SegmentManager86(self.ql.arch, gdtm)
segm.setup_cs_ds_ss_es(0, 4 << 30)
self.ql.hook_insn(self.hook_syscall, UC_X86_INS_SYSCALL)
self.gdtm = GDTManager(self.ql)
ql_x86_register_cs(self)
ql_x86_register_ds_ss_es(self)
def hook_syscall(self, intno= None):
+1 -1
View File
@@ -7,7 +7,7 @@ from qiling.const import QL_ARCH
from qiling.os.posix.posix import SYSCALL_PREF
def map_syscall(ql, syscall_num):
if ql.archtype == QL_ARCH.X8664:
if ql.arch.type == QL_ARCH.X8664:
return f'{SYSCALL_PREF}{x8664_syscall_table[syscall_num]}'
x8664_syscall_table = {
+4 -9
View File
@@ -40,19 +40,14 @@ def ql_syscall_sysarch(ql, op, parms, *args, **kw):
wild guess, of cause not working
"""
regreturn = 0
ql.GS_SEGMENT_ADDR = 0x6000
ql.GS_SEGMENT_SIZE = 0x8000
#ql.mem.map(ql.GS_SEGMENT_ADDR, ql.GS_SEGMENT_SIZE)
#ql.reg.msr(GSMSR, ql.GS_SEGMENT_ADDR)
ql.reg.msr(FSMSR, parms)
#ql.mem.map(GS_SEGMENT_ADDR, GS_SEGMENT_SIZE)
#ql.arch.msr.write(IA32_GS_BASE_MSR, GS_SEGMENT_ADDR)
ql.arch.msr.write(IA32_FS_BASE_MSR, parms)
#op_buf = ql.pack32(op)
#ql.mem.write(parms, op_buf)
return regreturn
return 0
def ql_syscall_sigprocmask(ql, how, mask, omask, *args, **kw):
ql.log.debug("sigprocmask(how: 0x%x, mask: 0x%x, omask: 0x%x)" % (how, mask, omask))
+91 -91
View File
@@ -68,78 +68,78 @@ class HookFunc:
def get_ret_pc(self):
# ARM
if self.ql.archtype== QL_ARCH.ARM:
return self.ql.reg.lr
if self.ql.arch.type == QL_ARCH.ARM:
return self.ql.arch.regs.lr
# MIPS32
elif self.ql.archtype== QL_ARCH.MIPS:
return self.ql.reg.ra
elif self.ql.arch.type == QL_ARCH.MIPS:
return self.ql.arch.regs.ra
# ARM64
elif self.ql.archtype== QL_ARCH.ARM64:
return self.ql.reg.x30
elif self.ql.arch.type == QL_ARCH.ARM64:
return self.ql.arch.regs.x30
# X86
elif self.ql.archtype== QL_ARCH.X86:
return self.ql.unpack(self.ql.mem.read(self.ql.reg.esp, self.ql.pointersize))
elif self.ql.arch.type == QL_ARCH.X86:
return self.ql.unpack(self.ql.mem.read(self.ql.arch.regs.esp, self.ql.arch.pointersize))
# X8664
elif self.ql.archtype== QL_ARCH.X8664:
return self.ql.unpack(self.ql.mem.read(self.ql.reg.rsp, self.ql.pointersize))
elif self.ql.arch.type == QL_ARCH.X8664:
return self.ql.unpack(self.ql.mem.read(self.ql.arch.regs.rsp, self.ql.arch.pointersize))
else:
raise
def context_fixup(self):
# ARM
if self.ql.archtype== QL_ARCH.ARM:
if self.ql.arch.type == QL_ARCH.ARM:
pass
# MIPS32
elif self.ql.archtype== QL_ARCH.MIPS:
elif self.ql.arch.type == QL_ARCH.MIPS:
pass
# ARM64
elif self.ql.archtype== QL_ARCH.ARM64:
elif self.ql.arch.type == QL_ARCH.ARM64:
pass
# X86
elif self.ql.archtype== QL_ARCH.X86:
self.ql.reg.esp = self.ql.reg.esp + self.ql.pointersize
elif self.ql.arch.type == QL_ARCH.X86:
self.ql.arch.regs.esp = self.ql.arch.regs.esp + self.ql.arch.pointersize
# X8664
elif self.ql.archtype== QL_ARCH.X8664:
self.ql.reg.rsp = self.ql.reg.rsp + self.ql.pointersize
elif self.ql.arch.type == QL_ARCH.X8664:
self.ql.arch.regs.rsp = self.ql.arch.regs.rsp + self.ql.arch.pointersize
else:
raise
def set_ret(self, addr):
# ARM
if self.ql.archtype== QL_ARCH.ARM:
self.ql.reg.lr = addr
if self.ql.arch.type == QL_ARCH.ARM:
self.ql.arch.regs.lr = addr
# MIPS32
elif self.ql.archtype== QL_ARCH.MIPS:
self.ql.reg.ra = addr
elif self.ql.arch.type == QL_ARCH.MIPS:
self.ql.arch.regs.ra = addr
# ARM64
elif self.ql.archtype== QL_ARCH.ARM64:
self.ql.mem.write(self.ql.reg.sp, self.ql.pack(addr))
elif self.ql.arch.type == QL_ARCH.ARM64:
self.ql.arch.stack_write(0, addr)
# X86
elif self.ql.archtype== QL_ARCH.X86:
self.ql.mem.write(self.ql.reg.esp, self.ql.pack(addr))
elif self.ql.arch.type == QL_ARCH.X86:
self.ql.arch.stack_write(0, addr)
# X8664
elif self.ql.archtype== QL_ARCH.X8664:
self.ql.mem.write(self.ql.reg.rsp, self.ql.pack(addr))
elif self.ql.arch.type == QL_ARCH.X8664:
self.ql.arch.stack_write(0, addr)
else:
raise
def call_enter(self):
# if self.ql.archtype == QL_ARCH.ARM or self.ql.archtype == QL_ARCH.ARM64:
# self.ql.reg.arch_pc = self.ql.reg.arch_pc + 4
# if self.ql.arch.type == QL_ARCH.ARM or self.ql.arch.type == QL_ARCH.ARM64:
# self.ql.arch.regs.arch_pc = self.ql.arch.regs.arch_pc + 4
next_pc = self.ql.unpack(self.ql.mem.read(self.hook_data_ptr, self.ql.pointersize))
next_pc = self.ql.unpack(self.ql.mem.read(self.hook_data_ptr, self.ql.arch.pointersize))
self.ret_pc = self.get_ret_pc()
onenter_cb = None
onenter_userdata = None
@@ -168,37 +168,37 @@ class HookFunc:
cb(self.ql, userdata)
else:
self.set_ret(self.exit_addr)
self.ql.reg.arch_pc = next_pc
self.ql.arch.regs.arch_pc = next_pc
else:
self.context_fixup()
def ret(self):
# ARM
if self.ql.archtype== QL_ARCH.ARM:
self.ql.reg.arch_pc = self.ret_pc
if self.ql.arch.type == QL_ARCH.ARM:
self.ql.arch.regs.arch_pc = self.ret_pc
# MIPS32
elif self.ql.archtype== QL_ARCH.MIPS:
self.ql.reg.arch_pc = self.ret_pc
elif self.ql.arch.type == QL_ARCH.MIPS:
self.ql.arch.regs.arch_pc = self.ret_pc
# ARM64
elif self.ql.archtype== QL_ARCH.ARM64:
self.ql.reg.arch_pc = self.ret_pc
elif self.ql.arch.type == QL_ARCH.ARM64:
self.ql.arch.regs.arch_pc = self.ret_pc
# X86
elif self.ql.archtype== QL_ARCH.X86:
self.ql.reg.arch_pc = self.ret_pc
elif self.ql.arch.type == QL_ARCH.X86:
self.ql.arch.regs.arch_pc = self.ret_pc
# X8664
elif self.ql.archtype== QL_ARCH.X8664:
self.ql.reg.arch_pc = self.ret_pc
elif self.ql.arch.type == QL_ARCH.X8664:
self.ql.arch.regs.arch_pc = self.ret_pc
else:
raise
def call_exit(self):
# if self.ql.archtype == QL_ARCH.ARM or self.ql.archtype == QL_ARCH.ARM64:
# self.ql.reg.arch_pc = self.ql.reg.arch_pc + 4
# if self.ql.arch.type == QL_ARCH.ARM or self.ql.arch.type == QL_ARCH.ARM64:
# self.ql.arch.regs.arch_pc = self.ql.arch.regs.arch_pc + 4
onexit_cb = None
onexit_userdata = None
@@ -241,10 +241,10 @@ class HookFuncRel(HookFunc):
self.ori_offest = self.rel.r_offset
self.rel.r_offset = self.hook_data_ptr - self.load_base
self.ori_data = self.ql.mem.read(self.ori_offest + self.load_base, self.ql.pointersize)
self.ori_data = self.ql.mem.read(self.ori_offest + self.load_base, self.ql.arch.pointersize)
self.ql.mem.write(self.rel.ptr, self.rel.pack())
self.ql.mem.write(self.ori_offest + self.load_base, self.ql.pack(self.hook_fuc_ptr))
self.ql.mem.write_ptr(self.ori_offest + self.load_base, self.hook_fuc_ptr)
self.ql.mem.write(self.hook_data_ptr, bytes(self.ori_data))
class HookFuncMips(HookFunc):
@@ -255,21 +255,21 @@ class HookFuncMips(HookFunc):
self.gotidx = gotidx
def _hook_fuc_enter(self, ql):
self.ql.reg.t9 = self.ql.unpack(self.ql.mem.read(self.hook_data_ptr, self.ql.pointersize))
self.ql.arch.regs.t9 = self.ql.unpack(self.ql.mem.read(self.hook_data_ptr, self.ql.arch.pointersize))
self.call_enter()
def _hook_fuc_exit(self, ql):
self.call_exit()
tmp = self.ql.unpack(self.ql.mem.read(self.got + self.load_base + self.gotidx * self.ql.pointersize, self.ql.pointersize))
tmp = self.ql.unpack(self.ql.mem.read(self.got + self.load_base + self.gotidx * self.ql.arch.pointersize, self.ql.arch.pointersize))
if tmp != self.hook_fuc_ptr:
self.ql.mem.write(self.got + self.load_base + self.gotidx * self.ql.pointersize, self.ql.pack(self.hook_fuc_ptr))
self.ql.mem.write(self.hook_data_ptr, self.ql.pack(tmp))
self.ql.mem.write_ptr(self.got + self.load_base + self.gotidx * self.ql.arch.pointersize, self.hook_fuc_ptr)
self.ql.mem.write_ptr(self.hook_data_ptr, tmp)
def _hook_got(self):
self.ori_data = self.ql.mem.read(self.got + self.load_base + self.gotidx * self.ql.pointersize, self.ql.pointersize)
self.ori_data = self.ql.mem.read(self.got + self.load_base + self.gotidx * self.ql.arch.pointersize, self.ql.arch.pointersize)
self.ql.mem.write(self.got + self.load_base + self.gotidx * self.ql.pointersize, self.ql.pack(self.hook_fuc_ptr))
self.ql.mem.write_ptr(self.got + self.load_base + self.gotidx * self.ql.arch.pointersize, self.hook_fuc_ptr)
self.ql.mem.write(self.hook_data_ptr, bytes(self.ori_data))
def enable(self):
@@ -491,7 +491,7 @@ class ELF_Symtab:
self.symtab = symtab
self.endian = endian
self.symclass = ELF32_Sym if self.ql.archbit == 32 else ELF64_Sym
self.symclass = ELF32_Sym if self.ql.arch.bits == 32 else ELF64_Sym
def __getitem__(self, idx):
buf = self.ql.mem.read(self.symtab + idx * self.symclass.Sym_SIZE, self.symclass.Sym_SIZE)
@@ -536,19 +536,19 @@ class FunctionHook:
self.strtab_size = None
self.symtab = None
self.syment = ELF32_Sym.Sym_SIZE if ql.archbit == 32 else ELF64_Sym.Sym_SIZE
self.syment = ELF32_Sym.Sym_SIZE if ql.arch.bits == 32 else ELF64_Sym.Sym_SIZE
self.plt_rel_size = None
self.plt_rel = None
self.plt_rel_type = DT_REL if ql.archbit == 32 else DT_RELA
self.plt_rel_type = DT_REL if ql.arch.bits == 32 else DT_RELA
self.rela = None
self.rela_size = None
self.relaent = ELF32_Rela.Rela_SIZE if ql.archbit == 32 else ELF64_Rela.Rela_SIZE
self.relaent = ELF32_Rela.Rela_SIZE if ql.arch.bits == 32 else ELF64_Rela.Rela_SIZE
self.rel = None
self.rel_size = None
self.relent = ELF32_Rel.Rel_SIZE if ql.archbit == 32 else ELF64_Rel.Rel_SIZE
self.relent = ELF32_Rel.Rel_SIZE if ql.arch.bits == 32 else ELF64_Rel.Rel_SIZE
self.plt_got = None
self.mips_local_gotno = None
@@ -556,10 +556,10 @@ class FunctionHook:
self.mips_gotsym = None
self.rel_list = []
self.endian = 0 if ql.archendian == QL_ENDIAN.EL else 1
self.endian = 0 if ql.arch.endian == QL_ENDIAN.EL else 1
# ARM
if self.ql.archtype in [QL_ARCH.ARM, QL_ARCH.ARM_THUMB]:
if self.ql.arch.type == QL_ARCH.ARM:
self.GLOB_DAT = 21
self.JMP_SLOT = 22
# orr r1, r1, r1
@@ -567,7 +567,7 @@ class FunctionHook:
self.add_function_hook = self.add_function_hook_relocation
# MIPS32
elif self.ql.archtype== QL_ARCH.MIPS:
elif self.ql.arch.type == QL_ARCH.MIPS:
self.GLOB_DAT = 21
self.JMP_SLOT = 22
# add $t9, $t9, $zero
@@ -575,7 +575,7 @@ class FunctionHook:
self.add_function_hook = self.add_function_hook_mips
# ARM64
elif self.ql.archtype== QL_ARCH.ARM64:
elif self.ql.arch.type == QL_ARCH.ARM64:
self.GLOB_DAT = 1025
self.JMP_SLOT = 1026
# orr x1,x1,x1
@@ -583,7 +583,7 @@ class FunctionHook:
self.add_function_hook = self.add_function_hook_relocation
# X86
elif self.ql.archtype== QL_ARCH.X86:
elif self.ql.arch.type == QL_ARCH.X86:
self.GLOB_DAT = 6
self.JMP_SLOT = 7
# nop
@@ -591,14 +591,14 @@ class FunctionHook:
self.add_function_hook = self.add_function_hook_relocation
# X8664
elif self.ql.archtype== QL_ARCH.X8664:
elif self.ql.arch.type == QL_ARCH.X8664:
self.GLOB_DAT = 6
self.JMP_SLOT = 7
# nop
ins = b'\x90'
self.add_function_hook = self.add_function_hook_relocation
elif self.ql.archtype== QL_ARCH.RISCV:
elif self.ql.arch.type == QL_ARCH.RISCV:
self.GLOB_DAT = 21
self.JMP_SLOT = 22
@@ -606,7 +606,7 @@ class FunctionHook:
ins = b'\x00\x01'
self.add_function_hook = self.add_function_hook_relocation
elif self.ql.archtype== QL_ARCH.RISCV64:
elif self.ql.arch.type == QL_ARCH.RISCV64:
self.GLOB_DAT = 21
self.JMP_SLOT = 22
@@ -626,7 +626,7 @@ class FunctionHook:
self.rel_list += self.plt_rel
self.show_relocation(self.plt_rel)
if self.ql.archtype == QL_ARCH.MIPS and self.plt_got != None and self.mips_gotsym != None and self.mips_local_gotno != None and self.mips_symtabno != None:
if self.ql.arch.type == QL_ARCH.MIPS and self.plt_got != None and self.mips_gotsym != None and self.mips_local_gotno != None and self.mips_symtabno != None:
self.show_dynsym_name(self.mips_gotsym, self.mips_symtabno)
self.ql.mem.map(hook_mem, 0x2000, perms=7, info="[hook_mem]")
@@ -700,9 +700,9 @@ class FunctionHook:
return
def parse_program_header(self):
if self.ql.archbit == 64:
if self.ql.arch.bits == 64:
return self.parse_program_header64()
elif self.ql.archbit == 32:
elif self.ql.arch.bits == 32:
return self.parse_program_header32()
def parse_dynamic64(self):
@@ -768,9 +768,9 @@ class FunctionHook:
return
def parse_dynamic(self):
if self.ql.archbit == 64:
if self.ql.arch.bits == 64:
return self.parse_dynamic64()
elif self.ql.archbit == 32:
elif self.ql.arch.bits == 32:
return self.parse_dynamic32()
def _parse(self):
@@ -786,19 +786,19 @@ class FunctionHook:
if d.d_tag == DT_NULL:
break
elif d.d_tag == DT_HASH:
# self.hash_nbucket = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un, self.ql.pointersize))
# self.hash_nchain = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un + self.ql.pointersize, self.ql.pointersize))
# self.hash_bucket = self.ql.mem.read(self.load_base + d.d_un + self.ql.pointersize * 2, self.ql.pointersize * self.hash_nbucket)
# self.hash_chain = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un + self.ql.pointersize * 2 + self.ql.pointersize * self.hash_nbucket, self.ql.pointersize))
# self.hash_nbucket = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un, self.ql.arch.pointersize))
# self.hash_nchain = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un + self.ql.arch.pointersize, self.ql.arch.pointersize))
# self.hash_bucket = self.ql.mem.read(self.load_base + d.d_un + self.ql.arch.pointersize * 2, self.ql.arch.pointersize * self.hash_nbucket)
# self.hash_chain = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un + self.ql.arch.pointersize * 2 + self.ql.arch.pointersize * self.hash_nbucket, self.ql.arch.pointersize))
pass
elif d.d_tag == DT_GNU_HASH:
# self.gnu_nbucket = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un, self.ql.pointersize))
# self.gnu_symbias = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un + self.ql.pointersize, self.ql.pointersize))))
# self.gnu_maskwords = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un + self.ql.pointersize * 2, self.ql.pointersize))
# self.gnu_shift2 = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un + self.ql.pointersize * 3, self.ql.pointersize))
# self.gnu_bloom_filter = self.ql.mem.read(self.load_base + d.d_un + self.ql.pointersize * 4, self.gnu_maskwords)
# self.gnu_bucket = self.ql.mem.read(self.load_base + d.d_un + self.ql.pointersize * 4 + self.gnu_maskwords, self.ql.pointersize * self.gnu_nbucket)
# self.gnu_chain = self.load_base + d.d_un + self.ql.pointersize * 4 + self.gnu_maskwords + self.ql.pointersize * self.gnu_nbucket - self.ql.pointersize * self.gnu_symbias
# self.gnu_nbucket = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un, self.ql.arch.pointersize))
# self.gnu_symbias = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un + self.ql.arch.pointersize, self.ql.arch.pointersize))))
# self.gnu_maskwords = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un + self.ql.arch.pointersize * 2, self.ql.arch.pointersize))
# self.gnu_shift2 = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un + self.ql.arch.pointersize * 3, self.ql.arch.pointersize))
# self.gnu_bloom_filter = self.ql.mem.read(self.load_base + d.d_un + self.ql.arch.pointersize * 4, self.gnu_maskwords)
# self.gnu_bucket = self.ql.mem.read(self.load_base + d.d_un + self.ql.arch.pointersize * 4 + self.gnu_maskwords, self.ql.arch.pointersize * self.gnu_nbucket)
# self.gnu_chain = self.load_base + d.d_un + self.ql.arch.pointersize * 4 + self.gnu_maskwords + self.ql.arch.pointersize * self.gnu_nbucket - self.ql.arch.pointersize * self.gnu_symbias
pass
elif d.d_tag == DT_STRTAB:
@@ -859,31 +859,31 @@ class FunctionHook:
if self.rela != None and self.rela_size != None:
rela_buf = self.ql.mem.read(self.rela, self.rela_size)
rela_ptr = self.rela
if self.ql.archbit == 32:
if self.ql.arch.bits == 32:
self.rela = [ELF32_Rela(rela_buf[_ * self.relaent : (_ + 1) * self.relaent], self.endian, rela_ptr + _ * self.relaent) for _ in range(self.rela_size // self.relaent)]
elif self.ql.archbit == 64:
elif self.ql.arch.bits == 64:
self.rela = [ELF64_Rela(rela_buf[_ * self.relaent : (_ + 1) * self.relaent], self.endian, rela_ptr + _ * self.relaent) for _ in range(self.rela_size // self.relaent)]
if self.rel != None and self.rel_size != None:
rel_buf = self.ql.mem.read(self.rel, self.rel_size)
rel_ptr = self.rel
if self.ql.archbit == 32:
if self.ql.arch.bits == 32:
self.rel = [ELF32_Rel(rel_buf[_ * self.relent : (_ + 1) * self.relent], self.endian, rel_ptr + _ * self.relent) for _ in range(self.rel_size // self.relent)]
elif self.ql.archbit == 64:
elif self.ql.arch.bits == 64:
self.rel = [ELF64_Rel(rel_buf[_ * self.relent : (_ + 1) * self.relent], self.endian, rel_ptr + _ * self.relent) for _ in range(self.rel_size // self.relent)]
if self.plt_rel != None and self.plt_rel_size != None:
plt_rel_buf = self.ql.mem.read(self.plt_rel, self.plt_rel_size)
plt_rel_ptr = self.plt_rel
if self.plt_rel_type == DT_REL:
if self.ql.archbit == 32:
if self.ql.arch.bits == 32:
self.plt_rel = [ELF32_Rel(plt_rel_buf[_ * self.relent : (_ + 1) * self.relent], self.endian, plt_rel_ptr + _ * self.relent) for _ in range(self.plt_rel_size // self.relent)]
elif self.ql.archbit == 64:
elif self.ql.arch.bits == 64:
self.plt_rel = [ELF64_Rel(plt_rel_buf[_ * self.relent : (_ + 1) * self.relent], self.endian, plt_rel_ptr + _ * self.relent) for _ in range(self.plt_rel_size // self.relent)]
else:
if self.ql.archbit == 32:
if self.ql.arch.bits == 32:
self.plt_rel = [ELF32_Rela(plt_rel_buf[_ * self.relaent : (_ + 1) * self.relaent], self.endian, plt_rel_ptr + _ * self.relaent) for _ in range(self.plt_rel_size // self.relaent)]
elif self.ql.archbit == 64:
elif self.ql.arch.bits == 64:
self.plt_rel = [ELF64_Rela(plt_rel_buf[_ * self.relaent : (_ + 1) * self.relaent], self.endian, plt_rel_ptr + _ * self.relaent) for _ in range(self.plt_rel_size // self.relaent)]
if self.symtab != None:
@@ -902,7 +902,7 @@ class FunctionHook:
self.ql.log.debug('dynsym name ' + str(rel_name))
def _hook_int(self, ql, intno):
idx = (self.ql.reg.arch_pc - self.hook_mem) // 0x10
idx = (self.ql.arch.regs.arch_pc - self.hook_mem) // 0x10
if idx not in self.use_list.keys():
raise
@@ -932,9 +932,9 @@ class FunctionHook:
hf.enable()
# if self.hook_int == False:
# if self.ql.archtype == QL_ARCH.X86 or self.ql.archtype == QL_ARCH.X8664:
# if self.ql.arch.type == QL_ARCH.X86 or self.ql.arch.type == QL_ARCH.X8664:
# self.ql.hook_intno(self._hook_int, 0xa0)
# elif self.ql.archtype == QL_ARCH.ARM or self.ql.archtype == QL_ARCH.ARM64:
# elif self.ql.arch.type == QL_ARCH.ARM or self.ql.arch.type == QL_ARCH.ARM64:
# self.ql.hook_intno(self._hook_int, 7)
# self.hook_int = True
+29 -21
View File
@@ -5,10 +5,12 @@
from typing import Callable
from unicorn import UcError
from unicorn.x86_const import UC_X86_INS_SYSCALL
from qiling import Qiling
from qiling.arch.x86_const import UC_X86_INS_SYSCALL
from qiling.arch.x86 import GDTManager, ql_x8664_set_gs, ql_x86_register_cs, ql_x86_register_ds_ss_es
from qiling.arch.x86_const import GS_SEGMENT_ADDR, GS_SEGMENT_SIZE
from qiling.arch.x86_utils import GDTManager, SegmentManager86, SegmentManager64
from qiling.arch import arm_utils
from qiling.cc import QlCC, intel, arm, mips, riscv
from qiling.const import QL_ARCH, QL_INTERCEPT
from qiling.os.fcall import QlFunctionCall
@@ -33,7 +35,7 @@ class QlOsLinux(QlOsPosix):
QL_ARCH.MIPS : mips.mipso32,
QL_ARCH.RISCV : riscv.riscv,
QL_ARCH.RISCV64: riscv.riscv,
}[ql.archtype](ql)
}[ql.arch.type](ql.arch)
self.fcall = QlFunctionCall(ql, cc)
@@ -44,61 +46,67 @@ class QlOsLinux(QlOsPosix):
self.elf_mem_start = 0x0
self.load()
if self.ql.archtype == QL_ARCH.X8664:
ql_x8664_set_gs(self.ql)
def load(self):
self.futexm = futex.QlLinuxFutexManagement()
# ARM
if self.ql.archtype == QL_ARCH.ARM:
if self.ql.arch.type == QL_ARCH.ARM:
self.ql.arch.enable_vfp()
self.ql.hook_intno(self.hook_syscall, 2)
self.thread_class = thread.QlLinuxARMThread
self.ql.arch.init_get_tls()
arm_utils.init_get_tls(self.ql, self.ql.arch.arm_get_tls_addr)
# MIPS32
elif self.ql.archtype == QL_ARCH.MIPS:
elif self.ql.arch.type == QL_ARCH.MIPS:
self.ql.hook_intno(self.hook_syscall, 17)
self.thread_class = thread.QlLinuxMIPS32Thread
# ARM64
elif self.ql.archtype == QL_ARCH.ARM64:
elif self.ql.arch.type == QL_ARCH.ARM64:
self.ql.arch.enable_vfp()
self.ql.hook_intno(self.hook_syscall, 2)
self.thread_class = thread.QlLinuxARM64Thread
# X86
elif self.ql.archtype == QL_ARCH.X86:
elif self.ql.arch.type == QL_ARCH.X86:
self.gdtm = GDTManager(self.ql)
ql_x86_register_cs(self)
ql_x86_register_ds_ss_es(self)
# setup gdt and segments selectors
segm = SegmentManager86(self.ql.arch, self.gdtm)
segm.setup_cs_ds_ss_es(0, 4 << 30)
self.ql.hook_intno(self.hook_syscall, 0x80)
self.thread_class = thread.QlLinuxX86Thread
# X8664
elif self.ql.archtype == QL_ARCH.X8664:
elif self.ql.arch.type == QL_ARCH.X8664:
self.gdtm = GDTManager(self.ql)
ql_x86_register_cs(self)
ql_x86_register_ds_ss_es(self)
# setup gdt and segments selectors
segm = SegmentManager64(self.ql.arch, self.gdtm)
segm.setup_cs_ds_ss_es(0, 4 << 30)
segm.setup_gs(GS_SEGMENT_ADDR, GS_SEGMENT_SIZE)
self.ql.hook_insn(self.hook_syscall, UC_X86_INS_SYSCALL)
# Keep test for _cc
#self.ql.hook_insn(hook_posix_api, UC_X86_INS_SYSCALL)
self.thread_class = thread.QlLinuxX8664Thread
elif self.ql.archtype == QL_ARCH.RISCV:
elif self.ql.arch.type == QL_ARCH.RISCV:
self.ql.arch.enable_float()
self.ql.hook_intno(self.hook_syscall, 8)
self.thread_class = None
elif self.ql.archtype == QL_ARCH.RISCV64:
elif self.ql.arch.type == QL_ARCH.RISCV64:
self.ql.arch.enable_float()
self.ql.hook_intno(self.hook_syscall, 8)
self.thread_class = None
for i in range(NR_OPEN):
# on fork or execve, do not inherit opened files tagged as 'close on exec'
for i in range(len(self.fd)):
if getattr(self.fd[i], 'close_on_exec', 0):
self.fd[i] = 0
self.fd[i] = None
def hook_syscall(self, ql, intno = None):
return self.load_syscall()
@@ -138,7 +146,7 @@ class QlOsLinux(QlOsPosix):
elif self.ql.loader.elf_entry != self.ql.loader.entry_point:
entry_address = self.ql.loader.elf_entry
if self.ql.archtype == QL_ARCH.ARM and entry_address & 1 == 1:
if self.ql.arch.type == QL_ARCH.ARM and entry_address & 1 == 1:
entry_address -= 1
self.ql.emu_start(self.ql.loader.entry_point, entry_address, self.ql.timeout)
self.ql.enable_lib_patch()
+2 -2
View File
@@ -20,8 +20,8 @@ def map_syscall(ql, syscall_num):
QL_ARCH.MIPS: mips_syscall_table,
QL_ARCH.RISCV: riscv32_syscall_table,
QL_ARCH.RISCV64: riscv64_syscall_table,
}[ql.archtype]
}[ql.arch.type]
return f'{SYSCALL_PREF}{syscall_table[syscall_num]}'
arm_syscall_table = {
+20 -22
View File
@@ -21,7 +21,7 @@ class timespec(ctypes.Structure):
# Temporary dirty fix.
# TODO: Pack ctypes.Structure according to ql.archtype and ql.ostype?
# TODO: Pack ctypes.Structure according to ql.arch.type and ql.ostype?
class timespec32(ctypes.Structure):
_fields_ = [
("tv_sec", ctypes.c_uint32),
@@ -30,11 +30,8 @@ class timespec32(ctypes.Structure):
_pack_ = 4
def ql_syscall_set_thread_area(ql: Qiling, u_info_addr, *args, **kw):
if ql.archtype == QL_ARCH.X86:
GDT_ENTRY_TLS_MIN = 12
GDT_ENTRY_TLS_MAX = 14
def ql_syscall_set_thread_area(ql: Qiling, u_info_addr: int):
if ql.arch.type == QL_ARCH.X86:
u_info = ql.mem.read(u_info_addr, 4 * 4)
index = ql.unpack32s(u_info[0 : 4])
base = ql.unpack32(u_info[4 : 8])
@@ -45,37 +42,38 @@ def ql_syscall_set_thread_area(ql: Qiling, u_info_addr, *args, **kw):
if index == -1:
index = ql.os.gdtm.get_free_idx(12)
if index == -1 or index < GDT_ENTRY_TLS_MIN or index > GDT_ENTRY_TLS_MAX:
if index in (12, 13, 14):
access = QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_3 | QL_X86_A_DIR_CON_BIT
ql.os.gdtm.register_gdt_segment(index, base, limit, access)
ql.mem.write_ptr(u_info_addr, index, 4)
else:
ql.log.warning(f"Wrong index {index} from address {hex(u_info_addr)}")
return -1
else:
ql.os.gdtm.register_gdt_segment(index, base, limit, QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_3 | QL_X86_A_DIR_CON_BIT, QL_X86_S_GDT | QL_X86_S_PRIV_3)
ql.mem.write(u_info_addr, ql.pack32(index))
return 0
elif ql.archtype == QL_ARCH.MIPS:
elif ql.arch.type == QL_ARCH.MIPS:
CONFIG3_ULR = (1 << 13)
ql.reg.cp0_config3 = CONFIG3_ULR
ql.reg.cp0_userlocal = u_info_addr
ql.reg.v0 = 0
ql.reg.a3 = 0
ql.arch.regs.cp0_config3 = CONFIG3_ULR
ql.arch.regs.cp0_userlocal = u_info_addr
ql.arch.regs.v0 = 0
ql.arch.regs.a3 = 0
ql.log.debug ("set_thread_area(0x%x)" % u_info_addr)
return 0
def ql_syscall_set_tls(ql, address, *args, **kw):
if ql.archtype == QL_ARCH.ARM:
ql.reg.c13_c0_3 = address
ql.mem.write(ql.arch.arm_get_tls_addr + 12, ql.pack32(address))
ql.reg.r0 = address
if ql.arch.type == QL_ARCH.ARM:
ql.arch.regs.c13_c0_3 = address
ql.mem.write_ptr(ql.arch.arm_get_tls_addr + 12, address, 4)
ql.arch.regs.r0 = address
ql.log.debug("settls(0x%x)" % address)
def ql_syscall_clock_gettime(ql, clock_gettime_clock_id, clock_gettime_timespec, *args, **kw):
now = datetime.now().timestamp()
tv_sec = floor(now)
tv_nsec = floor((now - floor(now)) * 1e6)
if ql.archtype == QL_ARCH.X8664:
if ql.arch.type == QL_ARCH.X8664:
tp = timespec(tv_sec= tv_sec, tv_nsec=tv_nsec)
else:
tp = timespec32(tv_sec= tv_sec, tv_nsec=tv_nsec)
@@ -89,7 +87,7 @@ def ql_syscall_gettimeofday(ql, gettimeofday_tv, gettimeofday_tz, *args, **kw):
now = datetime.now().timestamp()
tv_sec = floor(now)
tv_nsec = floor((now - floor(now)) * 1e6)
if ql.archtype == QL_ARCH.X8664:
if ql.arch.type == QL_ARCH.X8664:
tp = timespec(tv_sec= tv_sec, tv_nsec=tv_nsec)
else:
tp = timespec32(tv_sec= tv_sec, tv_nsec=tv_nsec)
+65 -53
View File
@@ -5,11 +5,12 @@
import gevent, os
from typing import Callable
from typing import Callable, Sequence
from abc import abstractmethod
from unicorn.unicorn import UcError
from qiling import Qiling
from qiling.os.thread import *
from qiling.arch.x86_const import *
from qiling.exception import QlErrorExecutionStop
@@ -25,7 +26,7 @@ THREAD_STATUS_STOPPED = 4
THREAD_STATUS_SUSPEND = 5
class QlLinuxThread(QlThread):
def __init__(self, ql, start_address, exit_point, context = None, set_child_tid_addr = None, thread_id = None):
def __init__(self, ql: Qiling, start_address: int, exit_point: int, context = None, set_child_tid_addr = None, thread_id: int = None):
super(QlLinuxThread, self).__init__(ql)
if not thread_id:
self.new_thread_id()
@@ -84,7 +85,7 @@ class QlLinuxThread(QlThread):
self._robust_list_head_len = None
if self._set_child_tid_address != None:
self.ql.mem.write(self._set_child_tid_address, ql.pack32(self.id))
self.ql.mem.write_ptr(self._set_child_tid_address, self.id, 4)
@property
def ql(self):
@@ -222,7 +223,7 @@ class QlLinuxThread(QlThread):
# In this context, we do:
# - Call gevent functions to switch threads.
# - Forward blocking syscalls to gevent.
self.ql.reg.arch_pc = self.start_address
self.ql.arch.regs.arch_pc = self.start_address
if not self._saved_context:
self.save()
@@ -235,11 +236,11 @@ class QlLinuxThread(QlThread):
self.restore()
# Sanity check
if self.ql.reg.arch_pc == self.exit_point:
if self.ql.arch.regs.arch_pc == self.exit_point:
self.ql.log.warning(f"Nothing to do but still get scheduled!")
# Run and log the run event
start_address = self.ql.arch.get_pc() # For arm thumb.
start_address = getattr(self.ql.arch, 'effective_pc', self.ql.arch.regs.arch_pc) # For arm thumb.
self.sched_cb = QlLinuxThread._default_sched_cb
self.ql.log.debug(f"Scheduled from {hex(start_address)}.")
@@ -250,7 +251,7 @@ class QlLinuxThread(QlThread):
self.ql.os.emu_error()
self.ql.log.exception("")
raise e
self.ql.log.debug(f"Suspended at {hex(self.ql.reg.arch_pc)}")
self.ql.log.debug(f"Suspended at {hex(self.ql.arch.regs.arch_pc)}")
self.save()
# Note that this callback may be set by UC callbacks.
@@ -261,7 +262,7 @@ class QlLinuxThread(QlThread):
self.ql.log.debug(f"Call sched_cb: {self.sched_cb}")
self.sched_cb(self)
if self.status == THREAD_STATUS_TERMINATED or self.ql.reg.arch_pc == self.exit_point:
if self.status == THREAD_STATUS_TERMINATED or self.ql.arch.regs.arch_pc == self.exit_point:
break
self._on_stop()
@@ -303,18 +304,18 @@ class QlLinuxThread(QlThread):
return new_thread
def save_context(self):
self.saved_context = self.ql.arch.context_save()
self.saved_context = self.ql.arch.save()
def restore_context(self):
self.ql.arch.context_restore(self.saved_context)
self.ql.arch.restore(self.saved_context)
def set_start_address(self, addr):
# We can't modify UcContext directly.
old_context = self.ql.arch.context_save()
old_context = self.ql.arch.save()
self.restore_context()
self.ql.reg.arch_pc = addr
self.ql.arch.regs.arch_pc = addr
self.save_context()
self.ql.arch.context_restore(old_context)
self.ql.arch.restore(old_context)
def set_clear_child_tid_addr(self, addr):
self.clear_child_tid_address = addr
@@ -332,7 +333,7 @@ class QlLinuxThread(QlThread):
if self.clear_child_tid_address is not None:
self.ql.log.debug(f"Perform CLONE_CHILD_CLEARTID at {hex(self.clear_child_tid_address)}")
self.ql.mem.write(self.clear_child_tid_address, self.ql.pack32(0))
self.ql.mem.write_ptr(self.clear_child_tid_address, 0, 4)
wakes = self.ql.os.futexm.get_futex_wake_list(self.ql, self.clear_child_tid_address, 1)
self.clear_child_tid_address = None
# When the thread is to stop, we don't have chance for next sched_cb, so
@@ -368,12 +369,18 @@ class QlLinuxX86Thread(QlLinuxThread):
"""docstring for X86Thread"""
def __init__(self, ql, start_address, exit_point, context = None, set_child_tid_addr = None, thread_id = None):
super(QlLinuxX86Thread, self).__init__(ql, start_address, exit_point, context, set_child_tid_addr, thread_id)
self.tls = bytes(b'\x00' * (8 * 3))
self.tls = [b'\x00' * 8] * 3
def __read_tls(self) -> Sequence[bytes]:
return [self.ql.os.gdtm.get_entry(i) for i in (12, 13, 14)]
def __write_tls(self, tls: Sequence[bytes]) -> None:
for i, entry in zip((12, 13, 14), tls):
self.ql.os.gdtm.set_entry(i, entry)
def set_thread_tls(self, tls_addr):
old_tls = bytes(self.ql.os.gdtm.get_gdt_buf(12, 14 + 1))
self.ql.os.gdtm.set_gdt_buf(12, 14 + 1, self.tls)
old_tls = self.__read_tls()
self.__write_tls(self.tls)
u_info = self.ql.mem.read(tls_addr, 4 * 4)
index = self.ql.unpack32s(u_info[0 : 4])
@@ -383,25 +390,30 @@ class QlLinuxX86Thread(QlLinuxThread):
if index == -1:
index = self.ql.os.gdtm.get_free_idx(12)
if index == -1 or index < 12 or index > 14:
raise
else:
self.ql.os.gdtm.register_gdt_segment(index, base, limit, QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_3 | QL_X86_A_DIR_CON_BIT, QL_X86_S_GDT | QL_X86_S_PRIV_3)
self.ql.mem.write(tls_addr, self.ql.pack32(index))
if index in (12, 13, 14):
access = QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_3 | QL_X86_A_DIR_CON_BIT
self.tls = bytes(self.ql.os.gdtm.get_gdt_buf(12, 14 + 1))
self.ql.os.gdtm.set_gdt_buf(12, 14 + 1, old_tls)
self.ql.log.debug(f"Set tls to index={hex(index)} base={hex(base)} limit={hex(limit)} fs={hex(self.ql.reg.fs)} gs={hex(self.ql.reg.gs)} gdt_buf={self.tls}")
self.ql.os.gdtm.register_gdt_segment(index, base, limit, access)
self.ql.mem.write_ptr(tls_addr, index, 4)
else:
raise
self.tls = self.__read_tls()
self.__write_tls(old_tls)
self.ql.log.debug(f'Set TLS to index={index:d} base={base:#x} limit={limit:#x} fs={self.ql.arch.regs.fs:#06x} gs={self.ql.arch.regs.gs:#06x}')
def save(self):
self.save_context()
self.tls = bytes(self.ql.os.gdtm.get_gdt_buf(12, 14 + 1))
self.ql.log.debug(f"Saved context. fs={hex(self.ql.reg.fs)} gs={hex(self.ql.reg.gs)} gdt_buf={self.tls}")
self.tls = self.__read_tls()
self.ql.log.debug(f'Context saved (fs={self.ql.arch.regs.fs:#06x} gs={self.ql.arch.regs.gs:#06x} gdt_buf=[{" ".join(ent.hex() for ent in self.tls)}])')
def restore(self):
self.restore_context()
self.ql.os.gdtm.set_gdt_buf(12, 14 + 1, self.tls)
self.ql.log.debug(f"Restored context. fs={hex(self.ql.reg.fs)} gs={hex(self.ql.reg.gs)} gdt_buf={self.tls}")
self.__write_tls(self.tls)
self.ql.log.debug(f'Context restored (fs={self.ql.arch.regs.fs:#06x} gs={self.ql.arch.regs.gs:#06x} gdt_buf=[{" ".join(ent.hex() for ent in self.tls)}])')
def clone(self):
new_thread = super(QlLinuxX86Thread, self).clone()
@@ -416,7 +428,7 @@ class QlLinuxX8664Thread(QlLinuxThread):
def set_thread_tls(self, tls_addr):
self.tls = tls_addr
self.ql.reg.msr(FSMSR, self.tls)
self.ql.arch.msr.write(IA32_FS_BASE_MSR, self.tls)
self.ql.log.debug(f"Set fsbase to {hex(tls_addr)} for {str(self)}")
# Some notes:
@@ -424,13 +436,13 @@ class QlLinuxX8664Thread(QlLinuxThread):
# - https://stackoverflow.com/questions/11497563/detail-about-msr-gs-base-in-linux-x86-64
def save(self):
self.save_context()
self.tls = self.ql.reg.msr(FSMSR)
self.ql.log.debug(f"Saved context: fs={hex(self.ql.reg.fsbase)} tls={hex(self.tls)}")
self.tls = self.ql.arch.msr.read(IA32_FS_BASE_MSR)
self.ql.log.debug(f"Saved context: fs={hex(self.ql.arch.regs.fsbase)} tls={hex(self.tls)}")
def restore(self):
self.restore_context()
self.set_thread_tls(self.tls)
self.ql.log.debug(f"Restored context: fs={hex(self.ql.reg.fsbase)} tls={hex(self.tls)}")
self.ql.log.debug(f"Restored context: fs={hex(self.ql.arch.regs.fsbase)} tls={hex(self.tls)}")
def clone(self):
new_thread = super(QlLinuxX8664Thread, self).clone()
@@ -447,19 +459,19 @@ class QlLinuxMIPS32Thread(QlLinuxThread):
def set_thread_tls(self, tls_addr):
self.tls = tls_addr
CONFIG3_ULR = (1 << 13)
self.ql.reg.cp0_config3 = CONFIG3_ULR
self.ql.reg.cp0_userlocal = self.tls
self.ql.log.debug(f"Set cp0 to {hex(self.ql.reg.cp0_userlocal)}")
self.ql.arch.regs.cp0_config3 = CONFIG3_ULR
self.ql.arch.regs.cp0_userlocal = self.tls
self.ql.log.debug(f"Set cp0 to {hex(self.ql.arch.regs.cp0_userlocal)}")
def save(self):
self.save_context()
self.tls = self.ql.reg.cp0_userlocal
self.ql.log.debug(f"Saved context. cp0={hex(self.ql.reg.cp0_userlocal)}")
self.tls = self.ql.arch.regs.cp0_userlocal
self.ql.log.debug(f"Saved context. cp0={hex(self.ql.arch.regs.cp0_userlocal)}")
def restore(self):
self.restore_context()
self.set_thread_tls(self.tls)
self.ql.log.debug(f"Restored context. cp0={hex(self.ql.reg.cp0_userlocal)}")
self.ql.log.debug(f"Restored context. cp0={hex(self.ql.arch.regs.cp0_userlocal)}")
def clone(self):
new_thread = super(QlLinuxMIPS32Thread, self).clone()
@@ -475,19 +487,19 @@ class QlLinuxARMThread(QlLinuxThread):
def set_thread_tls(self, tls_addr):
self.tls = tls_addr
self.ql.reg.c13_c0_3 = self.tls
self.ql.log.debug(f"Set c13_c0_3 to {hex(self.ql.reg.c13_c0_3)}")
self.ql.arch.regs.c13_c0_3 = self.tls
self.ql.log.debug(f"Set c13_c0_3 to {hex(self.ql.arch.regs.c13_c0_3)}")
def save(self):
self.save_context()
self.tls = self.ql.reg.c13_c0_3
self.ql.log.debug(f"Saved context. c13_c0_3={hex(self.ql.reg.c13_c0_3)}")
self.tls = self.ql.arch.regs.c13_c0_3
self.ql.log.debug(f"Saved context. c13_c0_3={hex(self.ql.arch.regs.c13_c0_3)}")
def restore(self):
self.restore_context()
self.set_thread_tls(self.tls)
self.ql.log.debug(f"Restored context. c13_c0_3={hex(self.ql.reg.c13_c0_3)}")
self.ql.log.debug(f"Restored context. c13_c0_3={hex(self.ql.arch.regs.c13_c0_3)}")
def clone(self):
new_thread = super(QlLinuxARMThread, self).clone()
@@ -503,18 +515,18 @@ class QlLinuxARM64Thread(QlLinuxThread):
def set_thread_tls(self, tls_addr):
self.tls = tls_addr
self.ql.reg.tpidr_el0 = self.tls
self.ql.log.debug(f"Set tpidr_el0 to {hex(self.ql.reg.tpidr_el0)}")
self.ql.arch.regs.tpidr_el0 = self.tls
self.ql.log.debug(f"Set tpidr_el0 to {hex(self.ql.arch.regs.tpidr_el0)}")
def save(self):
self.save_context()
self.tls = self.ql.reg.tpidr_el0
self.ql.log.debug(f"Saved context. tpidr_el0={hex(self.ql.reg.tpidr_el0)}")
self.tls = self.ql.arch.regs.tpidr_el0
self.ql.log.debug(f"Saved context. tpidr_el0={hex(self.ql.arch.regs.tpidr_el0)}")
def restore(self):
self.restore_context()
self.set_thread_tls(self.tls)
self.ql.log.debug(f"Restored context. tpidr_el0={hex(self.ql.reg.tpidr_el0)}")
self.ql.log.debug(f"Restored context. tpidr_el0={hex(self.ql.arch.regs.tpidr_el0)}")
def clone(self):
new_thread = super(QlLinuxARM64Thread, self).clone()
@@ -565,14 +577,14 @@ class QlLinuxThreadManagement:
def _prepare_lib_patch(self):
if self.ql.loader.elf_entry != self.ql.loader.entry_point:
entry_address = self.ql.loader.elf_entry
if self.ql.archtype == QL_ARCH.ARM and entry_address & 1 == 1:
if self.ql.arch.type == QL_ARCH.ARM and entry_address & 1 == 1:
entry_address -= 1
self.main_thread = self.ql.os.thread_class.spawn(self.ql, self.ql.loader.entry_point, entry_address)
self.cur_thread = self.main_thread
self._clear_queued_msg()
gevent.joinall([self.main_thread], raise_error=True)
if self.ql.reg.arch_pc != entry_address:
self.ql.log.error(f"{self.cur_thread} Expect {hex(self.ql.loader.elf_entry)} but get {hex(self.ql.reg.arch_pc)} when running loader.")
if self.ql.arch.regs.arch_pc != entry_address:
self.ql.log.error(f"{self.cur_thread} Expect {hex(self.ql.loader.elf_entry)} but get {hex(self.ql.arch.regs.arch_pc)} when running loader.")
raise QlErrorExecutionStop('Dynamic library .init() failed!')
self.ql.enable_lib_patch()
self.ql.os.run_function_after_load()
+7 -7
View File
@@ -269,10 +269,10 @@ class QlMacOSEvManager:
remains = 0
if len(params) <= 6:
for idx, p in enumerate(params):
self.ql.reg.write(reg_list[idx], p)
self.ql.arch.regs.write(reg_list[idx], p)
else:
for idx, p in enumerate(params[:6]):
self.ql.reg.write(reg_list[idx], p)
self.ql.arch.regs.write(reg_list[idx], p)
remains = len(params) - 6
for i in range(remains):
self.ql.stack_push(params[6 + i])
@@ -298,9 +298,9 @@ class QlMacOSEvManager:
uap.newlen = newlen
uap.updateToMem()
self.ql.reg.rdi = self.proc_find(0x1337).base
self.ql.reg.rsi = uap_addr # uap
self.ql.reg.rdx = 0 # unused retval
self.ql.arch.regs.rdi = self.proc_find(0x1337).base
self.ql.arch.regs.rsi = uap_addr # uap
self.ql.arch.regs.rdx = 0 # unused retval
self.ql.os.savedrip=self.deadcode
self.ql.run(self.ql.loader.kernel_extrn_symbols_detail[b"_sysctlbyname"]["n_value"])
@@ -645,9 +645,9 @@ class QlMacOSEvManager:
sysent = self.ql.loader.kernel_local_symbols_detail[b"_sysent"]["n_value"]
system_table = [sysent_t(self.ql, sysent + x * ctypes.sizeof(sysent_t)).loadFromMem() for x in range(nsyscall)]
self.ql.reg.write(UC_X86_REG_RAX, sysnum)
self.ql.arch.regs.write(UC_X86_REG_RAX, sysnum)
reg_list = [UC_X86_REG_RDI, UC_X86_REG_RSI, UC_X86_REG_RDX, UC_X86_REG_R10, UC_X86_REG_R8, UC_X86_REG_R9]
for idx, p in enumerate(params):
self.ql.reg.write(reg_list[idx], p)
self.ql.arch.regs.write(reg_list[idx], p)
self.ql.os.savedrip=self.deadcode
self.ql.run(begin=system_table[sysnum].sy_call.value)
+2 -2
View File
@@ -19,10 +19,10 @@ def vm_shared_region_enter(ql):
def map_commpage(ql):
if ql.archtype== QL_ARCH.X8664:
if ql.arch.type == QL_ARCH.X8664:
addr_base = X8664_COMM_PAGE_START_ADDRESS
addr_size = 0x100000
elif ql.archtype== QL_ARCH.ARM64:
elif ql.arch.type == QL_ARCH.ARM64:
addr_base = ARM64_COMM_PAGE_START_ADDRESS
addr_size = 0x1000
ql.mem.map(addr_base, addr_size, info="[commpage]")
+33 -29
View File
@@ -4,11 +4,12 @@
#
from ctypes import sizeof
from unicorn import UcError
from unicorn.x86_const import UC_X86_INS_SYSCALL
from qiling import Qiling
from qiling.arch.x86 import GDTManager, ql_x86_register_cs, ql_x86_register_ds_ss_es
from qiling.arch.x86_const import UC_X86_INS_SYSCALL
from qiling.arch.x86_utils import GDTManager, SegmentManager64
from qiling.cc import intel
from qiling.const import QL_ARCH, QL_VERBOSE
from qiling.os.fcall import QlFunctionCall
@@ -23,7 +24,7 @@ class QlOsMacos(QlOsPosix):
super(QlOsMacos, self).__init__(ql)
self.ql = ql
self.fcall = QlFunctionCall(ql, intel.macosx64(ql))
self.fcall = QlFunctionCall(ql, intel.macosx64(ql.arch))
self.ql.counter = 0
self.ev_manager = QlMacOSEvManager(self.ql)
@@ -54,59 +55,59 @@ class QlOsMacos(QlOsPosix):
self.ql.stack_push(0)
self.savedrip=0xffffff8000a163bd
self.ql.run(begin=self.ql.loader.kext_alloc)
self.kext_object = self.ql.reg.rax
self.kext_object = self.ql.arch.regs.rax
self.ql.log.debug("Created kext object at 0x%x" % self.kext_object)
self.ql.reg.rdi = self.kext_object
self.ql.reg.rsi = 0 # NULL option
self.ql.arch.regs.rdi = self.kext_object
self.ql.arch.regs.rsi = 0 # NULL option
self.savedrip=0xffffff8000a16020
self.ql.run(begin=self.ql.loader.kext_init)
if self.ql.reg.rax == 0:
if self.ql.arch.regs.rax == 0:
self.ql.log.debug("Failed to initialize kext object")
return
self.ql.log.debug("Initialized kext object")
self.ql.reg.rdi = self.kext_object
self.ql.arch.regs.rdi = self.kext_object
# FIXME Determine provider for kext
self.ql.reg.rsi = 0 # ?
self.ql.arch.regs.rsi = 0 # ?
self.savedrip=0xffffff8000a16102
self.ql.run(begin=self.ql.loader.kext_attach)
if self.ql.reg.rax == 0:
if self.ql.arch.regs.rax == 0:
self.ql.log.debug("Failed to attach kext object")
return
self.ql.log.debug("Attached kext object 1st time")
self.ql.reg.rdi = self.kext_object
self.ql.reg.rdi = 0
self.ql.arch.regs.rdi = self.kext_object
self.ql.arch.regs.rdi = 0
# FIXME Determine provider for kext
self.ql.reg.rsi = 0 # ?
self.ql.arch.regs.rsi = 0 # ?
tmp = self.heap.alloc(8)
self.ql.reg.rdx = tmp
self.ql.arch.regs.rdx = tmp
self.savedrip=0xffffff8000a16184
self.ql.run(begin=self.ql.loader.kext_probe)
self.heap.free(tmp)
self.ql.log.debug("Probed kext object")
self.ql.reg.rdi = self.kext_object
self.ql.arch.regs.rdi = self.kext_object
# FIXME Determine provider for kext
self.ql.reg.rsi = 0 # ?
self.ql.arch.regs.rsi = 0 # ?
self.savedrip=0xffffff8000a16198
self.ql.run(begin=self.ql.loader.kext_detach)
self.ql.log.debug("Detached kext object")
self.ql.reg.rdi = self.kext_object
self.ql.arch.regs.rdi = self.kext_object
# FIXME Determine provider for kext
self.ql.reg.rsi = 0 # ?
self.ql.arch.regs.rsi = 0 # ?
self.savedrip=0xffffff8000a168a3
self.ql.run(begin=self.ql.loader.kext_attach)
if self.ql.reg.rax == 0:
if self.ql.arch.regs.rax == 0:
self.ql.log.debug("Failed to attach kext object")
return
self.ql.log.debug("Attached kext object 2nd time")
self.ql.reg.rdi = self.kext_object
self.ql.arch.regs.rdi = self.kext_object
# FIXME Determine provider for kext
self.ql.reg.rsi = 0 # ?
self.ql.arch.regs.rsi = 0 # ?
self.savedrip=0xffffff8000a168ed
self.ql.run(begin=self.ql.loader.kext_start)
else:
@@ -131,8 +132,8 @@ class QlOsMacos(QlOsPosix):
kmod_info.updateToMem()
self.ql.log.debug("Initialized kmod_info")
self.ql.reg.rdi = kmod_info_addr
self.ql.reg.rsi = 0
self.ql.arch.regs.rdi = kmod_info_addr
self.ql.arch.regs.rsi = 0
self.savedrip=0xffffff80009c2c16
self.ql.run(begin=self.ql.loader.kext_start)
@@ -141,16 +142,19 @@ class QlOsMacos(QlOsPosix):
if self.ql.code:
return
if self.ql.archtype== QL_ARCH.ARM64:
if self.ql.arch.type == QL_ARCH.ARM64:
self.ql.arch.enable_vfp()
self.ql.hook_intno(self.hook_syscall, 2)
self.ql.hook_intno(self.hook_sigtrap, 7)
elif self.ql.archtype== QL_ARCH.X8664:
elif self.ql.arch.type == QL_ARCH.X8664:
gdtm = GDTManager(self.ql)
# setup gdt and segments selectors
segm = SegmentManager64(self.ql.arch, gdtm)
segm.setup_cs_ds_ss_es(0, 4 << 30)
self.ql.hook_insn(self.hook_syscall, UC_X86_INS_SYSCALL)
self.gdtm = GDTManager(self.ql)
ql_x86_register_cs(self)
ql_x86_register_ds_ss_es(self)
def hook_syscall(self, intno= None, int = None):
@@ -179,7 +183,7 @@ class QlOsMacos(QlOsPosix):
"""
self.ql.stack_push(self.savedrip)
def callback_ret(ql):
ql.reg.arch_pc = 0
ql.arch.regs.arch_pc = 0
if self.savedrip not in self.hook_ret:
tmp = self.ql.hook_address(callback_ret, self.savedrip)
+2 -4
View File
@@ -9,15 +9,13 @@ from qiling.const import QL_ARCH
from qiling.os.posix.posix import SYSCALL_PREF
def map_syscall(ql, syscall_num):
if ql.archtype == QL_ARCH.X8664:
if ql.arch.type == QL_ARCH.X8664:
if syscall_num >= 0x2000000 and syscall_num <= 0x3000000:
syscall_num = syscall_num - 0x2000000
print("")
print(syscall_num)
return f'{SYSCALL_PREF}{x8664_syscall_table[syscall_num]}'
elif ql.archtype == QL_ARCH.ARM64:
elif ql.arch.type == QL_ARCH.ARM64:
if syscall_num >= 0xffffffffffffff00:
syscall_num = syscall_num - 0xffffffffffffff00
+7 -8
View File
@@ -204,10 +204,12 @@ def ql_syscall_pread(ql, fd, buf, nbyte, offset, *args, **kw):
ql.log.debug("pread(fd: 0x%x, buf: 0x%x, nbyte: 0x%x, offset: 0x%x)" % (
fd, buf, nbyte, offset
))
if fd >= 0 and fd <= MAX_FD_SIZE:
if fd in range(MAX_FD_SIZE + 1):
ql.os.fd[fd].seek(offset)
data = ql.os.fd[fd].read(nbyte)
ql.mem.write(buf, data)
set_eflags_cf(ql, 0x0)
return nbyte
@@ -299,7 +301,7 @@ def ql_syscall_shared_region_check_np(ql, p, uap, retvalp, *args, **kw):
# 0x150
def ql_syscall_proc_info(ql, callnum, pid, flavor, arg, buff, buffer_size):
retval = struct.unpack("<Q", ql.mem.read(ql.reg.rsp, 8))[0]
retval = struct.unpack("<Q", ql.mem.read(ql.arch.regs.rsp, 8))[0]
ql.log.debug("proc_info(callnum: 0x%x, pid: %d, flavor:0x%x, arg: 0x%x, buffer: 0x%x, buffersize: 0x%x, retval: 0x%x)" % (
callnum, pid, flavor, arg, buff, buffer_size, retval
))
@@ -358,16 +360,13 @@ def ql_syscall_open_nocancel(ql, filename, flags, mode, *args, **kw):
flags = flags & 0xffffffff
mode = mode & 0xffffffff
for i in range(256):
if ql.os.fd[i] == 0:
idx = i
break
idx = next((i for i in range(MAX_FD_SIZE + 1) if ql.os.fd[i] is None), -1)
if idx == -1:
regreturn = -1
else:
try:
if ql.archtype== QL_ARCH.ARM:
if ql.arch.type == QL_ARCH.ARM:
mode = 0
ql.os.fd[idx] = ql.os.fs_mapper.open_ql_file(path, flags, mode)
@@ -431,5 +430,5 @@ def ql_syscall_abort_with_payload(ql, reason_namespace, reason_code, payload, pa
# thread_set_tsd_base
def ql_syscall_thread_fast_set_cthread_self64(ql, u_info_addr, *args, **kw):
ql.log.debug("[mdep] thread fast set cthread self64(tsd_base:0x%x)" % (u_info_addr))
ql.reg.msr(GSMSR, u_info_addr)
ql.arch.msr.write(IA32_GS_BASE_MSR, u_info_addr)
return KERN_SUCCESS
+14 -14
View File
@@ -82,14 +82,14 @@ def IOConnectCallMethod(ql, selector,
ql.log.debug("Initialized IOExternalMethodArguments object")
ql.os.savedrip=0xffffff8000a106ba
ql.run(begin=ql.loader.user_alloc)
ql.os.user_object = ql.reg.rax
ql.os.user_object = ql.arch.regs.rax
ql.log.debug("Created user object at 0x%x" % ql.os.user_object)
ql.reg.rdi = ql.os.user_object
ql.reg.rsi = 0x1337 # owningTask
ql.reg.rdx = 0 # securityID
ql.reg.rcx = 0 # type
ql.reg.r8 = 0 # properties
ql.arch.regs.rdi = ql.os.user_object
ql.arch.regs.rsi = 0x1337 # owningTask
ql.arch.regs.rdx = 0 # securityID
ql.arch.regs.rcx = 0 # type
ql.arch.regs.r8 = 0 # properties
ql.stack_push(0)
ql.os.savedrip=0xffffff8000a10728
ql.run(begin=ql.loader.user_initWithTask)
@@ -97,12 +97,12 @@ def IOConnectCallMethod(ql, selector,
# TODO: Add some extra methods with correct order
ql.reg.rdi = ql.os.user_object
ql.reg.rsi = selector
ql.reg.rdx = args_addr
ql.reg.rcx = dispatch_addr
ql.reg.r8 = ql.os.kext_object
ql.reg.r9 = 0
ql.arch.regs.rdi = ql.os.user_object
ql.arch.regs.rsi = selector
ql.arch.regs.rdx = args_addr
ql.arch.regs.rcx = dispatch_addr
ql.arch.regs.r8 = ql.os.kext_object
ql.arch.regs.r9 = 0
ql.os.savedrip=0xffffff8000a6e9c7
ql.run(begin=ql.loader.user_externalMethod)
@@ -188,5 +188,5 @@ def page_align_end(addr, page_size):
def set_eflags_cf(ql, target_cf):
ql.reg.ef = ( ql.reg.ef & 0xfffffffe ) | target_cf
return ql.reg.ef
ql.arch.regs.ef = ( ql.arch.regs.ef & 0xfffffffe ) | target_cf
return ql.arch.regs.ef
+64 -54
View File
@@ -4,11 +4,11 @@
#
import inspect
import os
from typing import Any, MutableMapping, Union
from .path import QlPathManager
from .filestruct import ql_file
from .utils import QlOsUtils
# All mapped objects should inherit this class.
# Note this object is compatible with ql_file.
@@ -58,73 +58,83 @@ class QlFsMappedObject:
raise NotImplementedError("QlFsMappedObject property not implemented: name")
class QlFsMapper:
def __init__(self, ql):
self._mapping = {}
self._ql = ql
@property
def ql(self):
return self._ql
def _open_mapping_ql_file(self, ql_path, openflags, openmode):
real_dest = self._mapping[ql_path]
if isinstance(real_dest, str):
qlfile = ql_file.open(real_dest, openflags, openmode)
return qlfile
elif inspect.isclass(real_dest):
new_instance = real_dest()
return new_instance
else:
return real_dest
def _open_mapping(self, ql_path, openmode):
real_dest = self._mapping[ql_path]
if isinstance(real_dest, str):
f = open(real_dest, openmode)
return f
elif inspect.isclass(real_dest):
new_instance = real_dest()
return new_instance
else:
return real_dest
def __init__(self, path: QlPathManager):
self._mapping: MutableMapping[str, Any] = {}
self.path = path
def has_mapping(self, fm):
def _open_mapping_ql_file(self, ql_path: str, openflags: int, openmode: int):
real_dest = self._mapping[ql_path]
if isinstance(real_dest, str):
obj = ql_file.open(real_dest, openflags, openmode)
elif inspect.isclass(real_dest):
obj = real_dest()
else:
obj = real_dest
return obj
def _open_mapping(self, ql_path: str, openmode: str):
real_dest = self._mapping[ql_path]
if isinstance(real_dest, str):
obj = open(real_dest, openmode)
elif inspect.isclass(real_dest):
obj = real_dest()
else:
obj = real_dest
return obj
def has_mapping(self, fm: str) -> bool:
return fm in self._mapping
def mapping_count(self):
def mapping_count(self) -> int:
return len(self._mapping)
def open_ql_file(self, path, openflags, openmode):
def open_ql_file(self, path: str, openflags: int, openmode: int):
if self.has_mapping(path):
self.ql.log.info(f"mapping {path}")
return self._open_mapping_ql_file(path, openflags, openmode)
else:
real_path = self.ql.os.path.transform_to_real_path(path)
return ql_file.open(real_path, openflags, openmode)
def open(self, path, openmode):
real_path = self.path.transform_to_real_path(path)
return ql_file.open(real_path, openflags, openmode)
def open(self, path: str, openmode: str):
if self.has_mapping(path):
self.ql.log.info(f"mapping {path}")
return self._open_mapping(path, openmode)
else:
real_path = self.ql.os.path.transform_to_real_path(path)
return open(real_path, openmode)
def _parse_path(self, p):
if "__fspath__" in dir(p): # p is a os.PathLike object.
p = p.__fspath__()
real_path = self.path.transform_to_real_path(path)
return open(real_path, openmode)
def _parse_path(self, p: Union[os.PathLike, str]) -> str:
fspath = getattr(p, '__fspath__', None)
# p is an `os.PathLike` object
if fspath is not None:
p = fspath()
if isinstance(p, bytes): # os.PathLike.__fspath__ may return bytes.
p = p.decode("utf-8")
return p
# ql_path: Emulated path which should be convertable to a string or a hashable object. e.g. pathlib.Path
# real_dest: Mapped object, can be a string, an object or a class.
# string: mapped path in the host machine, e.g. `/dev/urandom` -> `/dev/urandom`.
# object: mapped object, will be returned each time the emulated path has been opened.
# class: mapped class, will be used to create a new instance each time the emulated path has been opened.
def add_fs_mapping(self, ql_path, real_dest):
def add_fs_mapping(self, ql_path: Union[os.PathLike, str], real_dest: Any) -> None:
"""Map an object to Qiling emulated file system.
Args:
ql_path: Emulated path which should be convertable to a string or a hashable object. e.g. pathlib.Path
real_dest: Mapped object, can be a string, an object or a class.
string: mapped path in the host machine, e.g. '/dev/urandom' -> '/dev/urandom'
object: mapped object, will be returned each time the emulated path has been opened
class: mapped class, will be used to create a new instance each time the emulated path has been opened
"""
ql_path = self._parse_path(ql_path)
real_dest = self._parse_path(real_dest)
self._mapping[ql_path] = real_dest
+10 -10
View File
@@ -13,10 +13,6 @@ class QlOsMcu(QlOs):
self.runable = True
def step(self):
self.ql.emu_start(self.ql.arch.get_pc(), 0, count=1)
self.ql.hw.step()
def stop(self):
self.ql.emu_stop()
self.runable = False
@@ -27,13 +23,17 @@ class QlOsMcu(QlOs):
count = self.ql.count
end = self.ql.exit_point if self.ql.exit_point is not None else -1
if isinstance(self.ql.arch, QlArchARM):
end |= self.ql.arch.thumb
while self.runable and \
self.ql.arch.get_pc() != end:
while self.runable:
current_address = self.ql.arch.regs.arch_pc
if isinstance(self.ql.arch, QlArchARM):
current_address |= int(self.ql.arch.is_thumb)
if current_address == end:
break
self.ql.emu_start(current_address, 0, count=1)
self.ql.hw.step()
self.step()
count -= 1
if count == 0:
+32 -6
View File
@@ -35,10 +35,10 @@ class QlMemoryManager:
16 : (1 << 20) - 1 # 20bit address line
}
if ql.archbit not in bit_stuff:
if ql.arch.bits not in bit_stuff:
raise QlErrorStructConversion("Unsupported Qiling archtecture for memory manager")
max_addr = bit_stuff[ql.archbit]
max_addr = bit_stuff[ql.arch.bits]
self.max_addr = max_addr
self.max_mem_addr = max_addr
@@ -288,6 +288,7 @@ class QlMemoryManager:
def read_ptr(self, addr: int, size: int=None) -> int:
"""Read an integer value from a memory address.
Bytes read will be unpacked using emulated architecture properties.
Args:
addr: memory address to read
@@ -297,7 +298,7 @@ class QlMemoryManager:
"""
if not size:
size = self.ql.pointersize
size = self.ql.arch.pointersize
__unpack = {
1 : self.ql.unpack8,
@@ -306,10 +307,10 @@ class QlMemoryManager:
8 : self.ql.unpack64
}.get(size)
if __unpack:
return __unpack(self.read(addr, size))
if __unpack is None:
raise QlErrorStructConversion(f"Unsupported pointer size: {size}")
raise QlErrorStructConversion(f"Unsupported pointer size: {size}")
return __unpack(self.read(addr, size))
def write(self, addr: int, data: bytes) -> None:
"""Write bytes to a memory.
@@ -321,6 +322,31 @@ class QlMemoryManager:
self.ql.uc.mem_write(addr, data)
def write_ptr(self, addr: int, value: int, size: int=None) -> None:
"""Write an integer value to a memory address.
Bytes written will be packed using emulated architecture properties.
Args:
addr: target memory address
value: integer value to write
size: pointer size (in bytes): either 1, 2, 4, 8, or None for arch native size
"""
if not size:
size = self.ql.arch.pointersize
__pack = {
1 : self.ql.pack8,
2 : self.ql.pack16,
4 : self.ql.pack32,
8 : self.ql.pack64
}.get(size)
if __pack is None:
raise QlErrorStructConversion(f"Unsupported pointer size: {size}")
self.write(addr, __pack(value))
def search(self, needle: bytes, begin: int = None, end: int = None) -> Sequence[int]:
"""Search for a sequence of bytes in memory.

Some files were not shown because too many files have changed in this diff Show More