Skip to content

Commit baa6aa6

Browse files
authored
Merge branch 'qilingframework:dev' into dev
2 parents 7cf9e88 + e8b8dd6 commit baa6aa6

File tree

7 files changed

+42
-54
lines changed

7 files changed

+42
-54
lines changed

qiling/extensions/trace.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22

33
# More info, please refer to https://github.com/qilingframework/qiling/pull/765
44

5-
65
from collections import deque
76
from typing import Deque, Iterable, Iterator, Mapping, Tuple
87

9-
from capstone import Cs, CsInsn, CS_OP_IMM, CS_OP_MEM, CS_OP_REG
8+
from capstone import Cs, CsInsn, CS_ARCH_X86, CS_OP_IMM, CS_OP_MEM, CS_OP_REG
109
from capstone.x86 import X86Op
1110
from capstone.x86_const import X86_INS_LEA, X86_REG_INVALID, X86_REG_RIP
1211

@@ -16,7 +15,7 @@
1615

1716
# <WORKAROUND>
1817
def __uc2_workaround() -> Mapping[int, int]:
19-
"""Starting from Unicron2, Unicron and Capstone Intel registers definitions are
18+
"""Starting from Unicorn2, Unicorn and Capstone Intel registers definitions are
2019
no longer aligned and cannot be used interchangebly. This temporary workaround
2120
maps capstone x86 registers definitions to unicorn x86 registers definitions.
2221
@@ -47,6 +46,7 @@ def __get_trace_records(ql: Qiling, address: int, size: int, md: Cs) -> Iterator
4746
# unicorn denotes unsupported instructions by a magic size value. though these instructions
4847
# are not emulated, capstone can still parse them.
4948
if size == 0xf1f1f1f1:
49+
# note that invalid instructions will generate a StopIteration exception here
5050
yield next(__get_trace_records(ql, address, 16, md))
5151
return
5252

@@ -125,6 +125,7 @@ def __parse_op(op: X86Op) -> str:
125125
2: 'word',
126126
4: 'dword',
127127
8: 'qword',
128+
10: 'fword',
128129
16: 'xmmword'
129130
}[op.size]
130131

@@ -154,13 +155,15 @@ def enable_full_trace(ql: Qiling):
154155
md = ql.create_disassembler()
155156
md.detail = True
156157

158+
assert md.arch == CS_ARCH_X86, 'currently available only for intel architecture'
159+
157160
# if available, use symbols map to resolve memory accesses
158161
symsmap = getattr(ql.loader, 'symsmap', {})
159162

160163
# show trace lines in a darker color so they would be easily distinguished from
161164
# ordinary log records
162-
DarkGray = "\x1b[90m"
163-
Default = "\x1b[39m"
165+
faded_color = "\033[2m"
166+
reset_color = "\033[0m"
164167

165168
def __trace_hook(ql: Qiling, address: int, size: int):
166169
"""[internal] Trace hook callback.
@@ -169,7 +172,7 @@ def __trace_hook(ql: Qiling, address: int, size: int):
169172
for record in __get_trace_records(ql, address, size, md):
170173
line = __to_trace_line(record, symsmap)
171174

172-
ql.log.debug(f'{DarkGray}{line}{Default}')
175+
ql.log.debug(f'{faded_color}{line}{reset_color}')
173176

174177
ql.hook_code(__trace_hook)
175178

@@ -189,6 +192,8 @@ def enable_history_trace(ql: Qiling, nrecords: int):
189192
md = ql.create_disassembler()
190193
md.detail = True
191194

195+
assert md.arch == CS_ARCH_X86, 'currently available only for intel architecture'
196+
192197
# if available, use symbols map to resolve memory accesses
193198
symsmap = getattr(ql.loader, 'symsmap', {})
194199

qiling/os/fcall.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def __get_typed_args(proto: Mapping[str, Any], args: Mapping[str, Any]) -> Itera
114114
if len(names) > len(types):
115115
types.extend([None] * (len(names) - len(types)))
116116

117-
return zip(types, names, values)
117+
return tuple(zip(types, names, values))
118118

119119
def call(self, func: CallHook, proto: Mapping[str, Any], params: Mapping[str, Any], hook_onenter: Optional[OnEnterHook], hook_onexit: Optional[OnExitHook], passthru: bool) -> Tuple[Iterable[TypedArg], int, int]:
120120
"""Execute a hooked function.

qiling/os/posix/const.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,24 @@
533533
'O_LARGEFILE': None
534534
}
535535

