Coverage Report

Created: 2026-07-12 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
 */