Skip to content

Commit 31c79b0

Browse files
committed
new stepping algorithm for qdb
1 parent 978299a commit 31c79b0

File tree

3 files changed

+56
-52
lines changed

3 files changed

+56
-52
lines changed

qiling/debugger/qdb/qdb.py

Lines changed: 49 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#
55

66
from __future__ import annotations
7-
from typing import Callable, Optional, Mapping, Tuple
7+
from typing import Callable, Optional, Mapping, Tuple, Union
88

99
import cmd
1010

@@ -24,7 +24,6 @@ def __init__(self: QlQdb, ql: Qiling, init_hook: str = "", rr: bool = False) ->
2424

2525
self.ql = ql
2626
self.prompt = f"{color.BOLD}{color.RED}Qdb> {color.END}"
27-
self.breakpoints = {}
2827
self._saved_reg_dump = None
2928
self.bp_list = {}
3029
self.rr = rr
@@ -34,17 +33,10 @@ def __init__(self: QlQdb, ql: Qiling, init_hook: str = "", rr: bool = False) ->
3433

3534
super().__init__()
3635

37-
# setup a breakpoint at entry point or user specified address
38-
address = self.ql.loader.entry_point if not init_hook else parse_int(init_hook)
39-
self.set_breakpoint(address, is_temp=True)
40-
self.dbg_hook()
36+
self.cur_addr = self.ql.loader.entry_point
4137

42-
def dbg_hook(self: QlQdb):
43-
"""
44-
hook every instruction with callback funtion _bp_handler
45-
"""
46-
47-
self.ql.hook_code(self._bp_handler)
38+
self.do_start()
39+
self.interactive()
4840

4941
@property
5042
def cur_addr(self: QlQdb) -> int:
@@ -67,16 +59,20 @@ def _bp_handler(self: QlQdb, *args) -> None:
6759
internal function for handling once breakpoint hitted
6860
"""
6961

70-
if (bp := self.bp_list.get(self.cur_addr, None)) is not None:
62+
if (bp := self.bp_list.get(self.cur_addr, None)):
7163

72-
if not isinstance(bp, TempBreakpoint):
73-
print(f"{color.CYAN}[+] hit breakpoint at 0x{self.cur_addr:08x}{color.END}")
64+
if isinstance(bp, TempBreakpoint):
65+
# remove TempBreakpoint once hitted
66+
self.del_breakpoint(bp)
7467

7568
else:
76-
# remove TempBreakpoint once hitted
77-
self.del_breakpoint(self.cur_addr)
69+
if bp.hitted:
70+
return
7871

79-
self.interactive()
72+
print(f"{color.CYAN}[+] hit breakpoint at 0x{self.cur_addr:08x}{color.END}")
73+
bp.hitted = True
74+
75+
self.do_context()
8076

8177
def _save(self: QlQdb, *args) -> None:
8278
"""
@@ -92,12 +88,18 @@ def _restore(self: QlQdb, *args) -> None:
9288

9389
self.ql.restore(self._states_list.pop())
9490

95-
def _run(self: Qldbg, *args) -> None:
91+
def _run(self: Qldbg, address: int = 0, count: int = 0) -> None:
9692
"""
97-
internal function for launching qiling instance
93+
internal function for emulating instruction
9894
"""
9995

100-
self.ql.run()
96+
if not address:
97+
address = self.cur_addr
98+
99+
if self.ql.archtype in (QL_ARCH.ARM, QL_ARCH.ARM_THUMB) and is_thumb(self.ql.reg.cpsr):
100+
address |= 1
101+
102+
self.ql.emu_start(address, 0, count=count)
101103

102104
def parseline(self: QlQdb, line: str) -> Tuple[Optional[str], Optional[str], str]:
103105
"""
@@ -126,16 +128,14 @@ def interactive(self: QlQdb, *args) -> None:
126128
initial an interactive interface
127129
"""
128130

129-
self.do_context()
130-
131131
return self.cmdloop()
132132

133133
def run(self: QlQdb, *args) -> None:
134134
"""
135-
do nothing, since it's already running when breakpoint hitted
135+
internal command for running debugger
136136
"""
137137

138-
pass
138+
self._run()
139139

