Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 102 additions & 37 deletions qiling/debugger/qdb/frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,63 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#

import math, copy, os
from __future__ import annotations
from typing import Optional, Mapping, Iterable, Union

import copy, math, os
from contextlib import contextmanager

from qiling.const import QL_ARCH
from .utils import dump_regs, get_arm_flags, disasm
from .const import color

from .utils import dump_regs, get_arm_flags, disasm, parse_int
from .const import *


# read data from memory of qiling instance
def examine_mem(ql, addr, fmt):
def examine_mem(ql: Qiling, line: str) -> Union[bool, (str, int, int)]:

_args = line.split()
DEFAULT_FMT = ('x', 4, 1)

if line.startswith("/"): # followed by format letter and size letter

def get_fmt(text):
def extract_count(t):
return "".join([s for s in t if s.isdigit()])

f, s, c = DEFAULT_FMT
if extract_count(text):
c = int(extract_count(text))

for char in text.strip(str(c)):
if char in SIZE_LETTER.keys():
s = SIZE_LETTER.get(char)

elif char in FORMAT_LETTER:
f = char

return (f, s, c)

fmt, addr = line.strip("/").split()

fmt = get_fmt(fmt)

elif len(_args) == 1: # only address
addr = _args[0]
fmt = DEFAULT_FMT

else:
return False

addr = addr.strip('$')

if ql.archtype in (QL_ARCH.ARM, QL_ARCH.ARM_THUMB):
addr = addr.replace("fp", "r11")

elif ql.archtype == QL_ARCH.MIPS:
addr = addr.replace("fp", "s8")

addr = getattr(ql.reg, addr) if addr in ql.reg.register_mapping.keys() else parse_int(addr)

def unpack(bs, sz):
return {
Expand All @@ -29,7 +76,7 @@ def unpack(bs, sz):
for offset in range(addr, addr+ct*4, 4):
line = disasm(ql, offset)
if line:
print("0x{:x}: {}\t{}".format(line.address, line.mnemonic, line.op_str))
print(f"0x{line.address:x}: {line.mnemonic}\t{line.op_str}")

print()

Expand All @@ -40,27 +87,29 @@ def unpack(bs, sz):

for line in range(lines):
offset = line * sz * 4
print("0x{:x}:\t".format(addr+offset), end="")
print("0x{addr+offset:x}:\t", end="")

idx = line * 4
for each in mem_read[idx:idx+4]:
idx = line * ql.pointersize
for each in mem_read[idx:idx+ql.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 ''
ft = ft.lower() if ft in ("x", "o", "b", "d") else ft.lower().replace("t", "b").replace("a", "x")

print("{}{{:{}{}}}\t".format(prefix, pad, ft).format(data), end="")
print(f"{prefix}{data:{pad}{ft}}\t", end="")

print()

return True


# get terminal window height and width
def get_terminal_size():
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, address, size):
def _try_read(ql: Qiling, address: int, size: int) -> Optional[bytes]:
try:
result = ql.mem.read(address, size)
except:
Expand All @@ -71,14 +120,14 @@ def _try_read(ql, address, size):

# divider printer
@contextmanager
def context_printer(ql, field_name, ruler="="):
def context_printer(ql: Qiling, field_name: str, ruler: str = "=") -> None:
_height, _width = get_terminal_size()
print(field_name, ruler * (_width - len(field_name) - 1))
yield
print(ruler * _width)


def context_reg(ql, saved_states=None, *args, **kwargs):
def context_reg(ql: Qiling, saved_states: Optional[Mapping[str, int]] = None, /, *args, **kwargs) -> None:

# context render for registers
with context_printer(ql, "[Registers]"):
Expand All @@ -104,7 +153,7 @@ def context_reg(ql, saved_states=None, *args, **kwargs):
line = "{}{}: 0x{{:08x}} {}\t".format(_colors[(idx-1) // 4], r, color.END)

if _diff and r in _diff:
line = "{}{}".format(color.UNDERLINE, color.BOLD) + line
line = f"{color.UNDERLINE}{color.BOLD}{line}"

if idx % 4 == 0 and idx != 32:
line += "\n"
Expand Down Expand Up @@ -152,45 +201,61 @@ def context_reg(ql, saved_states=None, *args, **kwargs):
_val = ql.mem.read(_addr, ql.pointersize)
print(f"$sp+0x{idx*4:02x}|[0x{_addr:08x}]=> 0x{ql.unpack(_val):08x}", end="")

try: # try to deference wether its a pointer
_deref = ql.mem.read(_addr, 4)
try: # try to deference wether its a pointer
_deref = ql.mem.read(_addr, ql.pointersize)
except:
_deref = None

if _deref:
print(f" => 0x{ql.unpack(_deref):08x}")


def print_asm(ql, instructions):
for ins in instructions:
fmt = (ins.address, ins.mnemonic.ljust(6), ins.op_str)
if ql.reg.arch_pc == ins.address:
print(f"PC ==> 0x{fmt[0]:x}\t{fmt[1]} {fmt[2]}")
else:
print(f"\t0x{fmt[0]:x}\t{fmt[1]} {fmt[2]}")
def print_asm(ql: Qiling, ins: CsInsn) -> None:
fmt = (ins.address, ins.mnemonic.ljust(6), ins.op_str)
if ql.reg.arch_pc == ins.address:
print(f"PC ==> 0x{fmt[0]:x}\t{fmt[1]} {fmt[2]}")
else:
print(f"\t0x{fmt[0]:x}\t{fmt[1]} {fmt[2]}")


def context_asm(ql, address, size, *args, **kwargs):
def context_asm(ql: Qiling, address: int) -> None:

with context_printer(ql, field_name="[Code]"):
md = ql.create_disassembler()

# assembly before current location

pre_tmp = _try_read(ql, address-0x10, 0x10)
if pre_tmp:
pre_ins = md.disasm(pre_tmp, address-0x10)
print_asm(ql, pre_ins)
past_list = []

if ql.archtype in (QL_ARCH.MIPS, QL_ARCH.ARM, QL_ARCH.ARM_THUMB):

line = disasm(ql, address-0x10)

while line:
if line.address == address:
break

addr = line.address + line.size
line = disasm(ql, addr)

if not line:
break

past_list.append(line)

# print four insns before current location
for line in past_list[:-1][:4]:
print_asm(ql, line)

# assembly for current locaton
# assembly for current location

tmp = ql.mem.read(address, size)
cur_ins = md.disasm(tmp, address)
cur_ins = disasm(ql, address)
print_asm(ql, cur_ins)

# assembly after current locaton
# assembly after current location

pos_tmp = _try_read(ql, address+4, 0x10)
if pos_tmp:
pos_ins = md.disasm(pos_tmp, address+4)
print_asm(ql, pos_ins)
forward_insn_size = cur_ins.size
for _ in range(5):
forward_insn = disasm(ql, address+forward_insn_size)
if forward_insn:
print_asm(ql, forward_insn)
forward_insn_size += forward_insn.size
Loading