Skip to content

Commit dd679e9

Browse files
committed
refactor(r2): add addr wrap and move wrap to utils
@wrap_arg_addr makes function accept name/R2Data as addr and return same func when args is empty rename: get_fcn_at -> get_fcn rename: get_bb_at -> get_bb
1 parent 7a1beb1 commit dd679e9

File tree

4 files changed

+54
-31
lines changed

4 files changed

+54
-31
lines changed

examples/extensions/r2/deflat_r2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
ql = R2Qiling(['rootfs/x86_linux/bin/test_fla_argv', '1'], 'rootfs/x86_linux', verbose=QL_VERBOSE.DEFAULT)
2020
r2 = ql.r2
2121
# now we can use r2 parsed symbol name instead of address
22-
fcn = r2.get_fcn_at(r2.where('target_function'))
22+
fcn = r2.get_fcn('target_function')
2323
print(fcn)
2424
r2.deflat(fcn)
2525
ql.run()

qiling/extensions/r2/deflat.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def _guide_hook(self, ql: Qiling, addr: int, size: int):
9393
ql.emu_stop()
9494
self.hook_data['result'] = False
9595
return
96-
cur_bb = self.r2.get_bb_at(addr)
96+
cur_bb = self.r2.get_bb(addr)
9797
if "force" in self.hook_data and addr in self.hook_data['force']:
9898
if self.hook_data['force'][addr]: # is True
9999
ql.log.info(f"Force execution at cond branch {hex(addr)}")
@@ -133,7 +133,7 @@ def _search_path(self):
133133
braddr = self._find_branch_in_block(bb)
134134
self.hook_data = {
135135
"startbb": bb,
136-
"func": self.r2.get_fcn_at(self.first_block.addr),
136+
"func": self.r2.get_fcn(self.first_block),
137137
"result": True,
138138
}
139139
ql_bb_start_ea = bb.addr

qiling/extensions/r2/r2.py

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88
import re
99
import libr
1010
from dataclasses import dataclass, field, fields
11-
from functools import cached_property, wraps
11+
from functools import cached_property
1212
from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Pattern, Tuple, Union
1313
from qiling.const import QL_ARCH
1414
from qiling.extensions import trace
1515
from unicorn import UC_PROT_NONE, UC_PROT_READ, UC_PROT_WRITE, UC_PROT_EXEC, UC_PROT_ALL
1616
from .callstack import CallStack
1717
from .deflat import R2Deflator
18+
from .utils import wrap_aaa, wrap_arg_addr
1819

1920
if TYPE_CHECKING:
2021
from qiling.extensions.r2 import R2Qiling
@@ -279,15 +280,6 @@ def _cmdj(self, cmd: str, r2c = None) -> Union[Dict, List[Dict]]:
279280
def offset(self) -> int:
280281
return self._r2c.contents.offset
281282

282-
def aaa(fun):
283-
@wraps(fun)
284-
def wrapper(self, *args, **kwargs):
285-
if self.analyzed is False:
286-
self._cmd("aaa")
287-
self.analyzed = True
288-
return fun(self, *args, **kwargs)
289-
return wrapper
290-
291283
@cached_property
292284
def binfo(self) -> Dict[str, str]:
293285
return self._cmdj("iIj")
@@ -316,46 +308,50 @@ def symbols(self) -> Dict[str, Symbol]:
316308
return {dic['name']: Symbol(**dic).vaddr for dic in sym_lst}
317309

318310
@cached_property
319-
@aaa
311+
@wrap_aaa
320312
def functions(self) -> Dict[str, Function]:
321313
fcn_lst = self._cmdj("aflj")
322314
return {dic['name']: Function(**dic) for dic in fcn_lst}
323315

324316
@cached_property
325-
@aaa
317+
@wrap_aaa
326318
def flags(self) -> List[Flag]:
327319
return [Flag(**dic) for dic in self._cmdj("fj")]
328320

329321
@cached_property
330-
@aaa
322+
@wrap_aaa
331323
def xrefs(self) -> List[Xref]:
332324
return [Xref(**dic) for dic in self._cmdj("axj")]
333325

