Skip to content

Commit fde213b

Browse files
author
kabeor
committed
Merge branch 'dev' of https://github.com/qilingframework/qiling into changelog_144
2 parents 9498570 + 5d7a8f0 commit fde213b

40 files changed

+2309
-1707
lines changed

.github/workflows/build-ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ jobs:
8080
cd ../qiling
8181
cd ../examples/rootfs/x86_linux/kernel && unzip -P infected m0hamed_rootkit.ko.zip
8282
cd ../../../../
83-
pip3 install -e .[evm]
83+
pip3 install -e .[evm,RE]
8484
8585
if [ ${{ matrix.os }} == 'ubuntu-18.04' ] and [ ${{ matrix.python-version }} == '3.9' ]; then
8686
docker run -it --rm -v ${GITHUB_WORKSPACE}:/qiling qilingframework/qiling:dev bash -c "cd tests && ./test_onlinux.sh"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ With qltool, easy execution can be performed:
182182
With shellcode:
183183

184184
```
185-
$ ./qltool shellcode --os linux --arch arm --hex -f examples/shellcodes/linarm32_tcp_reverse_shell.hex
185+
$ ./qltool code --os linux --arch arm --format hex -f examples/shellcodes/linarm32_tcp_reverse_shell.hex
186186
```
187187

188188
With binary file:

examples/extensions/r2/hello_r2.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ def func(ql: Qiling, *args, **kwargs):
1616
return
1717

1818
def my_sandbox(path, rootfs):
19-
ql = Qiling(path, rootfs, verbose=QL_VERBOSE.DEBUG)
19+
ql = Qiling(path, rootfs, verbose=QL_VERBOSE.DISASM)
20+
# QL_VERBOSE.DISASM will be monkey-patched when r2 is available
2021
r2 = R2(ql)
2122

2223
# search bytes sequence using ql.mem.search
@@ -33,7 +34,7 @@ def my_sandbox(path, rootfs):
3334
# get function address and hook it
3435
ql.hook_address(func, r2.functions['main'].offset)
3536
# enable trace powered by r2 symsmap
36-
r2.enable_trace()
37+
# r2.enable_trace()
3738
ql.run()
3839

3940
if __name__ == "__main__":

examples/sality.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,16 +119,16 @@ def hook_WriteFile(ql: Qiling, address: int, params):
119119
if hFile == 0x13371337:
120120
buffer = ql.mem.read(lpBuffer, nNumberOfBytesToWrite)
121121
try:
122-
r, nNumberOfBytesToWrite = utils.io_Write(ql.amsint32_driver, buffer)
122+
nNumberOfBytesToWrite = utils.io_Write(ql.amsint32_driver, buffer)
123123
ql.mem.write_ptr(lpNumberOfBytesWritten, nNumberOfBytesToWrite, 4)
124-
except Exception as e:
124+
except Exception:
125125
ql.log.exception("")
126-
print("Exception = %s" % str(e))
127-
r = 1
128-
if r:
129-
return 1
126+
r = False
130127
else:
131-
return 0
128+
r = True
129+
130+
return int(r)
131+
132132
else:
133133
return _WriteFile(ql, address, params)
134134

qiling/arch/evm/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import sys
22

3+
# python 3.10 has not been supported yet in the latest blake2b-py release
4+
if sys.version_info >= (3,10):
5+
sys.exit('Sorry, Python > 3.10 is not supported for now')
6+
37
# Ensure we can reach 1024 frames of recursion
48
#
59
EVM_RECURSION_LIMIT = 1024 * 12

qiling/arch/utils.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,12 @@ def ql_hook_block_disasm(ql: Qiling, address: int, size: int):
7373
self._block_hook = None
7474

7575
if verbosity >= QL_VERBOSE.DISASM:
76-
self._disasm_hook = self.ql.hook_code(self.disassembler)
76+
try: # monkey patch disassembler
77+
from qiling.extensions.r2 import R2
78+
r2 = R2(self.ql)
79+
self._disasm_hook = self.ql.hook_code(r2.disassembler)
80+
except (ImportError, ModuleNotFoundError):
81+
self._disasm_hook = self.ql.hook_code(self.disassembler)
7782

7883
if verbosity >= QL_VERBOSE.DUMP:
7984
self._block_hook = self.ql.hook_block(ql_hook_block_disasm)

qiling/debugger/gdb/gdb.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class QlGdb(QlDebugger):
6666
"""A simple gdbserver implementation.
6767
"""
6868

69-
def __init__(self, ql: Qiling, ip: str = '127.0.01', port: int = 9999):
69+
def __init__(self, ql: Qiling, ip: str = '127.0.0.1', port: int = 9999):
7070
super().__init__(ql)
7171

7272
if type(port) is str:
@@ -644,7 +644,7 @@ def handle_v(subcmd: str) -> Reply:
644644
groups = subcmd.split(';')[1:]
645645

646646
for grp in groups:
647-
cmd, tid = grp.split(':', maxsplit=1)
647+
cmd, *tid = grp.split(':', maxsplit=1)
648648

649649
if cmd in ('c', f'C{SIGTRAP:02x}'):
650650
return handle_c('')

qiling/extensions/r2/r2.py

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

6-
import bisect
76
import ctypes
87
import json
8+
import re
99
import libr
10-
from dataclasses import dataclass, fields
10+
from dataclasses import dataclass, field, fields
1111
from functools import cached_property, wraps
12-
from typing import TYPE_CHECKING, Dict, List, Literal, Tuple, Union
12+
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
@@ -80,6 +80,20 @@ class Symbol(R2Data):
8080
is_imported: bool
8181

