Skip to content

Commit 717241a

Browse files
authored
Merge pull request #1226 from ucgJhe/qdb
Qdb improvments: Mark, Jump and modify register value in qdb
2 parents 5eaea98 + 0f22094 commit 717241a

File tree

5 files changed

+176
-12
lines changed

5 files changed

+176
-12
lines changed

qiling/debugger/qdb/arch/arch.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55

66

77
from qiling.const import QL_ARCH
8+
from unicorn import UC_ERR_READ_UNMAPPED
9+
import unicorn
10+
811

912
class Arch:
1013
"""
@@ -23,4 +26,9 @@ def archbit(self):
2326
return 4
2427

2528
def read_insn(self, address: int):
26-
return self.read_mem(address, self.arch_insn_size)
29+
try:
30+
result = self.read_mem(address, self.arch_insn_size)
31+
except unicorn.unicorn.UcError as err:
32+
result = None
33+
34+
return result

qiling/debugger/qdb/context.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ def disasm(self, address: int, detail: bool = False) -> Optional[CsInsn]:
4848
md = self.ql.arch.disassembler
4949
md.detail = detail
5050

51-
return next(md.disasm(self.read_insn(address), address), None)
51+
if (addr := self.read_insn(address)):
52+
return next(md.disasm(addr, address), None)
53+
return None
5254

5355
def try_read(self, address: int, size: int) -> Optional[bytes]:
5456
"""

qiling/debugger/qdb/misc.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
44
#
55

6-
from typing import Callable, Optional
6+
from typing import AnyStr, Callable, Optional
77

88
import ast
99

@@ -50,17 +50,27 @@ def read_int(s: str) -> int:
5050
return int(s, 0)
5151

5252

53+
def try_read_int(s: AnyStr) -> Optional[int]:
54+
"""
55+
try to read string as integer is possible
56+
"""
57+
try:
58+
ret = read_int(s)
59+
except:
60+
ret = None
61+
62+
return ret
63+
64+
5365
def parse_int(func: Callable) -> Callable:
5466
"""
5567
function dectorator for parsing argument as integer
5668
"""
5769
def wrap(qdb, s: str = "") -> int:
5870
assert type(s) is str
59-
try:
60-
ret = read_int(s)
61-
except:
62-
ret = None
71+
ret = try_read_int(s)
6372
return func(qdb, ret)
73+
6474
return wrap
6575

6676

qiling/debugger/qdb/qdb.py

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
import cmd
99

1010
from qiling import Qiling
11-
from qiling.const import QL_ARCH, QL_VERBOSE
11+
from qiling.const import QL_OS, QL_ARCH, QL_VERBOSE
1212
from qiling.debugger import QlDebugger
1313

14-
from .utils import setup_context_render, setup_branch_predictor, SnapshotManager, run_qdb_script
14+
from .utils import setup_context_render, setup_branch_predictor, setup_address_marker, SnapshotManager, run_qdb_script
1515
from .memory import setup_memory_Manager
16-
from .misc import parse_int, Breakpoint, TempBreakpoint
16+
from .misc import parse_int, Breakpoint, TempBreakpoint, try_read_int
1717
from .const import color
1818