334-
@aaa
326+
@wrap_aaa
327+
@wrap_arg_addr
335328
def get_fcn_bbs(self, addr: int):
336329
'''list basic blocks of function'''
337330
return [BasicBlock(**dic) for dic in self._cmdj(f"afbj @ {addr}")]
338331

339-
@aaa
340-
def get_bb_at(self, addr: int):
332+
@wrap_aaa
333+
@wrap_arg_addr
334+
def get_bb(self, addr: int):
341335
'''get basic block at address'''
342336
try:
343337
dic = self._cmdj(f"afbj. {addr}")[0]
344338
return BasicBlock(**dic)
345339
except IndexError:
346340
pass
347341

348-
@aaa
349-
def get_fcn_at(self, addr: int):
342+
@wrap_aaa
343+
@wrap_arg_addr
344+
def get_fcn(self, addr: int):
350345
try:
351346
dic = self._cmdj(f"afij {addr}")[0] # afi show function information
352347
return Function(**dic)
353348
except IndexError:
354349
pass
355350

356-
@aaa
357-
def anal_op(self, target: Union[int, Instruction]):
358-
addr = target.offset if isinstance(target, Instruction) else target
351+
@wrap_aaa
352+
@wrap_arg_addr
353+
def anal_op(self, addr: int):
354+
'''r2 opcode analysis (detail about an instruction) at address'''
359355
dic = self._cmdj(f"aoj @ {addr}")[0]
360356
return AnalOp(**dic)
361357

@@ -427,13 +423,12 @@ def _backtrace_fuzzy(self, at: int = None, depth: int = 128) -> Optional[CallSta
427423
cursp += wordsize
428424
return frame
429425

430-
def set_backtrace(self, target: Union[int, str]):
426+
@wrap_arg_addr
427+
def set_backtrace(self, addr: int):
431428
'''Set backtrace at target address before executing'''
432-
if isinstance(target, str):
433-
target = self.where(target)
434429
def bt_hook(__ql: 'R2Qiling', *args):
435430
print(self._backtrace_fuzzy())
436-
self.ql.hook_address(bt_hook, target)
431+
self.ql.hook_address(bt_hook, addr)
437432

438433
def disassembler(self, ql: 'R2Qiling', addr: int, size: int, filt: Pattern[str]=None) -> int:
439434
'''A human-friendly monkey patch of QlArchUtils.disassembler powered by r2, can be used for hook_code
@@ -468,9 +463,9 @@ def enable_trace(self, mode='full'):
468463
elif mode == 'history':
469464
trace.enable_history_trace(self.ql)
470465

471-
def deflat(self, target: Union[int, R2Data]):
472-
'''Create deflator with self r2 instance, will patch ql code'''
473-
addr = target if isinstance(target, int) else target.start_ea
466+
@wrap_arg_addr
467+
def deflat(self, addr: int):
468+
'''Deflat function at given address, will patch ql code'''
474469
deflator = R2Deflator(self)
475470
deflator.parse_blocks_for_deobf(addr)
476471
deflator._search_path()

qiling/extensions/r2/utils.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from functools import wraps
2+
3+
4+
def wrap_aaa(fun):
5+
@wraps(fun)
6+
def wrapper(self, *args, **kwargs):
7+
if self.analyzed is False:
8+
self._cmd("aaa")
9+
self.analyzed = True
10+
return fun(self, *args, **kwargs)
11+
return wrapper
12+
13+
def wrap_arg_addr(fun):
14+
@wraps(fun)
15+
def wrapper(self, *args, **kwargs):
16+
if not args: # just return same func if not args
17+
return fun(self, *args, **kwargs)
18+
# parse first argument to address
19+
target = args[0]
20+
if isinstance(target, int): # first arg is address
21+
addr = target
22+
elif isinstance(target, str): # first arg is name
23+
addr = self.where(args[0])
24+
else: # isinstance(target, R2Data)
25+
addr = target.start_ea
26+
newargs = (addr,) + args[1:]
27+
return fun(self, *newargs, **kwargs)
28+
return wrapper

0 commit comments

Comments
 (0)