8282

83+
@dataclass(unsafe_hash=True, init=False)
84+
class Instruction(R2Data):
85+
offset: int
86+
size: int
87+
opcode: str # raw opcode
88+
disasm: str = '' # flag resolved opcode
89+
bytes: bytes
90+
type: str
91+
92+
def __init__(self, **kwargs):
93+
super().__init__(**kwargs)
94+
self.bytes = bytes.fromhex(kwargs["bytes"])
95+
96+
8397
@dataclass(unsafe_hash=True, init=False)
8498
class Function(R2Data):
8599
name: str
@@ -90,7 +104,7 @@ class Function(R2Data):
90104

91105
@dataclass(unsafe_hash=True, init=False)
92106
class Flag(R2Data):
93-
offset: int
107+
offset: int # should be addr but r2 calls it offset
94108
name: str = ''
95109
size: int = 0
96110

@@ -166,7 +180,9 @@ def _cmd(self, cmd: str) -> str:
166180
self._r2c, ctypes.create_string_buffer(cmd.encode("utf-8")))
167181
return ctypes.string_at(r).decode('utf-8')
168182

169-
@staticmethod
183+
def _cmdj(self, cmd: str) -> Union[Dict, List[Dict]]:
184+
return json.loads(self._cmd(cmd))
185+
170186
def aaa(fun):
171187
@wraps(fun)
172188
def wrapper(self):
@@ -176,9 +192,6 @@ def wrapper(self):
176192
return fun(self)
177193
return wrapper
178194

179-
def _cmdj(self, cmd: str) -> Union[Dict, List[Dict]]:
180-
return json.loads(self._cmd(cmd))
181-
182195
@cached_property
183196
def binfo(self) -> Dict[str, str]:
184197
return self._cmdj("iIj")
@@ -222,13 +235,24 @@ def flags(self) -> List[Flag]:
222235
def xrefs(self) -> List[Xref]:
223236
return [Xref(**dic) for dic in self._cmdj("axj")]
224237

225-
def at(self, addr: int) -> Tuple[Flag, int]:
226-
# the most suitable flag should have address <= addr
227-
# bisect_right find the insertion point, right side if value exists
228-
idx = bisect.bisect_right(self.flags, Flag(offset=addr))
229-
# minus 1 to find the corresponding flag
230-
flag = self.flags[idx - 1]
231-
return flag, addr - flag.offset
238+
def at(self, addr: int, parse=False) -> Union[str, Tuple[str, int]]:
239+
'''Given an address, return [name, offset] or "name + offset"'''
240+
name = self._cmd(f'fd {addr}').strip()
241+
if parse:
242+
try:
243+
name, offset = name.split(' + ')
244+
offset = int(offset)
245+
except ValueError: # split fail when offset=0
246+
offset = 0
247+
return name, offset
248+
return name
249+
250+
def where(self, name: str, offset: int=0) -> int:
251+
'''Given a name (+ offset), return its address (0 when not found)'''
252+
if offset != 0: # name can already have offset, multiple + is allowd
253+
name += f' + {offset}'
254+
addr = self._cmd(f'?v {name}').strip() # 0x0 when name is not found
255+
return int(addr, 16)
232256

233257
def refrom(self, addr: int) -> List[Xref]:
234258
return [x for x in self.xrefs if x.fromaddr == addr]
@@ -240,6 +264,35 @@ def read(self, addr: int, size: int) -> bytes:
240264
hexstr = self._cmd(f"p8 {size} @ {addr}")
241265
return bytes.fromhex(hexstr)
242266

267+
def dis_nbytes(self, addr: int, size: int) -> List[Instruction]:
268+
insts = [Instruction(**dic) for dic in self._cmdj(f"pDj {size} @ {addr}")]
269+
return insts
270+
271+
def disassembler(self, ql: 'Qiling', addr: int, size: int, filt: Pattern[str]=None) -> int:
272+
'''A human-friendly monkey patch of QlArchUtils.disassembler powered by r2, can be used for hook_code
273+
:param ql: Qiling instance
274+
:param addr: start address for disassembly
275+
:param size: size in bytes
276+
:param filt: regex pattern to filter instructions
277+
:return: progress of dissembler, should be equal to size if success
278+
'''
279+
anibbles = ql.arch.bits // 4
280+
progress = 0
281+
for inst in self.dis_nbytes(addr, size):
282+
if inst.type.lower() == 'invalid':
283+
break # stop disasm
284+
name, offset = self.at(inst.offset, parse=True)
285+
if filt is None or filt.search(name):
286+
ql.log.info(f'{inst.offset:0{anibbles}x} [{name:20s} + {offset:#08x}] {inst.bytes.hex(" "):20s} {inst.disasm}')
287+
progress = inst.offset + inst.size - addr
288+
if progress < size:
289+
ql.arch.utils.disassembler(ql, addr + progress, size - progress)
290+
return progress
291+
292+
def enable_disasm(self, filt_str: str=''):
293+
filt = re.compile(filt_str)
294+
self.ql.hook_code(self.disassembler, filt)
295+
243296
def enable_trace(self, mode='full'):
244297
# simple map from addr to flag name, cannot resolve addresses in the middle
245298
self.ql.loader.symsmap = {flag.offset: flag.name for flag in self.flags}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
For latest documentation, please visit https://docs.qiling.io

qiling/extensions/tracing/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)