536+
windows_x86_open_flags = {
537+
'O_RDONLY': 0x0,
538+
'O_WRONLY': 0x1,
539+
'O_RDWR': 0x2,
540+
'O_NONBLOCK': None,
541+
'O_APPEND': 0x8,
542+
'O_ASYNC': None,
543+
'O_SYNC': None,
544+
'O_NOFOLLOW': None,
545+
'O_CREAT': 0x100,
546+
'O_TRUNC': 0x200,
547+
'O_EXCL': 0x400,
548+
'O_NOCTTY': None,
549+
'O_DIRECTORY': None,
550+
'O_BINARY': 0x8000,
551+
'O_LARGEFILE': None
552+
}
553+
536554
qnx_arm64_open_flags = {
537555
'O_RDONLY' : 0x00000,
538556
'O_WRONLY' : 0x00001,

qiling/os/posix/const_mapping.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ def flag_mapping(flags, mapping_name, mapping_from, mapping_to):
6464
f = macos_x86_open_flags
6565
elif ql.ostype == QL_OS.FREEBSD:
6666
f = freebsd_x86_open_flags
67+
elif ql.ostype == QL_OS.WINDOWS:
68+
f = windows_x86_open_flags
6769
elif ql.ostype == QL_OS.QNX:
6870
f = qnx_arm64_open_flags
6971

@@ -73,6 +75,8 @@ def flag_mapping(flags, mapping_name, mapping_from, mapping_to):
7375
t = macos_x86_open_flags
7476
elif ql.platform_os == QL_OS.FREEBSD:
7577
t = freebsd_x86_open_flags
78+
elif ql.platform_os == QL_OS.WINDOWS:
79+
t = windows_x86_open_flags
7680

7781
if f == t:
7882
return flags

qiling/os/utils.py

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -68,21 +68,21 @@ def string_appearance(self, s: str) -> None:
6868
self.appeared_strings.setdefault(token, set()).add(self.syscalls_counter)
6969

7070
@staticmethod
71-
def read_string(ql: Qiling, address: int, terminator: str) -> str:
72-
result = ""
71+
def read_string(ql: Qiling, address: int, terminator: bytes) -> str:
72+
result = bytearray()
7373
charlen = len(terminator)
7474

7575
char = ql.mem.read(address, charlen)
7676

77-
while char.decode(errors="ignore") != terminator:
77+
while char != terminator:
7878
address += charlen
79-
result += char.decode(errors="ignore")
79+
result += char
8080
char = ql.mem.read(address, charlen)
8181

82-
return result
82+
return result.decode(errors="ignore")
8383

8484
def read_wstring(self, address: int) -> str:
85-
s = QlOsUtils.read_string(self.ql, address, '\x00\x00')
85+
s = QlOsUtils.read_string(self.ql, address, b'\x00\x00')
8686

8787
# We need to remove \x00 inside the string. Compares do not work otherwise
8888
s = s.replace("\x00", "")
@@ -91,7 +91,7 @@ def read_wstring(self, address: int) -> str:
9191
return s
9292

9393
def read_cstring(self, address: int) -> str:
94-
s = QlOsUtils.read_string(self.ql, address, '\x00')
94+
s = QlOsUtils.read_string(self.ql, address, b'\x00')
9595

9696
self.string_appearance(s)
9797

@@ -184,24 +184,3 @@ def printf(self, format: str, args: MutableSequence, wstring: bool = False) -> i
184184

185185
def update_ellipsis(self, params: MutableMapping, args: Sequence) -> None:
186186
params.update((f'{QlOsUtils.ELLIPSIS_PREF}{i}', a) for i, a in enumerate(args))
187-
188-
def exec_arbitrary(self, start: int, end: int):
189-
old_sp = self.ql.reg.arch_sp
190-
191-
# we read where this hook is supposed to return
192-
ret = self.ql.stack_read(0)
193-
194-
def restore(ql: Qiling):
195-
self.ql.log.debug(f"Executed code from {start:#x} to {end:#x}")
196-
# now we can restore the register to be where we were supposed to
197-
ql.reg.arch_sp = old_sp + ql.pointersize
198-
ql.reg.arch_pc = ret
199-
200-
# we want to execute the code once, not more
201-
hret.remove()
202-
203-
# we have to set an address to restore the registers
204-
hret = self.ql.hook_address(restore, end)
205-
# we want to rewrite the return address to the function
206-
self.ql.stack_write(0, start)
207-

qiling/os/windows/dlls/kernel32/errhandlingapi.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,7 @@ def hook_SetErrorMode(ql: Qiling, address: int, params):
7575
def hook_RaiseException(ql: Qiling, address: int, params):
7676
func_addr = ql.os.handle_manager.search("TopLevelExceptionHandler").obj
7777

78-
# TODO: this implementation won't work most of the time
79-
size = find_size_function(ql, func_addr)
80-
ql.os.exec_arbitrary(func_addr, func_addr + size)
78+
ql.os.fcall.call_native(func_addr, [], None)
8179

8280
return 0
8381

@@ -122,7 +120,6 @@ def exec_standard_into(ql: Qiling, intno: int, user_data):
122120
ql.reg.esi = user_data
123121

124122
addr = params["Handler"]
125-
#size = find_size_function(ql, addr)
126123

127124
# the interrupts 0x2d, 0x3 must be hooked
128125
hook = ql.hook_intno(exec_standard_into, 0x3, user_data=addr)

qiling/os/windows/utils.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,6 @@ def path_leaf(path):
3737
head, tail = ntpath.split(path)
3838
return tail or ntpath.basename(head)
3939

40-
# FIXME: determining a function size by locating 'ret' opcodes in its code is a very unreliable way, to say
41-
# the least. not only that 'ret' instructions may appear more than once in a single function, they not are
42-
# necessarily located at the last function basic block: think of a typical nested loop spaghetty.
43-
#
44-
# also, there is no telling whether a 0xC3 value found in function code is actually a 'ret' instruction, or
45-
# just part of a magic value (e.g. "mov eax, 0xffffffc3").
46-
#
47-
# finally, if this method happens to find the correct function size, by any chance, that would be a pure luck.
48-
def find_size_function(ql: Qiling, func_addr: int):
49-
# We have to retrieve the return address position
50-
code = ql.mem.read(func_addr, 0x100)
51-
return_procedures = [b"\xc3", b"\xc2", b"\xcb", b"\xca"]
52-
min_index = min([code.index(return_value) for return_value in return_procedures if return_value in code])
53-
return min_index
54-
5540

5641
def io_Write(ql: Qiling, in_buffer: bytes):
5742
heap = ql.os.heap

0 commit comments

Comments
 (0)