Skip to content

Commit 4a20ef2

Browse files
authored
Merge pull request #911 from elicn/posix-improv
Refactored POSIX syscalls
2 parents 64bd23d + eddb96a commit 4a20ef2

28 files changed

+1079
-1267
lines changed

qiling/core.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,6 @@ def __init__(
178178
self._log_file,
179179
self._console,
180180
self._filter,
181-
self._multithread,
182181
self._log_override,
183182
self._log_plain)
184183

qiling/os/posix/posix.py

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -161,36 +161,40 @@ def getNameFromErrorCode(ret: int) -> str:
161161
def load_syscall(self):
162162
# import syscall mapping function
163163
map_syscall = ql_syscall_mapping_function(self.ql.ostype)
164-
syscall = self.syscall
165-
syscall_name = map_syscall(self.ql, syscall)
164+
syscall_id = self.syscall
165+
syscall_name = map_syscall(self.ql, syscall_id)
166166

167167
# get syscall on-enter hook (if any)
168168
hooks_dict = self.posix_syscall_hooks[QL_INTERCEPT.ENTER]
169-
onenter_hook = hooks_dict.get(syscall_name) or hooks_dict.get(syscall)
169+
onenter_hook = hooks_dict.get(syscall_name) or hooks_dict.get(syscall_id)
170170

171171
# get syscall on-exit hook (if any)
172172
hooks_dict = self.posix_syscall_hooks[QL_INTERCEPT.EXIT]
173-
onexit_hook = hooks_dict.get(syscall_name) or hooks_dict.get(syscall)
173+
onexit_hook = hooks_dict.get(syscall_name) or hooks_dict.get(syscall_id)
174174

175175
# get syscall replacement hook (if any)
176176
hooks_dict = self.posix_syscall_hooks[QL_INTERCEPT.CALL]
177-
syscall_hook = hooks_dict.get(syscall_name) or hooks_dict.get(syscall)
177+
syscall_hook = hooks_dict.get(syscall_name) or hooks_dict.get(syscall_id)
178+
179+
if not syscall_hook:
180+
osname = ostype_convert_str(self.ql.ostype)
181+
os_syscalls = ql_get_module_function(f"qiling.os.{osname.lower()}", "syscall")
182+
posix_syscalls = ql_get_module_function(f"qiling.os.posix", "syscall")
183+
184+
# look in os-specific and posix syscall hooks
185+
syscall_hook = getattr(os_syscalls, syscall_name, None) or getattr(posix_syscalls, syscall_name, None)
178186

179187
if syscall_hook:
180188
syscall_name = syscall_hook.__name__
181-
else:
182-
_ostype_str = ostype_convert_str(self.ql.ostype)
183-
_posix_syscall = ql_get_module_function(f"qiling.os.posix", "syscall")
184-
_os_syscall = ql_get_module_function(f"qiling.os.{_ostype_str.lower()}", "syscall")
185189

186-
if syscall_name in dir(_posix_syscall) or syscall_name in dir(_os_syscall):
187-
syscall_hook = eval(syscall_name)
188-
syscall_name = syscall_hook.__name__
189-
else:
190-
syscall_hook = None
190+
# extract the parameters list from hook signature
191+
param_names = tuple(signature(syscall_hook).parameters.values())
191192

192-
if syscall_hook:
193-
params = [self.__syscall_cc.getRawParam(i) for i in range(6)]
193+
# skip first arg (always 'ql') and filter out python special args (*args and **kwargs)
194+
param_names = [info.name for info in param_names[1:] if info.kind == Parameter.POSITIONAL_OR_KEYWORD]
195+
196+
# read parameter values
197+
params = [self.__syscall_cc.getRawParam(i) for i in range(len(param_names))]
194198

195199
try:
196200
# if set, fire up the on-enter hook and let it override original args set
@@ -218,24 +222,14 @@ def load_syscall(self):
218222
raise
219223

220224
except Exception as e:
221-
self.ql.log.exception("")
222-
self.ql.log.info(f'Syscall ERROR: {syscall_name} DEBUG: {e}')
225+
self.ql.log.exception(f'Syscall ERROR: {syscall_name} DEBUG: {e}')
223226
raise e
224227

225228
# print out log entry
226-
syscall_basename = syscall_hook.__name__[len(SYSCALL_PREF):]
229+
syscall_basename = syscall_name[len(SYSCALL_PREF):]
227230
args = []
228231