140140
def emptyline(self: QlQdb, *args) -> None:
141141
"""
@@ -147,18 +147,9 @@ def emptyline(self: QlQdb, *args) -> None:
147147

148148
def do_run(self: QlQdb, *args) -> None:
149149
"""
150-
launch qiling instance from a fresh start
150+
launching qiling instance
151151
"""
152152

153-
self.ql = Qiling(
154-
argv=self.ql.argv,
155-
rootfs=self.ql.rootfs,
156-
verbose=self.ql.verbose,
157-
console=self.ql.console,
158-
log_file=self.ql.log_file,
159-
)
160-
161-
self.dbg_hook()
162153
self._run()
163154

164155
def do_context(self: QlQdb, *args) -> None:
@@ -198,45 +189,54 @@ def do_step(self: QlQdb, *args) -> Optional[bool, None]:
198189
if next_stop is CODE_END:
199190
return True
200191

201-
self.bp_list.update({next_stop: TempBreakpoint()})
202-
203192
if self.rr:
204193
self._save()
205194

206-
return True
195+
count = 1
196+
if self.ql.archtype == QL_ARCH.MIPS and next_stop != self.cur_addr + 4:
197+
# make sure delay slot executed
198+
count = 2
199+
200+
self._run(count=count)
201+
self.do_context()
207202

208203
def set_breakpoint(self: QlQdb, address: int, is_temp: bool = False) -> None:
209204
"""
210205
internal function for placing breakpoints
211206
"""
212207

213-
bp = TempBreakpoint() if is_temp else Breakpoint()
208+
bp = TempBreakpoint(address) if is_temp else Breakpoint(address)
209+
210+
bp.hook = self.ql.hook_address(self._bp_handler, address)
214211

215212
self.bp_list.update({address: bp})
216213

217-
def del_breakpoint(self: QlQdb, address: int) -> None:
214+
def del_breakpoint(self: QlQdb, bp: Union[Breakpoint, TempBreakpoint]) -> None:
218215
"""
219216
internal function for removing breakpoints
220217
"""
221218

222-
self.bp_list.pop(address, None)
219+
if self.bp_list.pop(bp.addr, None):
220+
bp.hook.remove()
223221

224222
def do_start(self: QlQdb, address: str = "", *args) -> None:
225223
"""
226-
pause at entry point by setting a temporary breakpoint on it
224+
move current context to ql.loader.entry_point
227225
"""
228226

229-
# entry = self.ql.loader.entry_point # ld.so
230-
# entry = self.ql.loader.elf_entry # .text of binary
227+
self.cur_addr = self.ql.loader.entry_point # ld.so
228+
# self.cur_addr = self.ql.loader.elf_entry # .text of binary
231229

232-
self._run()
230+
# need a proper method for this
231+
# self.ql.restore(self._init_state)
232+
233+
self.do_context()
233234

234235
def do_breakpoint(self: QlQdb, address: str = "") -> None:
235236
"""
236237
set breakpoint on specific address
237238
"""
238239

239-
# address = parse_int(address) if address else self.ql.reg.arch_pc
240240
address = parse_int(address) if address else self.cur_addr
241241

242242
self.set_breakpoint(address)
@@ -249,10 +249,10 @@ def do_continue(self: QlQdb, address: str = "") -> None:
249249
"""
250250

251251
if address:
252-
self.cur_addr = parse_int(address)
252+
address = parse_int(address)
253253

254254
print(f"{color.CYAN}continued from 0x{self.cur_addr:08x}{color.END}")
255-
return True
255+
self._run(address)
256256

257257
def do_examine(self: QlQdb, line: str) -> None:
258258
"""
@@ -302,7 +302,7 @@ def do_quit(self: QlQdb, *args) -> bool:
302302
"""
303303

304304
self.ql.stop()
305-
return True
305+
exit()
306306

307307
do_r = do_run
308308
do_s = do_step

qiling/debugger/qdb/utils.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -417,13 +417,17 @@ class Breakpoint(object):
417417
"""
418418
dummy class for breakpoint
419419
"""
420-
pass
420+
def __init__(self, address: int):
421+
self.addr = address
422+
self.hitted = False
423+
self.hook = None
421424

422425
class TempBreakpoint(Breakpoint):
423426
"""
424427
dummy class for temporay breakpoint
425428
"""
426-
pass
429+
def __init__(self, address):
430+
super().__init__(address)
427431

428432

429433
if __name__ == "__main__":

qltool

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def handle_run(options: argparse.Namespace):
124124

125125
# attach Qdb at entry point
126126
if options.qdb is True:
127-
QlQdb(ql, rr=options.rr).do_start()
127+
QlQdb(ql, rr=options.rr).run()
128128
exit()
129129

130130
return ql

0 commit comments

Comments
 (0)