/src/wireshark/wiretap/procmon.c
Line | Count | Source |
1 | | /** procmon.c |
2 | | * |
3 | | * Implements reading of MS Procmon files |
4 | | * Used a lot of information from https://github.com/eronnen/procmon-parser |
5 | | * |
6 | | * Wiretap Library |
7 | | * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu> |
8 | | * |
9 | | * SPDX-License-Identifier: GPL-2.0-or-later |
10 | | */ |
11 | | |
12 | | #include "config.h" |
13 | 0 | #define WS_LOG_DOMAIN LOG_DOMAIN_WIRETAP |
14 | | |
15 | | #include "procmon.h" |
16 | | #include "file_wrappers.h" |
17 | | #include "wtap_module.h" |
18 | | #include "pcapng_module.h" |
19 | | |
20 | | #include <wsutil/buffer.h> |
21 | | |
22 | | // To do: |
23 | | // - Figure out module timestamps |
24 | | // - Read the ports array? Is there any advantage to doing that vs our built in |
25 | | // port number resolution? |
26 | | |
27 | | #pragma pack(push,1) |
28 | | typedef struct procmon_header_s { |
29 | | uint32_t signature; // Magic Signature - 'PML_' |
30 | | uint32_t version; // Version of the PML file. 9 in the current version. |
31 | | uint32_t system_bitness; // System bitness: 1 if the system is 64 bit, 0 otherwise. |
32 | | uint16_t computer_name[16]; // Name of the computer (that did the capture). |
33 | | uint16_t system_root_path[260]; // System root path (e.g. "C:\Windows"). |
34 | | uint32_t num_events; // Total number of events in the log file. |
35 | | uint64_t unused; // ? (seems to be unused) |
36 | | uint64_t start_events_offset; // File offset to the start of the events array. |
37 | | uint64_t event_offsets_array_offset;// File offset to an array of offsets to all the events. |
38 | | uint64_t process_array_offset; // File offset to the array of processes. |
39 | | uint64_t string_array_offset; // File offset to the array of strings. |
40 | | uint64_t icon_array_offset; // File offset to the icons array. |
41 | | uint64_t maximum_user_address; // SYSTEM_INFO.lpMaximumApplicationAddress: Maximum User Address |
42 | | uint32_t os_version_info_size; // OSVERSIONINFOEXW.dwOSVersionInfoSize: sizeof(OSVERSIONINFOEXW) |
43 | | uint32_t major_version; // OSVERSIONINFOEXW.dwMajorVersion: Major version number of the operating system. |
44 | | uint32_t minor_version; // OSVERSIONINFOEXW.dwMinorVersion: Minor version number of the operating system. |
45 | | uint32_t build_number; // OSVERSIONINFOEXW.dwBuildNumber: Build number of the operating system. |
46 | | uint32_t platform_id; // OSVERSIONINFOEXW.dwPlatformId: Operating system platform. |
47 | | uint16_t csd_version[128]; // OSVERSIONINFOEXW.szCSDVersion: Indicates the latest Service Pack installed. |
48 | | uint16_t service_pack_major; // OSVERSIONINFOEXW.wServicePackMajor: Major version number of the latest Service Pack. |
49 | | uint16_t service_pack_minor; // OSVERSIONINFOEXW.wServicePackMinor: Minor version number of the latest Service Pack. |
50 | | uint16_t suite_mask; // OSVERSIONINFOEXW.wSuiteMask: Bit mask that identifies the product suites available. |
51 | | uint8_t product_type; // OSVERSIONINFOEXW.wProductType: Additional information about the system. |
52 | | uint8_t version_reserved; // OSVERSIONINFOEXW.wReserved: Reserved for future use. |
53 | | uint32_t num_processors; // SYSTEM_INFO.dwNumberOfProcessors: Number of logical processors. |
54 | | uint64_t total_physical_memory; // MEMORYSTATUSEX.ullTotalPhys: Total physical memory (in bytes). |
55 | | uint64_t start_events_offset_dup; // File offset to the start of the events array (again). |
56 | | uint64_t host_port_array_offset; // File offset to hosts and ports arrays. |
57 | | } procmon_header_t; |
58 | | |
59 | | typedef enum { |
60 | | PROCMON_EVENT_TYPE_UNKNOWN = 0, |
61 | | PROCMON_EVENT_TYPE_PROCESS = 1, |
62 | | PROCMON_EVENT_TYPE_REGISTRY = 2, |
63 | | PROCMON_EVENT_TYPE_FILE_SYSTEM = 3, |
64 | | PROCMON_EVENT_TYPE_PROFILING = 4, |
65 | | PROCMON_EVENT_TYPE_NETWORK = 5, |
66 | | } procmon_event_class_type_t; |
67 | | |
68 | | typedef struct procmon_event_header_s { |
69 | | uint32_t process_index; // The index to the process of the event. |
70 | | uint32_t thread_id; // Thread Id. |
71 | | uint32_t event_class; // Event class (of type procmon_event_class_type_t) |
72 | | uint16_t operation_type; // Operation type (dependent on the event class) |
73 | | uint8_t unknown[6]; // Unknown |
74 | | uint64_t duration; // Duration of the operation in 100 nanoseconds interval. |
75 | | uint64_t timestamp; // The time when the event was captured (in FILETIME format) |
76 | | uint32_t event_result; // The value of the event result. |
77 | | uint16_t stack_trace_depth; // The depth of the captured stack trace. |
78 | | uint16_t unknown3; // Unknown |
79 | | uint32_t details_size; // The size of the specific detail structure (contains path and other details) |
80 | | uint32_t extra_details_offset; // The offset from the start of the event to extra detail structure (not necessarily continuous with this structure). |
81 | | |
82 | | } procmon_event_header_t; |
83 | | #pragma pack(pop) |
84 | | |
85 | | typedef struct { |
86 | | uint32_t process_index; |
87 | | uint32_t process_id; |
88 | | uint32_t parent_process_id; |
89 | | uint32_t parent_process_index; |
90 | | uint64_t authentication_id; |
91 | | uint32_t session_number; |
92 | | uint32_t unknown1; |
93 | | uint64_t start_time; // FILETIME |
94 | | uint64_t end_time; // FILETIME |
95 | | uint32_t is_virtualized; |
96 | | uint32_t is_64_bit; |
97 | | uint32_t integrity_si; |
98 | | uint32_t user_name_si; |
99 | | uint32_t process_name_si; |
100 | | uint32_t image_path_si; |
101 | | uint32_t command_line_si; |
102 | | uint32_t company_si; |
103 | | uint32_t version_si; |
104 | | uint32_t description_si; |
105 | | uint32_t icon_index_big; |
106 | | uint32_t icon_index_small; |
107 | | } procmon_raw_process_t; |
108 | | |
109 | | typedef struct { |
110 | | uint32_t unknown1; |
111 | | uint32_t base_address; |
112 | | uint32_t size; |
113 | | uint32_t image_path_si; |
114 | | uint32_t version_si; |
115 | | uint32_t company_si; |
116 | | uint32_t description_si; |
117 | | uint32_t timestamp; |
118 | | uint64_t unknown2[3]; |
119 | | } procmon_raw_module_32_t; |
120 | | |
121 | | typedef struct { |
122 | | uint64_t unknown1; |
123 | | uint64_t base_address; |
124 | | uint32_t size; |
125 | | uint32_t image_path_si; |
126 | | uint32_t version_si; |
127 | | uint32_t company_si; |
128 | | uint32_t description_si; |
129 | | uint32_t timestamp; |
130 | | uint64_t unknown2[3]; |
131 | | } procmon_raw_module_64_t; |
132 | | |
133 | | typedef struct { |
134 | | procmon_header_t header; |
135 | | uint32_t *event_offsets; |
136 | | uint32_t cur_event; |
137 | | const char **string_array; |
138 | | size_t string_array_size; |
139 | | uint32_t *process_index_map; /* Map of process index to process array index */ |
140 | | size_t process_index_map_size; |
141 | | struct procmon_process_t *process_array; |
142 | | size_t process_array_size; |
143 | | } procmon_file_info_t; |
144 | | |
145 | 0 | #define COMMON_EVENT_STRUCT_SIZE 52 |
146 | | // Most of these are arbitrary |
147 | 0 | #define MAX_PROCMON_EVENTS (500 * 1000 * 1000) |
148 | 0 | #define MAX_PROCMON_STRINGS (1000 * 1000) |
149 | 0 | #define MAX_PROCMON_STRING_LENGTH 8192 |
150 | 0 | #define MAX_PROCMON_PROCESSES (500 * 1000) |
151 | | #define MAX_PROCMON_MODULES 1000 |
152 | | |
153 | | static int procmon_file_type_subtype = -1; |
154 | | |
155 | | void register_procmon(void); |
156 | | |
157 | | static void file_info_cleanup(procmon_file_info_t* file_info) |
158 | 0 | { |
159 | 0 | g_free(file_info->event_offsets); |
160 | 0 | g_free(file_info->string_array); |
161 | 0 | g_free(file_info->process_index_map); |
162 | 0 | if (file_info->process_array) { |
163 | 0 | for (size_t idx = 0; idx < file_info->process_array_size; idx++) { |
164 | 0 | g_free(file_info->process_array[idx].modules); |
165 | 0 | } |
166 | 0 | g_free(file_info->process_array); |
167 | 0 | } |
168 | 0 | g_free(file_info); |
169 | 0 | } |
170 | | |
171 | | static const char *procmon_string(procmon_file_info_t* file_info, uint32_t str_index) |
172 | 0 | { |
173 | 0 | if (str_index >= file_info->string_array_size) { |
174 | 0 | return "<unknown>"; |
175 | 0 | } |
176 | 0 | return file_info->string_array[str_index]; |
177 | 0 | } |
178 | | |
179 | | static char *procmon_read_string(FILE_T fh, gunichar2 *str_buf, int *err, char **err_info) |
180 | 0 | { |
181 | 0 | uint32_t cur_str_size; |
182 | 0 | if (!wtap_read_bytes_or_eof(fh, &cur_str_size, sizeof(cur_str_size), err, err_info)) |
183 | 0 | { |
184 | 0 | ws_debug("wtap_read_bytes_or_eof() failed, err = %d.", *err); |
185 | 0 | if (*err == 0 || *err == WTAP_ERR_SHORT_READ) |
186 | 0 | { |
187 | | // Short read or EOF. |
188 | 0 | *err = 0; |
189 | 0 | g_free(*err_info); |
190 | 0 | *err_info = NULL; |
191 | 0 | } |
192 | 0 | return NULL; |
193 | 0 | } |
194 | 0 | cur_str_size = GUINT32_FROM_LE(cur_str_size); |
195 | 0 | if (cur_str_size > MAX_PROCMON_STRING_LENGTH) |
196 | 0 | { |
197 | 0 | if (file_seek(fh, cur_str_size - MAX_PROCMON_STRING_LENGTH, SEEK_CUR, err) == -1) |
198 | 0 | { |
199 | 0 | ws_debug("Failed to skip excess string data"); |
200 | 0 | return NULL; |
201 | 0 | } |
202 | 0 | ws_debug("Truncating string from %u bytes to %u", cur_str_size, MAX_PROCMON_STRING_LENGTH); |
203 | 0 | cur_str_size = MAX_PROCMON_STRING_LENGTH; |
204 | 0 | } |
205 | 0 | if (!wtap_read_bytes_or_eof(fh, str_buf, cur_str_size, err, err_info)) |
206 | 0 | { |
207 | 0 | ws_debug("wtap_read_bytes_or_eof() failed, err = %d.", *err); |
208 | 0 | if (*err == 0 || *err == WTAP_ERR_SHORT_READ) |
209 | 0 | { |
210 | | // Short read or EOF. |
211 | 0 | *err = 0; |
212 | 0 | g_free(*err_info); |
213 | 0 | *err_info = NULL; |
214 | 0 | } |
215 | 0 | return NULL; |
216 | 0 | } |
217 | 0 | char *utf8_str = g_convert_with_fallback((const char *)str_buf, cur_str_size, "UTF-8", "UTF-16LE", "?", NULL, NULL, NULL); |
218 | 0 | return utf8_str ? utf8_str : g_strdup("<invalid>"); |
219 | 0 | } |
220 | | |
221 | | // Read the hosts array. Assume failures here are non-fatal. |
222 | | static void procmon_read_hosts(wtap *wth, int64_t host_port_array_offset, int *err, char **err_info) |
223 | 0 | { |
224 | 0 | if (!(wth->add_new_ipv4 && wth->add_new_ipv6)) { |
225 | 0 | return; |
226 | 0 | } |
227 | | |
228 | 0 | if (file_seek(wth->fh, host_port_array_offset, SEEK_SET, err) == -1) |
229 | 0 | { |
230 | 0 | ws_debug("Failed to locate procmon hosts+ports data"); |
231 | 0 | return; |
232 | 0 | } |
233 | 0 | uint32_t num_hosts; |
234 | 0 | if (!wtap_read_bytes_or_eof(wth->fh, &num_hosts, sizeof(num_hosts), err, err_info)) |
235 | 0 | { |
236 | 0 | ws_debug("wtap_read_bytes_or_eof() failed, err = %d.", *err); |
237 | 0 | return; |
238 | 0 | } |
239 | 0 | num_hosts = GUINT32_FROM_LE(num_hosts); |
240 | 0 | if (num_hosts > MAX_PROCMON_STRINGS) |
241 | 0 | { |
242 | 0 | ws_debug("Truncating hosts from %u to %u", num_hosts, MAX_PROCMON_STRINGS); |
243 | 0 | num_hosts = MAX_PROCMON_STRINGS; |
244 | 0 | } |
245 | 0 | gunichar2 *str_buf = g_new(gunichar2, MAX_PROCMON_STRING_LENGTH); |
246 | | // Procmon appears to use the hosts table to store ASCII representations of |
247 | | // addresses, so skip those. |
248 | 0 | GRegex *numeric_re = g_regex_new("^([0-9.]+|.*:.*)$", (GRegexCompileFlags)(G_REGEX_CASELESS | G_REGEX_RAW | G_REGEX_OPTIMIZE), (GRegexMatchFlags)0, NULL); |
249 | 0 | for (unsigned idx = 0; idx < num_hosts; idx++) |
250 | 0 | { |
251 | 0 | ws_in6_addr addr; |
252 | 0 | if (!wtap_read_bytes_or_eof(wth->fh, &addr, sizeof(addr), err, err_info)) |
253 | 0 | { |
254 | 0 | ws_debug("wtap_read_bytes_or_eof() failed, err = %d.", *err); |
255 | 0 | g_regex_unref(numeric_re); |
256 | 0 | g_free(str_buf); |
257 | 0 | return; |
258 | 0 | } |
259 | 0 | char *name = procmon_read_string(wth->fh, str_buf, err, err_info); |
260 | 0 | if (!name) { |
261 | 0 | continue; |
262 | 0 | } |
263 | 0 | if (g_regex_match(numeric_re, name, (GRegexMatchFlags)0, NULL)) |
264 | 0 | { |
265 | 0 | g_free(name); |
266 | 0 | continue; |
267 | 0 | } |
268 | | // The PML format gives us a 16 byte blob with no indication as to |
269 | | // whether or not the blob is a v4 or v6 address. Given that there are |
270 | | // several pingable v6 addresses that end with 12 bytes of zeroes (at |
271 | | // the time of this writing 2600::, 2409::, 2a09::, and 2a11:: are all |
272 | | // pingable), let's assume that all addresses are v6 and ones that end |
273 | | // with 12 bytes of zeroes are also v4. |
274 | 0 | wth->add_new_ipv6(&addr, name, false); |
275 | 0 | if (!*(uint32_t*)(&addr.bytes[4]) && !*(uint64_t*)(&addr.bytes[8])) { |
276 | 0 | ws_in4_addr v4addr = *(uint32_t *)(&addr.bytes[4]); |
277 | 0 | wth->add_new_ipv4(v4addr, name, false); |
278 | 0 | } |
279 | 0 | g_free(name); |
280 | 0 | } |
281 | 0 | g_regex_unref(numeric_re); |
282 | 0 | g_free(str_buf); |
283 | 0 | } |
284 | | |
285 | | static bool procmon_read_event(FILE_T fh, wtap_rec* rec, procmon_file_info_t* file_info, int* err, char** err_info) |
286 | 0 | { |
287 | 0 | wtapng_block_t wblock; |
288 | 0 | procmon_event_header_t event_header; |
289 | |
|
290 | 0 | wblock.rec = rec; |
291 | |
|
292 | 0 | wblock.block = wtap_block_create(WTAP_BLOCK_FT_SPECIFIC_EVENT); |
293 | |
|
294 | 0 | wblock.rec->presence_flags = WTAP_HAS_CAP_LEN; |
295 | 0 | wblock.rec->tsprec = WTAP_TSPREC_NSEC; |
296 | | |
297 | | /* Read the event header */ |
298 | 0 | if (!wtap_read_bytes(fh, &event_header, sizeof event_header, err, err_info)) { |
299 | 0 | ws_debug("Failed to read procmon process index"); |
300 | 0 | return false; |
301 | 0 | } |
302 | | |
303 | | /* Append the raw data of the event header */ |
304 | 0 | ws_buffer_append(&wblock.rec->data, (const uint8_t*)&event_header, sizeof event_header); |
305 | |
|
306 | 0 | wblock.rec->presence_flags |= WTAP_HAS_TS; |
307 | 0 | filetime_to_nstime(&wblock.rec->ts, GUINT64_FROM_LE(event_header.timestamp)); |
308 | | |
309 | | /* Read stack trace data */ |
310 | 0 | uint32_t sizeof_stacktrace = event_header.stack_trace_depth * (file_info->header.system_bitness ? 8 : 4); |
311 | | |
312 | | /* Append the size of the stack trace data so the dissector doesn't need to know about system bitness */ |
313 | 0 | ws_buffer_append(&wblock.rec->data, (const uint8_t*)&sizeof_stacktrace, sizeof sizeof_stacktrace); |
314 | |
|
315 | 0 | if (!wtap_read_bytes_buffer(fh, &wblock.rec->data, sizeof_stacktrace, err, err_info)) { |
316 | 0 | ws_debug("Failed to read procmon stack trace data"); |
317 | 0 | return false; |
318 | 0 | } |
319 | | |
320 | | /* Read detail data */ |
321 | 0 | if (!wtap_read_bytes_buffer(fh, &wblock.rec->data, event_header.details_size, err, err_info)) { |
322 | 0 | ws_debug("Failed to read procmon detail data"); |
323 | 0 | return false; |
324 | 0 | } |
325 | | |
326 | 0 | if (event_header.extra_details_offset > 0) |
327 | 0 | { |
328 | 0 | int64_t current_offset = file_tell(fh); |
329 | | |
330 | | /* The extra details structure surprisingly can be separated from the event structure */ |
331 | 0 | int64_t real_details_offset = event_header.extra_details_offset - (COMMON_EVENT_STRUCT_SIZE + event_header.details_size + sizeof_stacktrace); |
332 | 0 | if (file_seek(fh, real_details_offset, SEEK_CUR, err) == -1) { |
333 | 0 | ws_debug("Failed to locate procmon extra details data"); |
334 | 0 | return false; |
335 | 0 | } |
336 | | /* However, pass the record data up as if it's consecutive */ |
337 | 0 | uint16_t extra_details_stream_size; |
338 | 0 | if (!wtap_read_bytes(fh, &extra_details_stream_size, sizeof extra_details_stream_size, err, err_info)) { |
339 | 0 | ws_debug("Failed to read procmon extra details offset"); |
340 | 0 | return false; |
341 | 0 | } |
342 | 0 | ws_buffer_append(&wblock.rec->data, (const uint8_t*)&extra_details_stream_size, sizeof extra_details_stream_size); |
343 | |
|
344 | 0 | if (!wtap_read_bytes_buffer(fh, &wblock.rec->data, extra_details_stream_size, err, err_info)) { |
345 | 0 | ws_debug("Failed to read procmon extra detail data"); |
346 | 0 | return false; |
347 | 0 | } |
348 | | |
349 | | /* If the extra data doesn't immediately follow the other data */ |
350 | 0 | if (real_details_offset != 0) |
351 | 0 | { |
352 | 0 | if (file_seek(fh, current_offset, SEEK_SET, err) == -1) { |
353 | 0 | ws_debug("Failed to restore procmon event data location"); |
354 | 0 | return false; |
355 | 0 | } |
356 | 0 | } |
357 | 0 | } |
358 | | |
359 | | /* |
360 | | * We return these to the caller in procmon_read(). |
361 | | */ |
362 | 0 | wtap_setup_ft_specific_event_rec(wblock.rec, procmon_file_type_subtype, event_header.event_class); |
363 | 0 | wblock.rec->rec_header.ft_specific_header.record_len = (uint32_t)ws_buffer_length(&wblock.rec->data); |
364 | 0 | wblock.rec->rec_header.ft_specific_header.pseudo_header.procmon.process_index_map = file_info->process_index_map; |
365 | 0 | wblock.rec->rec_header.ft_specific_header.pseudo_header.procmon.process_index_map_size = file_info->process_index_map_size; |
366 | 0 | wblock.rec->rec_header.ft_specific_header.pseudo_header.procmon.process_array = file_info->process_array; |
367 | 0 | wblock.rec->rec_header.ft_specific_header.pseudo_header.procmon.process_array_size = file_info->process_array_size; |
368 | 0 | wblock.rec->rec_header.ft_specific_header.pseudo_header.procmon.system_bitness = (file_info->header.system_bitness != 0); |
369 | 0 | wblock.internal = false; |
370 | | |
371 | | /* |
372 | | * We want dissectors (particularly packet_frame) to be able to |
373 | | * access packet comments and whatnot that are in the block. wblock->block |
374 | | * will be unref'd by procmon_seek_read(), so move the block to where |
375 | | * dissectors can find it. |
376 | | */ |
377 | 0 | wblock.rec->block = wblock.block; |
378 | 0 | wblock.block = NULL; |
379 | 0 | return true; |
380 | 0 | } |
381 | | |
382 | | static bool procmon_read(wtap *wth, wtap_rec *rec, |
383 | | int *err, char **err_info, int64_t *data_offset) |
384 | 0 | { |
385 | 0 | procmon_file_info_t* file_info = (procmon_file_info_t*)wth->priv; |
386 | | |
387 | | // file.c and strato.c call wtap_set_cb_new_ipv{4,6} after calling |
388 | | // wtap_open_offline, so read our hosts array here. |
389 | 0 | if (file_info->cur_event == 0) { |
390 | 0 | procmon_read_hosts(wth, file_info->header.host_port_array_offset, err, err_info); |
391 | 0 | } |
392 | | |
393 | | /* Stop processing once our offset reaches past events (or the file is malformed) */ |
394 | 0 | if (file_info->cur_event >= file_info->header.num_events) |
395 | 0 | { |
396 | 0 | ws_debug("end of events"); |
397 | 0 | return false; |
398 | 0 | } |
399 | | |
400 | 0 | *data_offset = file_info->event_offsets[file_info->cur_event]; |
401 | 0 | ws_noisy("file offset is %" PRId64 " array offset is %" PRId64, file_tell(wth->fh), *data_offset); |
402 | |
|
403 | 0 | if (file_seek(wth->fh, *data_offset, SEEK_SET, err) == -1) |
404 | 0 | { |
405 | 0 | ws_debug("Failed to seek to event %u at offsets %" PRId64, file_info->cur_event, *data_offset); |
406 | 0 | return false; |
407 | 0 | } |
408 | | |
409 | 0 | file_info->cur_event++; |
410 | | |
411 | | // if (*data_offset+COMMON_EVENT_STRUCT_SIZE >= (int64_t)file_info->header.event_offsets_array_offset) { |
412 | | // *err = WTAP_ERR_BAD_FILE; |
413 | | // *err_info = ws_strdup_printf("procmon: Not enough room for event content at offset %" PRIi64, *data_offset); |
414 | | // return false; |
415 | | // } |
416 | |
|
417 | 0 | return procmon_read_event(wth->fh, rec, file_info, err, err_info); |
418 | 0 | } |
419 | | |
420 | | static bool procmon_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec, |
421 | | int *err, char **err_info) |
422 | 0 | { |
423 | 0 | procmon_file_info_t* file_info = (procmon_file_info_t*)wth->priv; |
424 | | |
425 | | /* seek to the right file position */ |
426 | 0 | if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) < 0) { |
427 | 0 | return false; /* Seek error */ |
428 | 0 | } |
429 | 0 | ws_noisy("reading at offset %" PRIu64, seek_off); |
430 | |
|
431 | 0 | return procmon_read_event(wth->random_fh, rec, file_info, err, err_info); |
432 | 0 | } |
433 | | |
434 | | static const uint8_t procmon_magic[] = { 'P', 'M', 'L', '_' }; |
435 | | |
436 | | wtap_open_return_val procmon_open(wtap *wth, int *err, char **err_info) |
437 | 0 | { |
438 | 0 | procmon_file_info_t* file_info = g_new0(procmon_file_info_t, 1); |
439 | 0 | procmon_header_t* header = &file_info->header; |
440 | |
|
441 | 0 | ws_debug("opening file"); |
442 | | /* |
443 | | * First, try to read the procmon header. |
444 | | */ |
445 | 0 | if (!wtap_read_bytes_or_eof(wth->fh, header, sizeof(procmon_header_t), err, err_info)) |
446 | 0 | { |
447 | 0 | file_info_cleanup(file_info); |
448 | 0 | ws_debug("wtap_read_bytes_or_eof() failed, err = %d.", *err); |
449 | 0 | if (*err == 0 || *err == WTAP_ERR_SHORT_READ) { |
450 | | /* |
451 | | * Short read or EOF. |
452 | | * |
453 | | * We're reading this as part of an open, so |
454 | | * the file is too short to be a procmon file. |
455 | | */ |
456 | 0 | *err = 0; |
457 | 0 | g_free(*err_info); |
458 | 0 | *err_info = NULL; |
459 | 0 | } |
460 | 0 | return WTAP_OPEN_NOT_MINE; |
461 | 0 | } |
462 | | |
463 | 0 | if (memcmp(&header->signature, procmon_magic, sizeof(procmon_magic))) |
464 | 0 | { |
465 | 0 | file_info_cleanup(file_info); |
466 | 0 | return WTAP_OPEN_NOT_MINE; |
467 | 0 | } |
468 | | |
469 | | #if G_BYTE_ORDER == G_BIG_ENDIAN |
470 | | header->version = GUINT32_SWAP_LE_BE(header->version); |
471 | | header->system_bitness = GUINT32_SWAP_LE_BE(header->system_bitness); |
472 | | header->num_events = GUINT32_SWAP_LE_BE(header->num_events); |
473 | | header->start_events_offset = GUINT64_SWAP_LE_BE(header->start_events_offset); |
474 | | header->event_offsets_array_offset = GUINT64_SWAP_LE_BE(header->event_offsets_array_offset); |
475 | | header->process_array_offset = GUINT64_SWAP_LE_BE(header->process_array_offset); |
476 | | header->string_array_offset = GUINT64_SWAP_LE_BE(header->string_array_offset); |
477 | | header->icon_array_offset = GUINT64_SWAP_LE_BE(header->icon_array_offset); |
478 | | header->maximum_user_address = GUINT64_SWAP_LE_BE(header->maximum_user_address); |
479 | | header->os_version_info_size = GUINT32_SWAP_LE_BE(header->os_version_info_size); |
480 | | header->major_version = GUINT32_SWAP_LE_BE(header->major_version); |
481 | | header->minor_version = GUINT32_SWAP_LE_BE(header->minor_version); |
482 | | header->build_number = GUINT32_SWAP_LE_BE(header->build_number); |
483 | | header->platform_id = GUINT32_SWAP_LE_BE(header->platform_id); |
484 | | header->service_pack_major = GUINT16_SWAP_LE_BE(header->service_pack_major); |
485 | | header->service_pack_minor = GUINT16_SWAP_LE_BE(header->service_pack_minor); |
486 | | header->suite_mask = GUINT16_SWAP_LE_BE(header->suite_mask); |
487 | | header->num_processors = GUINT32_SWAP_LE_BE(header->num_processors); |
488 | | header->total_physical_memory = GUINT64_SWAP_LE_BE(header->total_physical_memory); |
489 | | header->start_events_offset_dup = GUINT64_SWAP_LE_BE(header->start_events_offset_dup); |
490 | | header->host_port_array_offset = GUINT64_SWAP_LE_BE(header->host_port_array_offset); |
491 | | #endif |
492 | | |
493 | 0 | if (header->num_events > MAX_PROCMON_EVENTS) { |
494 | 0 | ws_debug("Truncating events from %u to %u", header->num_events, MAX_PROCMON_EVENTS); |
495 | 0 | header->num_events = MAX_PROCMON_EVENTS; |
496 | 0 | } |
497 | | |
498 | | // Read the event offsets array, which we use in procmon_read(). It's not clear |
499 | | // if we really need this; in a test capture here the offsets in the array were |
500 | | // identical to the file positions we end up with if we just read sequentially. |
501 | 0 | if (file_seek(wth->fh, header->event_offsets_array_offset, SEEK_SET, err) == -1) |
502 | 0 | { |
503 | 0 | file_info_cleanup(file_info); |
504 | 0 | ws_debug("Failed to locate event offsets data"); |
505 | 0 | return WTAP_OPEN_NOT_MINE; |
506 | 0 | } |
507 | 0 | file_info->event_offsets = g_new(uint32_t, header->num_events); |
508 | 0 | for (unsigned idx = 0; idx < header->num_events; idx++) { |
509 | 0 | uint32_t event_offset; |
510 | | // Each offset entry is a uint32_t offset followed by a uint8_t maybe-flags |
511 | 0 | if (!wtap_read_bytes_or_eof(wth->fh, &event_offset, sizeof(event_offset), err, err_info)) |
512 | 0 | { |
513 | 0 | file_info_cleanup(file_info); |
514 | 0 | ws_debug("wtap_read_bytes_or_eof() failed, err = %d.", *err); |
515 | 0 | if (*err == 0 || *err == WTAP_ERR_SHORT_READ) |
516 | 0 | { |
517 | | // Short read or EOF. |
518 | 0 | *err = 0; |
519 | 0 | g_free(*err_info); |
520 | 0 | *err_info = NULL; |
521 | 0 | } |
522 | 0 | return WTAP_OPEN_NOT_MINE; |
523 | 0 | } |
524 | 0 | if (file_seek(wth->fh, 1, SEEK_CUR, err) == -1) |
525 | 0 | { |
526 | 0 | file_info_cleanup(file_info); |
527 | 0 | ws_debug("Failed to skip flags"); |
528 | 0 | return WTAP_OPEN_NOT_MINE; |
529 | 0 | } |
530 | 0 | file_info->event_offsets[idx] = GUINT32_FROM_LE(event_offset); |
531 | 0 | } |
532 | | |
533 | 0 | if (file_seek(wth->fh, header->string_array_offset, SEEK_SET, err) == -1) |
534 | 0 | { |
535 | 0 | ws_debug("Failed to locate procmon string data"); |
536 | 0 | return WTAP_OPEN_NOT_MINE; |
537 | 0 | } |
538 | | |
539 | 0 | uint32_t num_strings; |
540 | 0 | if (!wtap_read_bytes_or_eof(wth->fh, &num_strings, sizeof(num_strings), err, err_info)) |
541 | 0 | { |
542 | 0 | file_info_cleanup(file_info); |
543 | 0 | ws_debug("wtap_read_bytes_or_eof() failed, err = %d.", *err); |
544 | 0 | if (*err == 0 || *err == WTAP_ERR_SHORT_READ) |
545 | 0 | { |
546 | | // Short read or EOF. |
547 | 0 | *err = 0; |
548 | 0 | g_free(*err_info); |
549 | 0 | *err_info = NULL; |
550 | 0 | } |
551 | 0 | return WTAP_OPEN_NOT_MINE; |
552 | 0 | } |
553 | 0 | num_strings = GUINT32_FROM_LE(num_strings); |
554 | 0 | if (num_strings > MAX_PROCMON_STRINGS) { |
555 | 0 | ws_debug("Truncating strings from %u to %u", num_strings, MAX_PROCMON_STRINGS); |
556 | 0 | num_strings = MAX_PROCMON_STRINGS; |
557 | 0 | } |
558 | | |
559 | | // Strings aren't necessarily contiguous (or even in order?) |
560 | 0 | uint32_t *str_offsets = g_new(uint32_t, num_strings); |
561 | 0 | if (!wtap_read_bytes_or_eof(wth->fh, str_offsets, sizeof(uint32_t) * num_strings, err, err_info)) |
562 | 0 | { |
563 | 0 | file_info_cleanup(file_info); |
564 | 0 | ws_debug("wtap_read_bytes_or_eof() failed, err = %d.", *err); |
565 | 0 | if (*err == 0 || *err == WTAP_ERR_SHORT_READ) |
566 | 0 | { |
567 | | // Short read or EOF. |
568 | 0 | *err = 0; |
569 | 0 | g_free(*err_info); |
570 | 0 | *err_info = NULL; |
571 | 0 | } |
572 | 0 | g_free(str_offsets); |
573 | 0 | return WTAP_OPEN_NOT_MINE; |
574 | 0 | } |
575 | | #if G_BYTE_ORDER == G_BIG_ENDIAN |
576 | | for (unsigned idx = 0; idx < num_strings; idx++) |
577 | | { |
578 | | str_offsets[idx] = GUINT32_SWAP_LE_BE(str_offsets[idx]); |
579 | | } |
580 | | #endif |
581 | | |
582 | 0 | file_info->string_array_size = num_strings; |
583 | 0 | file_info->string_array = g_new0(const char *, num_strings); |
584 | 0 | gunichar2 *str_buf = g_new(gunichar2, MAX_PROCMON_STRING_LENGTH); |
585 | 0 | for (unsigned idx = 0; idx < num_strings; idx++) { |
586 | 0 | if (file_seek(wth->fh, header->string_array_offset + str_offsets[idx], SEEK_SET, err) == -1) |
587 | 0 | { |
588 | 0 | file_info_cleanup(file_info); |
589 | 0 | g_free(str_offsets); |
590 | 0 | g_free(str_buf); |
591 | 0 | ws_debug("Failed to locate procmon string %u", idx); |
592 | 0 | return WTAP_OPEN_NOT_MINE; |
593 | 0 | } |
594 | | |
595 | 0 | const char *cur_str = procmon_read_string(wth->fh, str_buf, err, err_info); |
596 | 0 | if (!cur_str) { |
597 | 0 | file_info_cleanup(file_info); |
598 | 0 | g_free(str_offsets); |
599 | 0 | g_free(str_buf); |
600 | 0 | ws_debug("Failed to read procmon string %u", idx); |
601 | 0 | return WTAP_OPEN_NOT_MINE; |
602 | 0 | } |
603 | | |
604 | 0 | file_info->string_array[idx] = cur_str; |
605 | 0 | } |
606 | 0 | g_free(str_offsets); |
607 | 0 | g_free(str_buf); |
608 | |
|
609 | 0 | if (file_seek(wth->fh, header->process_array_offset, SEEK_SET, err) == -1) |
610 | 0 | { |
611 | 0 | ws_debug("Failed to locate procmon process data"); |
612 | 0 | return WTAP_OPEN_NOT_MINE; |
613 | 0 | } |
614 | | |
615 | 0 | uint32_t num_processes; |
616 | 0 | if (!wtap_read_bytes_or_eof(wth->fh, &num_processes, sizeof(num_processes), err, err_info)) |
617 | 0 | { |
618 | 0 | file_info_cleanup(file_info); |
619 | 0 | ws_debug("wtap_read_bytes_or_eof() failed, err = %d.", *err); |
620 | 0 | if (*err == 0 || *err == WTAP_ERR_SHORT_READ) |
621 | 0 | { |
622 | | // Short read or EOF. |
623 | 0 | *err = 0; |
624 | 0 | g_free(*err_info); |
625 | 0 | *err_info = NULL; |
626 | 0 | } |
627 | 0 | return WTAP_OPEN_NOT_MINE; |
628 | 0 | } |
629 | 0 | num_processes = GUINT32_FROM_LE(num_processes); |
630 | 0 | if (num_processes > MAX_PROCMON_PROCESSES) { |
631 | 0 | ws_debug("Truncating processes from %u to %u", num_processes, MAX_PROCMON_PROCESSES); |
632 | 0 | num_processes = MAX_PROCMON_PROCESSES; |
633 | 0 | } |
634 | |
|
635 | 0 | uint32_t *process_indices = g_new(uint32_t, num_processes); |
636 | 0 | if (!wtap_read_bytes_or_eof(wth->fh, process_indices, sizeof(uint32_t) * num_processes, err, err_info)) |
637 | 0 | { |
638 | 0 | file_info_cleanup(file_info); |
639 | 0 | g_free(process_indices); |
640 | 0 | ws_debug("wtap_read_bytes_or_eof() failed, err = %d.", *err); |
641 | 0 | if (*err == 0 || *err == WTAP_ERR_SHORT_READ) |
642 | 0 | { |
643 | | // Short read or EOF. |
644 | 0 | *err = 0; |
645 | 0 | g_free(*err_info); |
646 | 0 | *err_info = NULL; |
647 | 0 | } |
648 | 0 | return WTAP_OPEN_NOT_MINE; |
649 | 0 | } |
650 | | |
651 | 0 | uint32_t max_process_index = 0; |
652 | 0 | for (unsigned idx = 0; idx < num_processes; idx++) { |
653 | 0 | process_indices[idx] = GUINT32_FROM_LE(process_indices[idx]); |
654 | 0 | max_process_index = MAX(max_process_index, process_indices[idx]); |
655 | 0 | } |
656 | 0 | g_free(process_indices); |
657 | 0 | if (max_process_index > MAX_PROCMON_PROCESSES * 2) { |
658 | 0 | ws_debug("Truncating max process index from %u to %u", max_process_index, MAX_PROCMON_PROCESSES * 2); |
659 | 0 | max_process_index = MAX_PROCMON_PROCESSES * 2; |
660 | 0 | } |
661 | 0 | file_info->process_index_map = g_new(uint32_t, max_process_index + 1); |
662 | | // Try to make invalid entries obvious. |
663 | 0 | memset(file_info->process_index_map, 0xff, sizeof(uint32_t) * (max_process_index + 1)); |
664 | 0 | file_info->process_index_map_size = max_process_index + 1; |
665 | |
|
666 | 0 | uint32_t *proc_offsets = g_new(uint32_t, num_processes); |
667 | 0 | if (!wtap_read_bytes_or_eof(wth->fh, proc_offsets, sizeof(uint32_t) * num_processes, err, err_info)) |
668 | 0 | { |
669 | 0 | file_info_cleanup(file_info); |
670 | 0 | g_free(proc_offsets); |
671 | 0 | ws_debug("wtap_read_bytes_or_eof() failed, err = %d.", *err); |
672 | 0 | if (*err == 0 || *err == WTAP_ERR_SHORT_READ) |
673 | 0 | { |
674 | | // Short read or EOF. |
675 | 0 | *err = 0; |
676 | 0 | g_free(*err_info); |
677 | 0 | *err_info = NULL; |
678 | 0 | } |
679 | 0 | return WTAP_OPEN_NOT_MINE; |
680 | 0 | } |
681 | | |
682 | 0 | file_info->process_array = g_new0(procmon_process_t, num_processes); |
683 | 0 | file_info->process_array_size = num_processes; |
684 | 0 | for (unsigned idx = 0; idx < num_processes; idx++) { |
685 | 0 | if (file_seek(wth->fh, header->process_array_offset + proc_offsets[idx], SEEK_SET, err) == -1) |
686 | 0 | { |
687 | 0 | file_info_cleanup(file_info); |
688 | 0 | g_free(proc_offsets); |
689 | 0 | ws_debug("Failed to locate procmon process %u", idx); |
690 | 0 | return WTAP_OPEN_NOT_MINE; |
691 | 0 | } |
692 | 0 | procmon_raw_process_t cur_raw_process; |
693 | 0 | if (!wtap_read_bytes_or_eof(wth->fh, &cur_raw_process, sizeof(cur_raw_process), err, err_info)) |
694 | 0 | { |
695 | 0 | file_info_cleanup(file_info); |
696 | 0 | g_free(proc_offsets); |
697 | 0 | ws_debug("wtap_read_bytes_or_eof() failed, err = %d.", *err); |
698 | 0 | if (*err == 0 || *err == WTAP_ERR_SHORT_READ) |
699 | 0 | { |
700 | | // Short read or EOF. |
701 | 0 | *err = 0; |
702 | 0 | g_free(*err_info); |
703 | 0 | *err_info = NULL; |
704 | 0 | } |
705 | 0 | return WTAP_OPEN_NOT_MINE; |
706 | 0 | } |
707 | 0 | uint32_t process_index = GUINT32_FROM_LE(cur_raw_process.process_index); |
708 | 0 | if (process_index <= max_process_index) { |
709 | 0 | file_info->process_index_map[process_index] = idx; |
710 | 0 | } else { |
711 | 0 | ws_debug("Process %u index %u exceeds max process index %u", idx, process_index, max_process_index); |
712 | 0 | } |
713 | 0 | procmon_process_t *cur_process = &file_info->process_array[idx]; |
714 | 0 | cur_raw_process.start_time = GUINT64_FROM_LE(cur_raw_process.start_time); |
715 | 0 | cur_raw_process.end_time = GUINT64_FROM_LE(cur_raw_process.end_time); |
716 | 0 | uint64_t filetime = GUINT64_FROM_LE(cur_raw_process.start_time); |
717 | 0 | filetime_to_nstime(&cur_process->start_time, filetime); |
718 | 0 | filetime = GUINT64_FROM_LE(cur_raw_process.end_time); |
719 | 0 | filetime_to_nstime(&cur_process->end_time, filetime); |
720 | |
|
721 | 0 | cur_process->process_id = GUINT32_FROM_LE(cur_raw_process.process_id); |
722 | 0 | cur_process->parent_process_id = GUINT32_FROM_LE(cur_raw_process.parent_process_id); |
723 | 0 | cur_process->parent_process_index = MAX(GUINT32_FROM_LE(cur_raw_process.parent_process_index), max_process_index); |
724 | 0 | cur_process->authentication_id = GUINT64_FROM_LE(cur_raw_process.authentication_id); |
725 | 0 | cur_process->session_number = GUINT32_FROM_LE(cur_raw_process.session_number); |
726 | 0 | cur_process->is_virtualized = cur_raw_process.is_virtualized != 0; |
727 | 0 | cur_process->is_64_bit = cur_raw_process.is_64_bit != 0; |
728 | 0 | cur_process->integrity = procmon_string(file_info, GUINT32_FROM_LE(cur_raw_process.integrity_si)); |
729 | 0 | cur_process->user_name = procmon_string(file_info, GUINT32_FROM_LE(cur_raw_process.user_name_si)); |
730 | 0 | cur_process->process_name = procmon_string(file_info, GUINT32_FROM_LE(cur_raw_process.process_name_si)); |
731 | 0 | cur_process->image_path = procmon_string(file_info, GUINT32_FROM_LE(cur_raw_process.image_path_si)); |
732 | 0 | cur_process->command_line = procmon_string(file_info, GUINT32_FROM_LE(cur_raw_process.command_line_si)); |
733 | 0 | cur_process->company = procmon_string(file_info, GUINT32_FROM_LE(cur_raw_process.company_si)); |
734 | 0 | cur_process->version = procmon_string(file_info, GUINT32_FROM_LE(cur_raw_process.version_si)); |
735 | 0 | cur_process->description = procmon_string(file_info, GUINT32_FROM_LE(cur_raw_process.description_si)); |
736 | 0 | if (file_seek(wth->fh, header->system_bitness ? 8 : 4, SEEK_CUR, err) == -1) |
737 | 0 | { |
738 | 0 | file_info_cleanup(file_info); |
739 | 0 | g_free(proc_offsets); |
740 | 0 | ws_debug("Failed to locate number of modules %u", idx); |
741 | 0 | return WTAP_OPEN_NOT_MINE; |
742 | 0 | } |
743 | 0 | uint32_t num_modules; |
744 | 0 | if (!wtap_read_bytes_or_eof(wth->fh, &num_modules, sizeof(num_modules), err, err_info)) |
745 | 0 | { |
746 | 0 | file_info_cleanup(file_info); |
747 | 0 | g_free(proc_offsets); |
748 | 0 | ws_debug("wtap_read_bytes_or_eof() failed, err = %d.", *err); |
749 | 0 | if (*err == 0 || *err == WTAP_ERR_SHORT_READ) |
750 | 0 | { |
751 | | // Short read or EOF. |
752 | 0 | *err = 0; |
753 | 0 | g_free(*err_info); |
754 | 0 | *err_info = NULL; |
755 | 0 | } |
756 | 0 | return WTAP_OPEN_NOT_MINE; |
757 | 0 | } |
758 | | |
759 | 0 | cur_process->num_modules = MIN(GUINT32_FROM_LE(num_modules), MAX_PROCMON_MODULES); |
760 | 0 | if (cur_process->num_modules > 0) { |
761 | 0 | cur_process->modules = g_new(procmon_module_t, cur_process->num_modules); |
762 | 0 | for (unsigned mod_idx = 0; mod_idx < cur_process->num_modules; mod_idx++) { |
763 | 0 | if (cur_process->is_64_bit) { |
764 | 0 | procmon_raw_module_64_t cur_raw_module; |
765 | 0 | if (!wtap_read_bytes_or_eof(wth->fh, &cur_raw_module, sizeof(cur_raw_module), err, err_info)) { |
766 | 0 | file_info_cleanup(file_info); |
767 | 0 | g_free(proc_offsets); |
768 | 0 | ws_debug("wtap_read_bytes_or_eof() failed, err = %d.", *err); |
769 | 0 | if (*err == 0 || *err == WTAP_ERR_SHORT_READ) |
770 | 0 | { |
771 | | // Short read or EOF. |
772 | 0 | *err = 0; |
773 | 0 | g_free(*err_info); |
774 | 0 | *err_info = NULL; |
775 | 0 | } |
776 | 0 | return WTAP_OPEN_NOT_MINE; |
777 | 0 | } |
778 | 0 | procmon_module_t *cur_module = &cur_process->modules[mod_idx]; |
779 | 0 | cur_module->base_address = GUINT64_FROM_LE(cur_raw_module.base_address); |
780 | 0 | cur_module->size = GUINT32_FROM_LE(cur_raw_module.size); |
781 | 0 | cur_module->image_path = procmon_string(file_info, GUINT32_FROM_LE(cur_raw_module.image_path_si)); |
782 | 0 | cur_module->version = procmon_string(file_info, GUINT32_FROM_LE(cur_raw_module.version_si)); |
783 | 0 | cur_module->company = procmon_string(file_info, GUINT32_FROM_LE(cur_raw_module.company_si)); |
784 | 0 | cur_module->description = procmon_string(file_info, GUINT32_FROM_LE(cur_raw_module.description_si)); |
785 | | // filetime = GUINT64_FROM_LE(cur_raw_module.timestamp); |
786 | | // filetime_to_nstime(&cur_module->timestamp, filetime); |
787 | 0 | } else { |
788 | 0 | procmon_raw_module_32_t cur_raw_module; |
789 | 0 | if (!wtap_read_bytes_or_eof(wth->fh, &cur_raw_module, sizeof(cur_raw_module), err, err_info)) { |
790 | 0 | file_info_cleanup(file_info); |
791 | 0 | g_free(proc_offsets); |
792 | 0 | ws_debug("wtap_read_bytes_or_eof() failed, err = %d.", *err); |
793 | 0 | if (*err == 0 || *err == WTAP_ERR_SHORT_READ) |
794 | 0 | { |
795 | | // Short read or EOF. |
796 | 0 | *err = 0; |
797 | 0 | g_free(*err_info); |
798 | 0 | *err_info = NULL; |
799 | 0 | } |
800 | 0 | return WTAP_OPEN_NOT_MINE; |
801 | 0 | } |
802 | 0 | procmon_module_t *cur_module = &cur_process->modules[mod_idx]; |
803 | 0 | cur_module->base_address = GUINT32_FROM_LE(cur_raw_module.base_address); |
804 | 0 | cur_module->size = GUINT32_FROM_LE(cur_raw_module.size); |
805 | 0 | cur_module->image_path = procmon_string(file_info, GUINT32_FROM_LE(cur_raw_module.image_path_si)); |
806 | 0 | cur_module->version = procmon_string(file_info, GUINT32_FROM_LE(cur_raw_module.version_si)); |
807 | 0 | cur_module->company = procmon_string(file_info, GUINT32_FROM_LE(cur_raw_module.company_si)); |
808 | 0 | cur_module->description = procmon_string(file_info, GUINT32_FROM_LE(cur_raw_module.description_si)); |
809 | | // filetime = GUINT64_FROM_LE(cur_raw_module.timestamp); |
810 | | // filetime_to_nstime(&cur_module->timestamp, filetime); |
811 | 0 | } |
812 | 0 | } |
813 | 0 | } else { |
814 | 0 | cur_process->modules = NULL; |
815 | 0 | } |
816 | 0 | } |
817 | 0 | g_free(proc_offsets); |
818 | |
|
819 | 0 | if (file_seek(wth->fh, header->start_events_offset, SEEK_SET, err) == -1) |
820 | 0 | { |
821 | 0 | ws_debug("Failed to locate procmon events data"); |
822 | 0 | return WTAP_OPEN_NOT_MINE; |
823 | 0 | } |
824 | | |
825 | 0 | wth->meta_events = g_array_new(false, false, sizeof(wtap_block_t)); |
826 | |
|
827 | 0 | wth->priv = file_info; |
828 | 0 | wth->file_type_subtype = procmon_file_type_subtype; |
829 | 0 | wth->file_encap = WTAP_ENCAP_PROCMON; |
830 | |
|
831 | 0 | wth->snapshot_length = 0; |
832 | 0 | wth->file_tsprec = WTAP_TSPREC_SEC; |
833 | |
|
834 | 0 | wth->subtype_read = procmon_read; |
835 | 0 | wth->subtype_seek_read = procmon_seek_read; |
836 | |
|
837 | 0 | return WTAP_OPEN_MINE; |
838 | 0 | } |
839 | | |
840 | | /* Options for meta event blocks. */ |
841 | | static const struct supported_option_type ft_specific_event_block_options_supported[] = { |
842 | | { OPT_COMMENT, MULTIPLE_OPTIONS_SUPPORTED }, |
843 | | { OPT_CUSTOM_STR_COPY, MULTIPLE_OPTIONS_SUPPORTED }, |
844 | | { OPT_CUSTOM_BIN_COPY, MULTIPLE_OPTIONS_SUPPORTED }, |
845 | | { OPT_CUSTOM_STR_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED }, |
846 | | { OPT_CUSTOM_BIN_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED } |
847 | | }; |
848 | | |
849 | | static const struct supported_block_type procmon_blocks_supported[] = { |
850 | | |
851 | | /* Multiple file-type specific events (including local ones). */ |
852 | | { WTAP_BLOCK_FT_SPECIFIC_EVENT, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(ft_specific_event_block_options_supported) }, |
853 | | |
854 | | /* Multiple custom blocks. */ |
855 | | { WTAP_BLOCK_CUSTOM, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }, |
856 | | }; |
857 | | |
858 | | static const struct file_type_subtype_info procmon_info = { |
859 | | "MS Procmon files", "procmon", NULL, NULL, |
860 | | false, BLOCKS_SUPPORTED(procmon_blocks_supported), |
861 | | NULL, NULL, NULL |
862 | | }; |
863 | | |
864 | | void register_procmon(void) |
865 | 15 | { |
866 | 15 | procmon_file_type_subtype = wtap_register_file_type_subtype(&procmon_info); |
867 | | |
868 | | /* |
869 | | * Register name for backwards compatibility with the |
870 | | * wtap_filetypes table in Lua. |
871 | | */ |
872 | 15 | wtap_register_backwards_compatibility_lua_name("Procmon", procmon_file_type_subtype); |
873 | 15 | } |
874 | | |
875 | | /* |
876 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
877 | | * |
878 | | * Local Variables: |
879 | | * c-basic-offset: 4 |
880 | | * tab-width: 8 |
881 | | * indent-tabs-mode: nil |
882 | | * End: |
883 | | * |
884 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
885 | | * :indentSize=4:tabSize=8:noTabs=true: |
886 | | */ |