Skip to content

Commit ee0b3fa

Browse files
committed
demo: add r2 extension
1 parent 63fc062 commit ee0b3fa

File tree

7 files changed

+263
-3
lines changed

7 files changed

+263
-3
lines changed

examples/fuzzing/linux_x8664/fuzz_x8664_linux.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from qiling.extensions import afl
3232

3333
def main(input_file: str):
34-
ql = Qiling(["./x8664_fuzz"], "../../rootfs/x8664_linux",
34+
ql = Qiling(["./x8664_fuzz"], "../../rootfs/x8664_linux", analyzer="r2",
3535
verbose=QL_VERBOSE.OFF, # keep qiling logging off
3636
console=False) # thwart program output
3737

@@ -64,7 +64,7 @@ def start_afl(ql: Qiling):
6464
ql.hook_address(callback=lambda x: os.abort(), address=ba + 0x1225)
6565

6666
# set afl instrumentation [re]starting point. we set it to 'main'
67-
ql.hook_address(callback=start_afl, address=ba + 0x122c)
67+
ql.hook_address(callback=start_afl, address=ba + ql.analyzer.addr_of("main"))
6868

6969
# okay, ready to roll
7070
ql.run()

qiling/bin/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .base import QlBinFunction, QlBinSection, QlBinString, QlBinSymbol
2+
from .analyze import QlBinAnalyzer

qiling/bin/analyze.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from abc import ABC, abstractmethod
2+
from typing import Set
3+
from .base import QlBinSection, QlBinString, QlBinSymbol
4+
5+
6+
class QlBinAnalyzer(ABC):
7+
"""
8+
An abstract base class for concrete static binary analyzers.
9+
To extend a new binary analyzer, just derive from this class and implement
10+
all the methods marked with the @abstractmethod decorator.
11+
"""
12+
13+
def __init__(self):
14+
super().__init__()
15+
16+
@property
17+
@abstractmethod
18+
def sections(self) -> Set[QlBinSection]:
19+
raise NotImplementedError
20+
21+
@property
22+
@abstractmethod
23+
def strings(self) -> Set[QlBinString]:
24+
raise NotImplementedError
25+
26+
@property
27+
@abstractmethod
28+
def symbols(self) -> Set[QlBinSymbol]:
29+
raise NotImplementedError
30+
31+
@abstractmethod
32+
def addr_of_sym(self, name: str) -> int | None:
33+
raise NotImplementedError
34+
35+
@abstractmethod
36+
def addr_of_fcn(self, name: str) -> int | None:
37+
raise NotImplementedError
38+
39+
@abstractmethod
40+
def addr_of(self, name: str) -> int | None:
41+
raise NotImplementedError

qiling/bin/base.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
4+
#
5+
6+
from dataclasses import dataclass, fields
7+
from enum import Enum
8+
9+
10+
@dataclass(unsafe_hash=True)
11+
class QlBinFunction:
12+
name: str
13+
offset: int
14+
size: int
15+
signature: str = None
16+
17+
def __init__(self, **kwargs):
18+
names = set([f.name for f in fields(self)])
19+
for k, v in kwargs.items():
20+
if k in names:
21+
setattr(self, k, v)
22+
23+
24+
@dataclass(unsafe_hash=True)
25+
class QlBinSection:
26+
name: str
27+
size: int
28+
vsize: int
29+
paddr: int
30+
vaddr: int
31+
perm: str # TODO: use enum
32+
33+
def __init__(self, **kwargs):
34+
names = set([f.name for f in fields(self)])
35+
for k, v in kwargs.items():
36+
if k in names:
37+
setattr(self, k, v)
38+
39+
40+
@dataclass(unsafe_hash=True)
41+
class QlBinString:
42+
string: str
43+
vaddr: int
44+
paddr: int
45+
size: int
46+
length: int
47+
section: str = None
48+
49+
def __init__(self, **kwargs):
50+
names = set([f.name for f in fields(self)])
51+
for k, v in kwargs.items():
52+
if k in names:
53+
setattr(self, k, v)
54+
55+
56+
@dataclass(unsafe_hash=True)
57+
class QlBinSymbol:
58+
# see https://github.com/rizinorg/rizin/blob/dev/librz/include/rz_bin.h
59+
class SymbolType(str, Enum):
60+
NOTYPE = "NOTYPE"
61+
OBJ = "OBJ"
62+
FUNC = "FUNC"
63+
FIELD = "FIELD"
64+
IFACE = "IFACE"
65+
METH = "METH"
66+
STATIC = "STATIC"
67+
SECT = "SECT"
68+
FILE = "FILE"
69+
COMMON = "COMMON"
70+
TLS = "TLS"
71+
NUM = "NUM"
72+
LOOS = "LOOS"
73+
HIOS = "HIOS"
74+
LOPROC = "LOPROC"
75+
HIPROC = "HIPROC"
76+
SPCL = "SPCL"
77+
UNK = "UNK"
78+
79+
class SymbolBind(str, Enum):
80+
LOCAL = "LOCAL"
81+
GLOBAL = "GLOBAL"
82+
WEAK = "WEAK"
83+
NUM = "NUM"
84+
LOOS = "LOOS"
85+
HIOS = "HIOS"
86+
LOPROC = "LOPROC"
87+
HIPROC = "HIPROC"
88+
IMPORT = "IMPORT"
89+
UNKNOWN = "UNKNOWN"
90+
91+
name: str
92+
realname: str
93+
bind: str
94+
size: int
95+
type: SymbolType
96+
vaddr: int
97+
paddr: int
98+
is_imported: bool
99+
100+
def __init__(self, **kwargs):
101+
names = set([f.name for f in fields(self)])
102+
for k, v in kwargs.items():
103+
if k in names:
104+
setattr(self, k, v)
105+
106+
def __str__(self):
107+
return f"{self.name} {self.type} {hex(self.vaddr)} {hex(self.paddr)}"
108+
109+
if __name__ == '__main__':
110+
sym = QlBinFunction(**{"name": "test", "offset": 0x1234, "size": 0x5678})
111+
print(sym)

qiling/core.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
from unicorn.unicorn import Uc
1414

15+
from qiling.extensions.r2.r2 import R2
16+
1517
if TYPE_CHECKING:
1618
from .arch.arch import QlArch
1719
from .os.os import QlOs
@@ -46,6 +48,7 @@ def __init__(
4648
multithread = False,
4749
filter = None,
4850
stop: QL_STOP = QL_STOP.NONE,
51+
analyzer: str = None,
4952
*,
5053
endian: QL_ENDIAN = None,
5154
thumb: bool = False,
@@ -195,7 +198,13 @@ def __init__(
195198

196199
# Run the loader
197200
self.loader.run()
198-
self._init_stop_guard()
201+
self._init_stop_guard()
202+
203+
# Initialize the binary analzer if provdided
204+
if analyzer == "r2":
205+
self.analyzer = R2(self._path)
206+
elif analyzer == "rizin":
207+
self.analyzer = R2(self._path)
199208

200209
#####################
201210
# Qiling Components #

qiling/extensions/r2/__init__.py

Whitespace-only changes.

qiling/extensions/r2/r2.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
4+
#
5+
6+
import os
7+
import functools
8+
import json
9+
import ctypes
10+
from typing import Set
11+
import libr
12+
from qiling.bin import QlBinAnalyzer
13+
from qiling.bin import QlBinFunction, QlBinSection, QlBinString, QlBinSymbol
14+
15+
16+
class R2(QlBinAnalyzer):
17+
def __init__(self, filename: str):
18+
super().__init__()
19+
filename = filename.encode()
20+
filename = os.path.realpath(filename)
21+
self._r2c = libr.r_core.r_core_new()
22+
fh = libr.r_core.r_core_file_open(self._r2c, filename, 0b101, 0)
23+
libr.r_core.r_core_bin_load(self._r2c, filename, (1 << 64) - 1)
24+
25+
def _cmd(self, cmd):
26+
r = libr.r_core.r_core_cmd_str(
27+
self._r2c, ctypes.create_string_buffer(cmd.encode("utf-8")))
28+
return ctypes.string_at(r).decode('utf-8')
29+
30+
@functools.cached_property
31+
def sections(self) -> Set[QlBinSection]:
32+
res = self._cmd("iSj")
33+
sec_lst = json.loads(res)
34+
return {QlBinSection(**dic) for dic in sec_lst}
35+
36+
@functools.cached_property
37+
def strings(self) -> Set[QlBinString]:
38+
res = self._cmd("izzj")
39+
str_lst = json.loads(res)
40+
return {QlBinString(**dic) for dic in str_lst}
41+
42+
@functools.cached_property
43+
def symbols(self) -> Set[QlBinSymbol]:
44+
res = self._cmd("isj")
45+
sym_lst = json.loads(res)
46+
return {QlBinSymbol(**dic) for dic in sym_lst}
47+
48+
@functools.cached_property
49+
def functions(self) -> Set[QlBinFunction]:
50+
self._cmd("aaa")
51+
res = self._cmd("aflj")
52+
fcn_lst = json.loads(res)
53+
return {QlBinFunction(**dic) for dic in fcn_lst}
54+
55+
@functools.cached_property
56+
def baddr(self) -> int:
57+
_bin = ctypes.cast(self._r2c.contents.bin,
58+
ctypes.POINTER(libr.r_bin.RBin))
59+
return libr.r_bin.r_bin_get_baddr(_bin)
60+
61+
def addr_of_str(self, name: str) -> int | None:
62+
strs = self.strings
63+
for str in strs:
64+
if str.string == name:
65+
return str.vaddr
66+
67+
def addr_of_sym(self, name: str) -> int | None:
68+
syms = self.symbols
69+
for sym in syms:
70+
if sym.name == name or sym.realname == name:
71+
return sym.vaddr
72+
73+
def addr_of_fcn(self, name: str) -> int | None:
74+
fcns = self.functions
75+
for fcn in fcns:
76+
if fcn.name == name or fcn.name.endswith(name):
77+
return fcn.offset
78+
79+
def addr_of(self, name: str) -> int | None:
80+
return self.addr_of_sym(name) or self.addr_of_fcn(name)
81+
82+
def __del__(self):
83+
libr.r_core.r_core_free(self._r2c)
84+
85+
86+
if __name__ == '__main__':
87+
r2 = R2("examples/fuzzing/linux_x8664/x8664_fuzz")
88+
print(r2.strings)
89+
print(r2.sections)
90+
print(hex(r2.baddr))
91+
print(hex(r2.addr_of("main")))
92+
while True:
93+
print("> ", end="")
94+
cmd = input()
95+
if cmd.strip() == "q":
96+
break
97+
print(r2._cmd(cmd))

0 commit comments

Comments
 (0)