1919
from .utils import QDB_MSG, qdb_print
@@ -34,6 +34,7 @@ def __init__(self, ql: Qiling, init_hook: str = "", rr: bool = False, script: st
3434
self._saved_reg_dump = None
3535
self._script = script
3636
self.bp_list = {}
37+
self.marker = setup_address_marker()
3738

3839
self.rr = SnapshotManager(ql) if rr else None
3940
self.mm = setup_memory_Manager(ql)
@@ -72,7 +73,10 @@ def bp_handler(ql, address, size, bp_list):
7273

7374
self.ql.hook_code(bp_handler, self.bp_list)
7475

75-
if init_hook and self.ql.loader.entry_point != init_hook:
76+
if self.ql.os.type == QL_OS.BLOB:
77+
self.ql.loader.entry_point = self.ql.loader.load_address
78+
79+
elif init_hook and self.ql.loader.entry_point != init_hook:
7680
self.do_breakpoint(init_hook)
7781

7882
self.cur_addr = self.ql.loader.entry_point
@@ -319,6 +323,7 @@ def do_disassemble(self, address: Optional[int] = None) -> None:
319323
qdb_print(QDB_MSG.ERROR)
320324

321325
def do_examine(self, line: str) -> None:
326+
322327
"""
323328
Examine memory: x/FMT ADDRESS.
324329
format letter: o(octal), x(hex), d(decimal), u(unsigned decimal), t(binary), f(float), a(address), i(instruction), c(char), s(string) and z(hex, zero padded on the left)
@@ -328,6 +333,29 @@ def do_examine(self, line: str) -> None:
328333

329334
if type(err_msg := self.mm.parse(line)) is str:
330335
qdb_print(QDB_MSG.ERROR, err_msg)
336+
337+
338+
def do_set(self, line: str) -> None:
339+
"""
340+
set register value of current context
341+
"""
342+
# set $a = b
343+
344+
reg, val = line.split("=")
345+
reg_name = reg.strip().strip("$")
346+
reg_val = try_read_int(val.strip())
347+
348+
if reg_name in self.ql.arch.regs.save().keys():
349+
if reg_val:
350+
setattr(self.ql.arch.regs, reg_name, reg_val)
351+
self.do_context()
352+
qdb_print(QDB_MSG.INFO, f"set register {reg_name} to 0x{(reg_val & 0xfffffff):08x}")
353+
354+
else:
355+
qdb_print(QDB_MSG.ERROR, f"error parsing input: {reg_val} as integer value")
356+
357+
else:
358+
qdb_print(QDB_MSG.ERROR, f"invalid register: {reg_name}")
331359

332360
def do_start(self, *args) -> None:
333361
"""
@@ -348,6 +376,61 @@ def do_context(self, *args) -> None:
348376
self.render.context_stack()
349377
self.render.context_asm()
350378

379+
def do_jump(self, loc: str, *args) -> None:
380+
"""
381+
seek to where ever valid location you want
382+
"""
383+
384+
sym = self.marker.get_symbol(loc)
385+
addr = sym if sym is not None else try_read_int(loc)
386+
387+
# check validation of the address to be seeked
388+
if self.ql.mem.is_mapped(addr, 4):
389+
if sym:
390+
qdb_print(QDB_MSG.INFO, f"seek to {loc} @ 0x{addr:08x} ...")
391+
else:
392+
qdb_print(QDB_MSG.INFO, f"seek to 0x{addr:08x} ...")
393+
394+
self.cur_addr = addr
395+
self.do_context()
396+
397+
else:
398+
qdb_print(QDB_MSG.ERROR, f"the address to be seeked isn't mapped")
399+
400+
def do_mark(self, args=""):
401+
"""
402+
mark a user specified address as a symbol
403+
"""
404+
405+
args = args.split()
406+
if len(args) == 0:
407+
loc = self.cur_addr
408+
sym_name = self.marker.mark_only_loc(loc)
409+
410+
elif len(args) == 1:
411+
if (loc := try_read_int(args[0])):
412+
sym_name = self.marker.mark_only_loc(loc)
413+
414+
else:
415+
loc = self.cur_addr
416+
sym_name = args[0]
417+
if (err := self.marker.mark(sym_name, loc)):
418+
qdb_print(QDB_MSG.ERROR, err)
419+
return
420+
421+
elif len(args) == 2:
422+
sym_name, addr = args
423+
if (loc := try_read_int(addr)):
424+
self.marker.mark(sym_name, loc)
425+
else:
426+
qdb_print(QDB_MSG.ERROR, f"unable to mark symbol at address: '{addr}'")
427+
return
428+
else:
429+
qdb_print(QDB_MSG.ERROR, "symbol should not be empty ...")
430+
return
431+
432+
qdb_print(QDB_MSG.INFO, f"mark symbol '{sym_name}' at address: 0x{loc:08x} ...")
433+
351434
def do_show(self, *args) -> None:
352435
"""
353436
show some runtime information
@@ -357,6 +440,7 @@ def do_show(self, *args) -> None:
357440
self.ql.log.info(info_line)
358441

359442
qdb_print(QDB_MSG.INFO, f"Breakpoints: {[hex(addr) for addr in self.bp_list.keys()]}")
443+
qdb_print(QDB_MSG.INFO, f"Marked symbol: {[{key:hex(val)} for key,val in self.marker.mark_list]}")
360444
if self.rr:
361445
qdb_print(QDB_MSG.INFO, f"Snapshots: {len([st for st in self.rr.layers if isinstance(st, self.rr.DiffedState)])}")
362446

@@ -403,6 +487,8 @@ def do_EOF(self, *args) -> None:
403487
do_r = do_run
404488
do_s = do_step_in
405489
do_n = do_step_over
490+
do_j = do_jump
491+
do_m = do_mark
406492
do_q = do_quit
407493
do_x = do_examine
408494
do_p = do_backward

qiling/debugger/qdb/utils.py

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from qiling.const import QL_ARCH
1313

1414
from .context import Context
15-
from .misc import read_int
1615

1716
from .render import (
1817
ContextRenderX86,
@@ -50,6 +49,65 @@ def print_info(msg):
5049

5150
print(color_coated)
5251

52+
"""
53+
54+
class Marker provide the ability for marking an address as a more easier rememberable alias
55+
56+
"""
57+
58+
def setup_address_marker():
59+
60+
class Marker:
61+
def __init__(self):
62+
self._mark_list = {}
63+
64+
def get_symbol(self, sym):
65+
"""
66+
get the mapped address to a symbol if it's in the mark_list
67+
"""
68+
69+
return self._mark_list.get(sym, None)
70+
71+
@property
72+
def mark_list(self):
73+
"""
74+
get a list about what we marked
75+
"""
76+
77+
return self._mark_list.items()
78+
79+
def gen_sym_name(self):
80+
"""
81+
generating symbol name automatically
82+
"""
83+
84+
sym_name, idx = "sym0", 0
85+
while sym_name in self._mark_list:
86+
idx += 1
87+
sym_name = f"sym{idx}"
88+
89+
return sym_name
90+
91+
def mark_only_loc(self, loc):
92+
"""
93+
mark when location provided only
94+
"""
95+
96+
sym_name = self.gen_sym_name()
97+
self.mark(sym_name, loc)
98+
return sym_name
99+
100+
def mark(self, sym: str, loc: int):
101+
"""
102+
mark loc as sym
103+
"""
104+
105+
if sym not in self.mark_list:
106+
self._mark_list.update({sym: loc})
107+
else:
108+
return f"dumplicated symbol name: {sym} at address: 0x{loc:08x}"
109+
110+
return Marker()
53111

54112
"""
55113

0 commit comments

Comments
 (0)