mirror of
https://github.com/qilingframework/qiling.git
synced 2026-10-12 02:21:40 +00:00
Merge branch 'dev' into next
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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'))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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,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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = {}
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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())
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 +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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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
@@ -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
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
Reference in New Issue
Block a user