forked from cuckoosandbox/cuckoo
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwindows.py
More file actions
314 lines (233 loc) · 10.8 KB
/
Copy pathwindows.py
File metadata and controls
314 lines (233 loc) · 10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# Copyright (C) 2010-2015 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
import logging
import datetime
from lib.cuckoo.common.abstracts import BehaviorHandler
from lib.cuckoo.common.netlog import BsonParser
log = logging.getLogger(__name__)
class MonitorProcessLog(list):
def __init__(self, eventstream):
self.eventstream = eventstream
self.first_seen = None
self.has_apicalls = False
def __iter__(self):
# call_id = 0
for event in self.eventstream:
if event["type"] == "process":
self.first_seen = event["first_seen"]
elif event["type"] == "apicall":
event["time"] = self.first_seen + datetime.timedelta(0, 0, event["time"] * 1000)
# backwards compat with previous reports, remove if not necessary
# event["repeated"] = 0
# event["timestamp"] = logtime(event.pop("time"))
# event["arguments"] = [dict(name=i, value=j) for i,j in event["arguments"].iteritems()]
# event["return"] = convert_to_printable(cleanup_value(event.pop("return_value")))
# event["is_success"] = bool(int(event.pop("status")))
# event["id"] = call_id
# call_id += 1
# Remove the event type for reporting output.
del event["type"]
# Get rid of the pid (we're below the pid in the structure).
del event["pid"]
# Get rid of the unique hash, this is only relevant
# for automation.
del event["uniqhash"]
yield event
def __nonzero__(self):
"""Required for the JSON reporting module as otherwise the on-demand
generated list of API calls would be seen as empty.
Note that the result structure is kept between processing and
reporting time which means that at reporting time, where this
functionality is actually needed, the has_apicalls will already have
been set while iterating through the BSON logs iterator in the parse()
function of the WindowsMonitor class. We use this knowledge to pass
along whether or not this log actually has API call events and thus
whether it's "nonzero" or not. (The correctness of this field is
required as otherwise the json.dump() function will fail - probably
due to buffering issues).
"""
return self.has_apicalls
class WindowsMonitor(BehaviorHandler):
"""Parses monitor generated logs."""
key = "processes"
def __init__(self, *args, **kwargs):
super(WindowsMonitor, self).__init__(*args, **kwargs)
self.processes = []
self.reconstructors = {}
self.matched = False
def handles_path(self, path):
if path.endswith(".bson"):
self.matched = True
return True
def parse(self, path):
# Invoke parsing of current log file.
parser = BsonParser(open(path, "rb"))
for event in parser:
if event["type"] == "process":
process = dict(event)
process["calls"] = MonitorProcessLog(parser)
self.processes.append(process)
self.reconstructors[process["pid"]] = BehaviorReconstructor()
# Create generic events out of the windows calls.
elif event["type"] == "apicall":
reconstructor = self.reconstructors[event["pid"]]
res = reconstructor.process_apicall(event)
if res and isinstance(res, tuple):
res = [res]
if res:
for category, arg in res:
yield {
"type": "generic",
"pid": event["pid"],
"category": category,
"value": arg,
}
# Indicate that the process has API calls. For more
# information on this matter, see also the __nonzero__ above.
process["calls"].has_apicalls = True
yield event
def run(self):
if not self.matched:
return
self.processes.sort(key=lambda process: process["first_seen"])
return self.processes
def NT_SUCCESS(value):
return value % 2**32 < 0x80000000
class BehaviorReconstructor(object):
"""Reconstructs the behavior of behavioral API logs."""
def __init__(self):
self.files = {}
def process_apicall(self, event):
fn = getattr(self, "_api_%s" % event["api"], None)
if fn is not None:
return fn(event["return_value"], event["arguments"])
# Generic file & directory stuff.
def _api_CreateDirectoryW(self, return_value, arguments):
return ("directory_created", arguments["dirpath"])
_api_CreateDirectoryExW = _api_CreateDirectoryW
def _api_RemoveDirectoryA(self, return_value, arguments):
return ("directory_removed", arguments["dirpath"])
_api_RemoveDirectoryW = _api_RemoveDirectoryA
def _api_MoveFileWithProgressW(self, return_value, arguments):
return ("file_moved", (arguments["oldfilepath"],
arguments["newfilepath"]))
def _api_CopyFileA(self, return_value, arguments):
return ("file_copied", (arguments["oldfilepath"],
arguments["newfilepath"]))
_api_CopyFileW = _api_CopyFileA
_api_CopyFileExW = _api_CopyFileA
def _api_DeleteFileA(self, return_value, arguments):
return ("file_deleted", arguments["filepath"])
_api_DeleteFileW = _api_DeleteFileA
_api_NtDeleteFile = _api_DeleteFileA
def _api_FindFirstFileExA(self, return_value, arguments):
return ("directory_enumerated", arguments["filepath"])
_api_FindFirstFileExW = _api_FindFirstFileExA
def _api_LdrLoadDll(self, return_value, arguments):
return ("dll_loaded", arguments["module_name"])
def _api_NtCreateFile(self, return_value, arguments):
self.files[arguments["file_handle"]] = arguments["filepath"]
return [
("file_opened", arguments["filepath"]),
("file_exists", arguments["filepath"]),
]
_api_NtOpenFile = _api_NtCreateFile
def _api_NtReadFile(self, return_value, arguments):
h = arguments["file_handle"]
if NT_SUCCESS(return_value) and h in self.files:
return ("file_read", self.files[h])
def _api_NtWriteFile(self, return_value, arguments):
h = arguments["file_handle"]
if NT_SUCCESS(return_value) and h in self.files:
return ("file_written", self.files[h])
def _api_GetFileAttributesW(self, return_value, arguments):
return ("file_exists", arguments["filepath"])
_api_GetFileAttributesExW = _api_GetFileAttributesW
# Registry stuff.
def _api_RegOpenKeyExA(self, return_value, arguments):
return ("regkey_opened", arguments["regkey"])
_api_RegOpenKeyExW = _api_RegOpenKeyExA
_api_RegCreateKeyExA = _api_RegOpenKeyExA
_api_RegCreateKeyExW = _api_RegOpenKeyExA
def _api_RegDeleteKeyA(self, return_value, arguments):
return ("regkey_deleted", arguments["regkey"])
_api_RegDeleteKeyW = _api_RegDeleteKeyA
_api_RegDeleteValueA = _api_RegDeleteKeyA
_api_RegDeleteValueW = _api_RegDeleteKeyA
_api_NtDeleteValueKey = _api_RegDeleteKeyA
def _api_RegQueryValueExA(self, return_value, arguments):
return ("regkey_read", arguments["regkey"])
_api_RegQueryValueExW = _api_RegQueryValueExA
_api_NtQueryValueKey = _api_RegQueryValueExA
def _api_RegSetValueExA(self, return_value, arguments):
return ("regkey_written", arguments["regkey"])
_api_RegSetValueExW = _api_RegSetValueExA
_api_NtSetValueKey = _api_RegSetValueExA
def _api_NtClose(self, return_value, arguments):
self.files.pop(arguments["handle"], None)
# Network stuff.
def _api_URLDownloadToFileW(self, return_value, arguments):
return [
("downloads_file", arguments["url"]),
("file_opened", arguments["filepath"]),
("file_written", arguments["filepath"]),
]
def _api_InternetConnectA(self, return_value, arguments):
return ("connects_host", arguments["hostname"])
_api_InternetConnectW = _api_InternetConnectA
def _api_InternetOpenUrlA(self, return_value, arguments):
return ("fetches_url", arguments["url"])
_api_InternetOpenUrlW = _api_InternetOpenUrlA
def _api_DnsQuery_A(self, return_value, arguments):
if arguments["hostname"]:
return ("resolves_host", arguments["hostname"])
_api_DnsQuery_W = _api_DnsQuery_A
_api_DnsQuery_UTF8 = _api_DnsQuery_A
_api_getaddrinfo = _api_DnsQuery_A
_api_GetAddrInfoW = _api_DnsQuery_A
_api_gethostbyname = _api_DnsQuery_A
def _api_connect(self, return_value, arguments):
return ("connects_ip", arguments["ip_address"])
# Mutex stuff
def _api_NtCreateMutant(self, return_value, arguments):
if arguments["mutant_name"]:
return ("mutex", arguments["mutant_name"])
_api_ConnectEx = _api_connect
# Process stuff.
def _api_CreateProcessInternalW(self, return_value, arguments):
cmdline = arguments["command_line"] or arguments["filepath"]
return ("command_line", cmdline)
def _api_ShellExecuteExW(self, return_value, arguments):
if arguments["parameters"]:
cmdline = "%s %s" % (arguments["filepath"], arguments["parameters"])
else:
cmdline = arguments["filepath"]
return ("command_line", cmdline)
def _api_system(self, return_value, arguments):
return ("command_line", arguments["command"])
# WMI stuff.
def _api_IWbemServices_ExecQuery(self, return_value, arguments):
return ("wmi_query", arguments["query"])
def _api_IWbemServices_ExecQueryAsync(self, return_value, arguments):
return ("wmi_query", arguments["query"])
# GUIDs.
def _api_CoCreateInstance(self, return_value, arguments):
# The iid vs riid is to be removed later on and should be just iid.
return [
("guid", arguments["clsid"]),
("guid", arguments.get("iid", arguments.get("riid"))),
]
def _api_CoCreateInstanceEx(self, return_value, arguments):
ret = [
("guid", arguments["clsid"]),
]
for iid in arguments["iid"]:
ret.append(("guid", iid))
return ret
def _api_CoGetClassObject(self, return_value, arguments):
# The iid vs riid is to be removed later on and should be just iid.
return [
("guid", arguments["clsid"]),
("guid", arguments.get("iid", arguments.get("riid"))),
]