refactor!: ql own mem as bytearray, ref passed to uc and r2 · qilingframework/qiling@1ce1ad0 · GitHub
Skip to content

Commit 1ce1ad0

Browse files
committed
refactor!: ql own mem as bytearray, ref passed to uc and r2
BREAKING CHANGE: mem is managed in Python instead of uc BREAKING CHANGE: MapInfoEntry now has 6 elements instead of 5 BREAKING CHANGE: r2 map io from ql.mem, no full binary, now missing symbols BREAKING CHANGE: del_mapinfo and change_mapinfo recreate and remap mem Add unit tests for ql mem operations Also fix potential bug in syscall_munmap
1 parent cb7ca60 commit 1ce1ad0

8 files changed

Lines changed: 282 additions & 53 deletions

File tree

‎examples/extensions/r2/hello_r2.py‎

Lines changed: 1 addition & 1 deletion

‎examples/extensions/r2/mem_r2.py‎

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import sys
2+
from types import FrameType
3+
sys.path.append('..')
4+
5+
from tests.test_elf import ELFTest
6+
from qiling import Qiling
7+
from qiling.const import QL_VERBOSE
8+
from qiling.extensions.r2 import R2
9+
10+
def test_elf_linux_arm():
11+
def my_puts(ql: Qiling):
12+
params = ql.os.resolve_fcall_params(ELFTest.PARAMS_PUTS)
13+
print(f'puts("{params["s"]}")')
14+
# all_mem = ql.mem.save()
15+
# for lbound, ubound, perm, _, _, _data in ql.mem.map_info:
16+
# print(f"{lbound:#x} - {ubound:#x} {ubound - lbound:#x} {len(_data):#x} {perm:#x}")
17+
# print()
18+
# ql.mem.restore(all_mem)
19+
20+
ql = Qiling(["../examples/rootfs/arm_linux/bin/arm_stat64"], "../examples/rootfs/arm_linux", verbose=QL_VERBOSE.DEBUG)
21+
ql.os.set_api('puts', my_puts)
22+
ql.run()
23+
del ql
24+
25+
def fn(frame: FrameType, msg, arg):
26+
if msg == 'return':
27+
print("Return: ", arg)
28+
return
29+
if msg != 'call': return
30+
# Filter as appropriate
31+
if 'memory' not in frame.f_code.co_filename: return
32+
if '<' in frame.f_code.co_name: return
33+
caller = frame.f_back.f_code.co_name
34+
print("Called ", frame.f_code.co_name, "from ", caller)
35+
for i in range(frame.f_code.co_argcount):
36+
name = frame.f_code.co_varnames[i]
37+
var = frame.f_locals[name]
38+
if isinstance(var, (bytes, bytearray)):
39+
var = f'{type(var)} len {len(var)}'
40+
print(" Argument", name, "is", var)
41+
42+
sys.settrace(fn)
43+
44+
def unmap_hook(ql: "Qiling", access: int, addr: int, size: int, value: int):
45+
print(f"Unmapped memory access at {addr:#x} - {addr + size:#x} with {value:#x} in type {access}")
46+
47+
def mem_cmp_hook(ql: "Qiling", addr: int, size: int):
48+
mapinfo = ql.mem.map_info
49+
for i, mem_region in enumerate(ql.uc.mem_regions()):
50+
assert (mapinfo[i][0], mapinfo[i][1] - 1, mapinfo[i][2]) == mem_region
51+
uc_mem = ql.mem.read(mem_region[0], mem_region[1] - mem_region[0] + 1)
52+
data = ql.mem.map_info[i][5]
53+
if uc_mem == data: continue
54+
print(f"Memory region {i} {mem_region[0]:#x} - {mem_region[1]:#x} not equal to map_info from {addr:#x}")
55+
for line in ql.mem.get_formatted_mapinfo():
56+
print(line)
57+
with open("mem.bin", "wb") as f:
58+
f.write(uc_mem)
59+
with open("map.bin", "wb") as f:
60+
f.write(data)
61+
assert False
62+
63+
def addr_hook(ql: "Qiling"):
64+
mapinfo = ql.mem.map_info
65+
for i, mem_region in enumerate(ql.uc.mem_regions()):
66+
if i != 8: continue
67+
uc_mem = ql.mem.read(mem_region[0], mem_region[1] - mem_region[0] + 1)
68+
with open('right.bin', 'wb') as f:
69+
f.write(uc_mem)
70+
71+
if __name__ == '__main__':
72+
# from tests.test_shellcode import X8664_LIN
73+
env = {'LD_DEBUG': 'all'}
74+
# ql = Qiling(rootfs="rootfs/x8664_linux", code=X8664_LIN, archtype="x8664", ostype="linux", verbose=QL_VERBOSE.DEBUG)
75+
# ql = Qiling(["rootfs/x86_windows/bin/x86_hello.exe"], "rootfs/x86_windows")
76+
# ql = Qiling(["rootfs/arm_linux/bin/arm_hello_static"], "rootfs/arm_linux", verbose=QL_VERBOSE.DISASM)
77+
# ql = Qiling(["rootfs/arm_linux/bin/arm_hello"], "rootfs/arm_linux", env=env, verbose=QL_VERBOSE.DEBUG)
78+
ql = Qiling(["rootfs/x86_linux/bin/x86_hello"], "rootfs/x86_linux", verbose=QL_VERBOSE.DEBUG)
79+
# ql.hook_mem_unmapped(unmap_hook)
80+
# ql.hook_code(mem_cmp_hook)
81+
# mprot_addr = 0x047d4824
82+
# ql.hook_address(addr_hook, mprot_addr)
83+
# ql.debugger = 'qdb'
84+
# ql = Qiling(["rootfs/x8664_linux/bin/testcwd"], "rootfs/x8664_linux", verbose=QL_VERBOSE.DEBUG)
85+
for line in ql.mem.get_formatted_mapinfo():
86+
print(line)
87+
ql.run()
88+
# test_elf_linux_arm()