229-
# ignore first arg, which is 'ql'
230-
args_info = tuple(signature(syscall_hook).parameters.values())[1:]
231-
232-
for info, value in zip(args_info, params):
233-
# skip python special args, like: *args and **kwargs
234-
if info.kind != Parameter.POSITIONAL_OR_KEYWORD:
235-
continue
236-
237-
name = info.name
238-
232+
for name, value in zip(param_names, params):
239233
# cut the first part of the arg if it is of form fstatat64_fd
240234
if name.startswith(f'{syscall_basename}_'):
241235
name = name.partition('_')[-1]
@@ -247,7 +241,7 @@ def load_syscall(self):
247241

248242
# record syscall statistics
249243
self.utils.syscalls.setdefault(syscall_name, []).append({
250-
"params": dict(zip((f'param{i}' for i in range(6)), params)),
244+
"params": dict(zip(param_names, params)),
251245
"result": retval,
252246
"address": self.ql.reg.arch_pc,
253247
"return_address": None,
@@ -256,10 +250,10 @@ def load_syscall(self):
256250

257251
self.utils.syscalls_counter += 1
258252
else:
259-
self.ql.log.warning(f'{self.ql.reg.arch_pc:#x}: syscall {syscall_name} number = {syscall:#x}({syscall:d}) not implemented')
253+
self.ql.log.warning(f'{self.ql.reg.arch_pc:#x}: syscall {syscall_name} number = {syscall_id:#x}({syscall_id:d}) not implemented')
260254

261255
if self.ql.debug_stop:
262-
raise QlErrorSyscallNotFound("Syscall Not Found")
256+
raise QlErrorSyscallNotFound(f'Syscall not found: {syscall_name}')
263257

264258
def get_syscall(self) -> int:
265259
if self.ql.archtype == QL_ARCH.ARM:

qiling/os/posix/stat.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,30 @@
66
import os
77

88
class StatBase:
9-
def __init__(self):
10-
self._stat_buf = None
9+
def __init__(self, stat: os.stat_result):
10+
self._stat_buf = stat
1111

1212
# Never iterate this object!
1313
def __getitem__(self, key):
1414
if type(key) is not str:
1515
raise TypeError
16-
if not key.startswith("__") and key in dir(self._stat_buf):
16+
17+
if not key.startswith("__") and hasattr(self._stat_buf, key):
1718
return self._stat_buf.__getattribute__(key)
19+
1820
return 0
19-
21+
2022
def __getattr__(self, key):
2123
return self.__getitem__(key)
24+
2225
class Stat(StatBase):
2326
def __init__(self, path):
24-
super(Stat, self).__init__()
25-
self._stat_buf = os.stat(path)
27+
super().__init__(os.stat(path))
2628

2729
class Fstat(StatBase):
28-
def __init__(self, fd):
29-
super(Fstat, self).__init__()
30-
self._stat_buf = os.fstat(fd)
30+
def __init__(self, fd: int):
31+
super().__init__(os.fstat(fd))
3132

3233
class Lstat(StatBase):
3334
def __init__(self, path):
34-
super(Lstat, self).__init__()
35-
self._stat_buf = os.lstat(path)
36-
37-
35+
super().__init__(os.lstat(path))

qiling/os/posix/syscall/fcntl.py

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

6+
import os
7+
68
from qiling import Qiling
7-
from qiling.const import *
8-
from qiling.os.linux.thread import *
9-
from qiling.os.posix.filestruct import *
10-
from qiling.os.filestruct import *
9+
from qiling.const import QL_OS, QL_ARCH
10+
from qiling.exception import QlSyscallError
1111
from qiling.os.posix.const import *
12-
from qiling.os.posix.const_mapping import *
13-
from qiling.exception import *
12+
from qiling.os.posix.const_mapping import ql_open_flag_mapping, open_flags_mapping
13+
from qiling.os.posix.filestruct import ql_socket
1414

15-
def ql_syscall_open(ql: Qiling, filename, flags, mode, *args, **kw):
15+
def ql_syscall_open(ql: Qiling, filename: int, flags: int, mode: int):
1616
path = ql.os.utils.read_cstring(filename)
1717
real_path = ql.os.path.transform_to_real_path(path)
1818
relative_path = ql.os.path.transform_to_relative_path(path)
@@ -38,12 +38,13 @@ def ql_syscall_open(ql: Qiling, filename, flags, mode, *args, **kw):
3838
ql.log.debug("open(%s, %s, 0o%o) = %d" % (relative_path, open_flags_mapping(flags, ql.archtype), mode, regreturn))
3939

4040
if regreturn >= 0 and regreturn != 2:
41-
ql.log.debug("File Found: %s" % real_path)
41+
ql.log.debug(f'File found: {real_path:s}')
4242
else:
43-
ql.log.debug("File Not Found %s" % real_path)
43+
ql.log.debug(f'File not found {real_path:s}')
44+
4445
return regreturn
4546

46-
def ql_syscall_creat(ql: Qiling, filename, mode, *args, **kw):
47+
def ql_syscall_creat(ql: Qiling, filename: int, mode: int):
4748
flags = linux_open_flags["O_WRONLY"] | linux_open_flags["O_CREAT"] | linux_open_flags["O_TRUNC"]
4849

4950
path = ql.os.utils.read_cstring(filename)
@@ -71,13 +72,14 @@ def ql_syscall_creat(ql: Qiling, filename, mode, *args, **kw):
7172
ql.log.debug("creat(%s, %s, 0o%o) = %d" % (relative_path, open_flags_mapping(flags, ql.archtype), mode, regreturn))
7273

7374
if regreturn >= 0 and regreturn != 2:
74-
ql.log.debug("File Found: %s" % real_path)
75+
ql.log.debug(f'File found: {real_path:s}')
7576
else:
76-
ql.log.debug("File Not Found %s" % real_path)
77+
ql.log.debug(f'File not found {real_path:s}')
78+
7779
return regreturn
7880

79-
def ql_syscall_openat(ql: Qiling, fd, path, flags, mode, *args, **kw):
80-
path = ql.os.utils.read_cstring(path)
81+
def ql_syscall_openat(ql: Qiling, fd: int, path: int, flags: int, mode: int):
82+
file_path = ql.os.utils.read_cstring(path)
8183
# real_path = ql.os.path.transform_to_real_path(path)
8284
# relative_path = ql.os.path.transform_to_relative_path(path)
8385

@@ -99,28 +101,27 @@ def ql_syscall_openat(ql: Qiling, fd, path, flags, mode, *args, **kw):
99101
except:
100102
dir_fd = None
101103

102-
ql.os.fd[idx] = ql.os.fs_mapper.open_ql_file(path, flags, mode, dir_fd)
104+
ql.os.fd[idx] = ql.os.fs_mapper.open_ql_file(file_path, flags, mode, dir_fd)
103105
regreturn = idx
104106
except QlSyscallError as e:
105107
regreturn = -e.errno
106108

107-
ql.log.debug(f'openat(fd = {fd:d}, path = {path}, flags = {open_flags_mapping(flags, ql.archtype)}, mode = {mode:#o}) = {regreturn:d}')
109+
ql.log.debug(f'openat(fd = {fd:d}, path = {file_path}, flags = {open_flags_mapping(flags, ql.archtype)}, mode = {mode:#o}) = {regreturn:d}')
108110

109111
return regreturn
110112

111113

112-
def ql_syscall_fcntl(ql: Qiling, fcntl_fd, fcntl_cmd, fcntl_arg, *args, **kw):
113-
if not (0 <= fcntl_fd < NR_OPEN) or \
114-
ql.os.fd[fcntl_fd] == 0:
114+
def ql_syscall_fcntl(ql: Qiling, fd: int, cmd: int, arg: int):
115+
if not (0 <= fd < NR_OPEN) or ql.os.fd[fd] == 0:
115116
return -EBADF
116117

117-
f = ql.os.fd[fcntl_fd]
118-
119-
if fcntl_cmd == F_DUPFD:
120-
if 0 <= fcntl_arg < NR_OPEN:
118+
f = ql.os.fd[fd]
119+
120+
if cmd == F_DUPFD:
121+
if 0 <= arg < NR_OPEN:
121122
for idx, val in enumerate(ql.os.fd):
122-
if val == 0 and idx >= fcntl_arg:
123-
new_fd = ql.os.fd[fcntl_fd].dup()
123+
if val == 0 and idx >= arg:
124+
new_fd = ql.os.fd[fd].dup()
124125
ql.os.fd[idx] = new_fd
125126
regreturn = idx
126127
break
@@ -129,18 +130,18 @@ def ql_syscall_fcntl(ql: Qiling, fcntl_fd, fcntl_cmd, fcntl_arg, *args, **kw):
129130
else:
130131
regreturn = -EINVAL
131132

132-
elif fcntl_cmd == F_GETFD:
133+
elif cmd == F_GETFD:
133134
regreturn = getattr(f, "close_on_exec", 0)
134135

135-
elif fcntl_cmd == F_SETFD:
136-
f.close_on_exec = 1 if fcntl_arg & FD_CLOEXEC else 0
136+
elif cmd == F_SETFD:
137+
f.close_on_exec = 1 if arg & FD_CLOEXEC else 0
137138
regreturn = 0
138139

139-
elif fcntl_cmd == F_GETFL:
140-
regreturn = ql.os.fd[fcntl_fd].fcntl(fcntl_cmd, fcntl_arg)
140+
elif cmd == F_GETFL:
141+
regreturn = ql.os.fd[fd].fcntl(cmd, arg)
141142

142-
elif fcntl_cmd == F_SETFL:
143-
ql.os.fd[fcntl_fd].fcntl(fcntl_cmd, fcntl_arg)
143+
elif cmd == F_SETFL:
144+
ql.os.fd[fd].fcntl(cmd, arg)
144145
regreturn = 0
145146

146147
else:
@@ -149,45 +150,50 @@ def ql_syscall_fcntl(ql: Qiling, fcntl_fd, fcntl_cmd, fcntl_arg, *args, **kw):
149150
return regreturn
150151

151152

152-
def ql_syscall_fcntl64(ql: Qiling, fcntl_fd, fcntl_cmd, fcntl_arg, *args, **kw):
153+
def ql_syscall_fcntl64(ql: Qiling, fd: int, cmd: int, arg: int):
153154

154155
# https://linux.die.net/man/2/fcntl64
155-
if fcntl_cmd == F_DUPFD:
156-
if 0 <= fcntl_arg < NR_OPEN and 0 <= fcntl_fd < NR_OPEN:
157-
if ql.os.fd[fcntl_fd] != 0:
158-
new_fd = ql.os.fd[fcntl_fd].dup()
156+
if cmd == F_DUPFD:
157+
if 0 <= arg < NR_OPEN and 0 <= fd < NR_OPEN:
158+
if ql.os.fd[fd] != 0:
159+
new_fd = ql.os.fd[fd].dup()
159160
for idx, val in enumerate(ql.os.fd):
160-
if val == 0 and idx >= fcntl_arg:
161+
if val == 0 and idx >= arg:
161162
ql.os.fd[idx] = new_fd
162163
regreturn = idx
163164
break
164165
else:
165166
regreturn = -1
166167
else:
167168
regreturn = -1
168-
elif fcntl_cmd == F_GETFL:
169+
170+
elif cmd == F_GETFL:
169171
regreturn = 2
170-
elif fcntl_cmd == F_SETFL:
171-
if isinstance(ql.os.fd[fcntl_fd], ql_socket):
172-
ql.os.fd[fcntl_fd].fcntl(fcntl_cmd, fcntl_arg)
172+
173+
elif cmd == F_SETFL:
174+
if isinstance(ql.os.fd[fd], ql_socket):
175+
ql.os.fd[fd].fcntl(cmd, arg)
173176
regreturn = 0
174-
elif fcntl_cmd == F_GETFD:
177+
178+
elif cmd == F_GETFD:
175179
regreturn = 2
176-
elif fcntl_cmd == F_SETFD:
180+
181+
elif cmd == F_SETFD:
177182
regreturn = 0
183+
178184
else:
179185
regreturn = 0
180186

181187
return regreturn
182188

183189

184-
def ql_syscall_flock(ql, flock_fd, flock_operation, *args, **kw):
190+
def ql_syscall_flock(ql: Qiling, fd: int, operation: int):
185191
# Should always return 0, we don't need a actual file lock
186-
regreturn = 0
187-
return regreturn
188192

193+
return 0
189194

190-
def ql_syscall_rename(ql: Qiling, oldname_buf, newname_buf, *args, **kw):
195+
196+
def ql_syscall_rename(ql: Qiling, oldname_buf: int, newname_buf: int):
191197
"""
192198
rename(const char *oldpath, const char *newpath)
193199
description: change the name or location of a file
@@ -208,10 +214,8 @@ def ql_syscall_rename(ql: Qiling, oldname_buf, newname_buf, *args, **kw):
208214

209215
try:
210216
os.rename(old_realpath, new_realpath)
211-
regreturn = 0
212217
except OSError:
213-
ql.log.exception(f"rename(): {newpath} is exist!")
218+
ql.log.exception(f"rename(): {newpath} exists!")
214219
regreturn = -1
215220

216221
return regreturn
217-

0 commit comments

Comments
 (0)