‎qiling/arch/utils.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def __init__(self, ql: Qiling):
2727

2828
@lru_cache(maxsize=64)
2929
def get_base_and_name(self, addr: int) -> Tuple[int, str]:
30-
for begin, end, _, name, _ in self.ql.mem.map_info:
30+
for begin, end, _, name, _, _ in self.ql.mem.map_info:
3131
if begin <= addr < end:
3232
return begin, basename(name)
3333

‎qiling/extensions/r2/r2.py‎

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,8 @@ def __init__(self, ql: "Qiling", baseaddr=(1 << 64) - 1, loadaddr=0):
142142
self.loadaddr = loadaddr # r2 -m [addr] map file at given address
143143
self.analyzed = False
144144
self._r2c = libr.r_core.r_core_new()
145-
if ql.code:
146-
self._setup_code(ql.code)
147-
else:
148-
self._setup_file(ql.path)
145+
self._r2i = ctypes.cast(self._r2c.contents.io, ctypes.POINTER(libr.r_io.struct_r_io_t))
146+
self._setup_mem(ql)
149147

150148
def _qlarch2r(self, archtype: QL_ARCH) -> str:
151149
return {
@@ -162,20 +160,21 @@ def _qlarch2r(self, archtype: QL_ARCH) -> str:
162160
QL_ARCH.PPC: "ppc",
163161
}[archtype]
164162

165-
def _setup_code(self, code: bytes):
166-
path = f'malloc://{len(code)}'.encode()
167-
fh = libr.r_core.r_core_file_open(self._r2c, path, UC_PROT_ALL, self.loadaddr)
168-
libr.r_core.r_core_bin_load(self._r2c, path, self.baseaddr)
169-
self._cmd(f'wx {code.hex()}')
163+
def _rbuf_map(self, buf: bytearray, perm: int = UC_PROT_ALL, addr: int = 0, delta: int = 0):
164+
rbuf = libr.r_buf_new_with_pointers(ctypes.c_ubyte.from_buffer(buf), len(buf), False)
165+
rbuf = ctypes.cast(rbuf, ctypes.POINTER(libr.r_io.struct_r_buf_t))
166+
desc = libr.r_io_open_buffer(self._r2i, rbuf, perm, 0) # last arg `mode` is always 0 in r2 code
167+
libr.r_io.r_io_map_add(self._r2i, desc.contents.fd, desc.contents.perm, delta, addr, len(buf))
168+
169+
def _setup_mem(self, ql: 'Qiling'):
170+
if not hasattr(ql, '_mem'):
171+
return
172+
for start, end, perms, _label, _mmio, buf in ql.mem.map_info:
173+
self._rbuf_map(buf, perms, start)
170174
# set architecture and bits for r2 asm
171-
arch = self._qlarch2r(self.ql.arch.type)
172-
self._cmd(f"e,asm.arch={arch},asm.bits={self.ql.arch.bits}")
173-
174-
def _setup_file(self, path: str):
175-
path = path.encode()
176-
fh = libr.r_core.r_core_file_open(self._r2c, path, UC_PROT_READ | UC_PROT_EXEC, self.loadaddr)
177-
libr.r_core.r_core_bin_load(self._r2c, path, self.baseaddr)
178-
175+
arch = self._qlarch2r(ql.arch.type)
176+
self._cmd(f"e,asm.arch={arch},asm.bits={ql.arch.bits}")
177+
179178
def _cmd(self, cmd: str) -> str:
180179
r = libr.r_core.r_core_cmd_str(
181180
self._r2c, ctypes.create_string_buffer(cmd.encode("utf-8")))

‎qiling/os/memory.py‎

Lines changed: 64 additions & 32 deletions

0 commit comments

Comments
 (0)