winevt_c 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,718 +1,790 @@
1
- #include <winevt_c.h>
2
-
3
- #include <sddl.h>
4
- #include <stdlib.h>
5
- #include <string>
6
- #include <vector>
7
-
8
- VALUE
9
- wstr_to_rb_str(UINT cp, const WCHAR* wstr, int clen)
10
- {
11
- VALUE vstr;
12
- CHAR* ptr;
13
- int len = WideCharToMultiByte(cp, 0, wstr, clen, nullptr, 0, nullptr, nullptr);
14
- ptr = ALLOCV_N(CHAR, vstr, len);
15
- WideCharToMultiByte(cp, 0, wstr, clen, ptr, len, nullptr, nullptr);
16
- VALUE str = rb_utf8_str_new_cstr(ptr);
17
- ALLOCV_END(vstr);
18
-
19
- return str;
20
- }
21
-
22
- void
23
- raise_system_error(VALUE error, DWORD errorCode)
24
- {
25
- WCHAR msgBuf[256] = { 0 };
26
- VALUE errorMessage;
27
-
28
- FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
29
- NULL,
30
- errorCode,
31
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
32
- msgBuf,
33
- _countof(msgBuf),
34
- NULL);
35
- errorMessage = wstr_to_rb_str(CP_UTF8, msgBuf, -1);
36
-
37
- #pragma GCC diagnostic push
38
- #pragma GCC diagnostic ignored "-Wformat="
39
- #pragma GCC diagnostic ignored "-Wformat-extra-args"
40
- rb_raise(error, "ErrorCode: %lu\nError: %" PRIsVALUE "\n", errorCode, errorMessage);
41
- #pragma GCC diagnostic pop
42
- }
43
-
44
- VALUE
45
- render_to_rb_str(EVT_HANDLE handle, DWORD flags)
46
- {
47
- VALUE vbuffer;
48
- WCHAR* buffer;
49
- ULONG bufferSize = 0;
50
- ULONG bufferSizeUsed = 0;
51
- ULONG count;
52
- BOOL succeeded;
53
- VALUE result;
54
-
55
- if (flags != EvtRenderEventXml && flags != EvtRenderBookmark) {
56
- return Qnil;
57
- }
58
-
59
- // Get the size of the buffer
60
- EvtRender(nullptr, handle, flags, 0, NULL, &bufferSize, &count);
61
-
62
- // bufferSize is in bytes, not characters
63
- buffer = (WCHAR*)ALLOCV(vbuffer, bufferSize);
64
-
65
- succeeded =
66
- EvtRender(nullptr, handle, flags, bufferSize, buffer, &bufferSizeUsed, &count);
67
- if (!succeeded) {
68
- DWORD status = GetLastError();
69
- ALLOCV_END(vbuffer);
70
- raise_system_error(rb_eWinevtQueryError, status);
71
- }
72
-
73
- result = wstr_to_rb_str(CP_UTF8, buffer, -1);
74
- ALLOCV_END(vbuffer);
75
-
76
- return result;
77
- }
78
-
79
- EVT_HANDLE
80
- connect_to_remote(LPWSTR computerName, LPWSTR domain, LPWSTR username, LPWSTR password,
81
- EVT_RPC_LOGIN_FLAGS flags)
82
- {
83
- EVT_HANDLE hRemote = NULL;
84
- EVT_RPC_LOGIN Credentials;
85
-
86
- RtlZeroMemory(&Credentials, sizeof(EVT_RPC_LOGIN));
87
-
88
- Credentials.Server = computerName;
89
- Credentials.Domain = domain;
90
- Credentials.User = username;
91
- Credentials.Password = password;
92
- Credentials.Flags = flags;
93
-
94
- hRemote = EvtOpenSession(EvtRpcLogin, &Credentials, 0, 0);
95
-
96
- SecureZeroMemory(&Credentials, sizeof(EVT_RPC_LOGIN));
97
-
98
- return hRemote;
99
- }
100
-
101
- static std::wstring
102
- guid_to_wstr(const GUID& guid)
103
- {
104
- LPOLESTR p = nullptr;
105
- if (FAILED(StringFromCLSID(guid, &p))) {
106
- return nullptr;
107
- }
108
- std::wstring s(p);
109
- CoTaskMemFree(p);
110
- return s;
111
- }
112
-
113
- static VALUE
114
- extract_user_evt_variants(PEVT_VARIANT pRenderedValues, DWORD propCount)
115
- {
116
- VALUE userValues = rb_ary_new();
117
- VALUE rbObj;
118
-
119
- for (DWORD i = 0; i < propCount; i++) {
120
- switch (pRenderedValues[i].Type) {
121
- case EvtVarTypeNull:
122
- rb_ary_push(userValues, Qnil);
123
- break;
124
- case EvtVarTypeString:
125
- if (pRenderedValues[i].StringVal == nullptr) {
126
- rb_ary_push(userValues, rb_utf8_str_new_cstr("(NULL)"));
127
- } else {
128
- std::wstring wStr(pRenderedValues[i].StringVal);
129
- rbObj = wstr_to_rb_str(CP_UTF8, &wStr[0], -1);
130
- rb_ary_push(userValues, rbObj);
131
- }
132
- break;
133
- case EvtVarTypeAnsiString:
134
- if (pRenderedValues[i].AnsiStringVal == nullptr) {
135
- rb_ary_push(userValues, rb_utf8_str_new_cstr("(NULL)"));
136
- } else {
137
- rb_ary_push(
138
- userValues,
139
- rb_utf8_str_new_cstr(const_cast<char*>(pRenderedValues[i].AnsiStringVal)));
140
- }
141
- break;
142
- case EvtVarTypeSByte:
143
- rbObj = INT2NUM(static_cast<UINT32>(pRenderedValues[i].SByteVal));
144
- rb_ary_push(userValues, rbObj);
145
- break;
146
- case EvtVarTypeByte:
147
- rbObj = INT2NUM(static_cast<UINT32>(pRenderedValues[i].ByteVal));
148
- rb_ary_push(userValues, rbObj);
149
- break;
150
- case EvtVarTypeInt16:
151
- rbObj = INT2NUM(static_cast<INT32>(pRenderedValues[i].Int16Val));
152
- rb_ary_push(userValues, rbObj);
153
- break;
154
- case EvtVarTypeUInt16:
155
- rbObj = UINT2NUM(static_cast<UINT32>(pRenderedValues[i].UInt16Val));
156
- rb_ary_push(userValues, rbObj);
157
- break;
158
- case EvtVarTypeInt32:
159
- rbObj = INT2NUM(pRenderedValues[i].Int32Val);
160
- rb_ary_push(userValues, rbObj);
161
- break;
162
- case EvtVarTypeUInt32:
163
- rbObj = UINT2NUM(pRenderedValues[i].UInt32Val);
164
- rb_ary_push(userValues, rbObj);
165
- break;
166
- case EvtVarTypeInt64:
167
- rbObj = LONG2NUM(pRenderedValues[i].Int64Val);
168
- rb_ary_push(userValues, rbObj);
169
- break;
170
- case EvtVarTypeUInt64:
171
- rbObj = ULONG2NUM(pRenderedValues[i].UInt64Val);
172
- rb_ary_push(userValues, rbObj);
173
- break;
174
- case EvtVarTypeSingle: {
175
- CHAR sResult[256];
176
- _snprintf_s(
177
- sResult, _countof(sResult), _TRUNCATE, "%f", pRenderedValues[i].SingleVal);
178
- rb_ary_push(userValues, rb_utf8_str_new_cstr(sResult));
179
- break;
180
- }
181
- case EvtVarTypeDouble: {
182
- CHAR sResult[256];
183
- _snprintf_s(
184
- sResult, _countof(sResult), _TRUNCATE, "%lf", pRenderedValues[i].DoubleVal);
185
- rb_ary_push(userValues, rb_utf8_str_new_cstr(sResult));
186
- break;
187
- }
188
- case EvtVarTypeBoolean:
189
- rbObj = pRenderedValues[i].BooleanVal ? Qtrue : Qfalse;
190
- rb_ary_push(userValues, rbObj);
191
- break;
192
- case EvtVarTypeGuid:
193
- if (pRenderedValues[i].GuidVal != nullptr) {
194
- const GUID guid = *pRenderedValues[i].GuidVal;
195
- std::wstring wstr = guid_to_wstr(guid);
196
- rbObj = wstr_to_rb_str(CP_UTF8, wstr.c_str(), -1);
197
- rb_ary_push(userValues, rbObj);
198
- } else {
199
- rb_ary_push(userValues, rb_utf8_str_new_cstr("?"));
200
- }
201
- break;
202
- case EvtVarTypeSizeT:
203
- rbObj = SIZET2NUM(pRenderedValues[i].SizeTVal);
204
- rb_ary_push(userValues, rbObj);
205
- break;
206
- case EvtVarTypeFileTime: {
207
- LARGE_INTEGER timestamp;
208
- CHAR strTime[128];
209
- FILETIME ft;
210
- SYSTEMTIME st;
211
- timestamp.QuadPart = pRenderedValues[i].FileTimeVal;
212
- ft.dwHighDateTime = timestamp.HighPart;
213
- ft.dwLowDateTime = timestamp.LowPart;
214
- if (FileTimeToSystemTime(&ft, &st)) {
215
- _snprintf_s(strTime,
216
- _countof(strTime),
217
- _TRUNCATE,
218
- "%04d-%02d-%02d %02d:%02d:%02d.%dZ",
219
- st.wYear,
220
- st.wMonth,
221
- st.wDay,
222
- st.wHour,
223
- st.wMinute,
224
- st.wSecond,
225
- st.wMilliseconds);
226
- rb_ary_push(userValues, rb_utf8_str_new_cstr(strTime));
227
- } else {
228
- rb_ary_push(userValues, rb_utf8_str_new_cstr("?"));
229
- }
230
- break;
231
- }
232
- case EvtVarTypeSysTime: {
233
- CHAR strTime[128];
234
- SYSTEMTIME st;
235
- if (pRenderedValues[i].SysTimeVal != nullptr) {
236
- st = *pRenderedValues[i].SysTimeVal;
237
- _snprintf_s(strTime,
238
- _countof(strTime),
239
- _TRUNCATE,
240
- "%04d-%02d-%02d %02d:%02d:%02d.%dZ",
241
- st.wYear,
242
- st.wMonth,
243
- st.wDay,
244
- st.wHour,
245
- st.wMinute,
246
- st.wSecond,
247
- st.wMilliseconds);
248
- rb_ary_push(userValues, rb_utf8_str_new_cstr(strTime));
249
- } else {
250
- rb_ary_push(userValues, rb_utf8_str_new_cstr("?"));
251
- }
252
- break;
253
- }
254
- case EvtVarTypeSid: {
255
- WCHAR* tmpWChar = nullptr;
256
- if (ConvertSidToStringSidW(pRenderedValues[i].SidVal, &tmpWChar)) {
257
- rbObj = wstr_to_rb_str(CP_UTF8, tmpWChar, -1);
258
- rb_ary_push(userValues, rbObj);
259
- LocalFree(tmpWChar);
260
- } else {
261
- rb_ary_push(userValues, rb_utf8_str_new_cstr("?"));
262
- }
263
- break;
264
- }
265
- case EvtVarTypeHexInt32:
266
- rbObj = rb_sprintf("%#x", pRenderedValues[i].UInt32Val);
267
- rb_ary_push(userValues, rbObj);
268
- break;
269
- case EvtVarTypeHexInt64:
270
- rbObj = rb_sprintf("%#I64x", pRenderedValues[i].UInt64Val);
271
- rb_ary_push(userValues, rbObj);
272
- break;
273
- case EvtVarTypeEvtXml:
274
- if (pRenderedValues[i].XmlVal == nullptr) {
275
- rb_ary_push(userValues, rb_utf8_str_new_cstr("(NULL)"));
276
- } else {
277
- rbObj = wstr_to_rb_str(CP_UTF8, pRenderedValues[i].XmlVal, -1);
278
- rb_ary_push(userValues, rbObj);
279
- }
280
- break;
281
- default:
282
- rb_ary_push(userValues, rb_utf8_str_new_cstr("?"));
283
- break;
284
- }
285
- }
286
-
287
- return userValues;
288
- }
289
-
290
- VALUE
291
- get_values(EVT_HANDLE handle)
292
- {
293
- VALUE vbuffer;
294
- PEVT_VARIANT pRenderedValues;
295
- ULONG bufferSize = 0;
296
- ULONG bufferSizeUsed = 0;
297
- DWORD propCount = 0;
298
- BOOL succeeded;
299
- VALUE userValues = Qnil;
300
-
301
- EVT_HANDLE renderContext = EvtCreateRenderContext(0, nullptr, EvtRenderContextUser);
302
- if (renderContext == nullptr) {
303
- rb_raise(rb_eWinevtQueryError, "Failed to create renderContext");
304
- }
305
-
306
- // Get the size of the buffer
307
- EvtRender(
308
- renderContext, handle, EvtRenderEventValues, 0, NULL, &bufferSize, &propCount);
309
-
310
- // bufferSize is in bytes, not array size
311
- pRenderedValues = (PEVT_VARIANT)ALLOCV(vbuffer, bufferSize);
312
-
313
- succeeded = EvtRender(renderContext,
314
- handle,
315
- EvtRenderEventValues,
316
- bufferSize,
317
- pRenderedValues,
318
- &bufferSizeUsed,
319
- &propCount);
320
- if (!succeeded) {
321
- DWORD status = GetLastError();
322
- ALLOCV_END(vbuffer);
323
- EvtClose(renderContext);
324
- raise_system_error(rb_eWinevtQueryError, status);
325
- }
326
-
327
- userValues = extract_user_evt_variants(pRenderedValues, propCount);
328
-
329
- ALLOCV_END(vbuffer);
330
- EvtClose(renderContext);
331
-
332
- return userValues;
333
- }
334
-
335
- static std::vector<WCHAR>
336
- get_message(EVT_HANDLE hMetadata, EVT_HANDLE handle)
337
- {
338
- #define BUFSIZE 4096
339
- std::vector<WCHAR> result;
340
- ULONG status;
341
- ULONG bufferSizeNeeded = 0;
342
- LPVOID lpMsgBuf;
343
- std::vector<WCHAR> message(BUFSIZE);
344
-
345
- if (!EvtFormatMessage(hMetadata,
346
- handle,
347
- 0xffffffff,
348
- 0,
349
- nullptr,
350
- EvtFormatMessageEvent,
351
- message.size(),
352
- &message[0],
353
- &bufferSizeNeeded)) {
354
- status = GetLastError();
355
-
356
- if (status != ERROR_EVT_UNRESOLVED_VALUE_INSERT) {
357
- switch (status) {
358
- case ERROR_EVT_MESSAGE_NOT_FOUND:
359
- case ERROR_EVT_MESSAGE_ID_NOT_FOUND:
360
- case ERROR_EVT_MESSAGE_LOCALE_NOT_FOUND:
361
- case ERROR_RESOURCE_LANG_NOT_FOUND:
362
- case ERROR_MUI_FILE_NOT_FOUND:
363
- case ERROR_EVT_UNRESOLVED_PARAMETER_INSERT: {
364
- if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
365
- FORMAT_MESSAGE_IGNORE_INSERTS,
366
- nullptr,
367
- status,
368
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
369
- reinterpret_cast<WCHAR*>(&lpMsgBuf),
370
- 0,
371
- nullptr) == 0)
372
- FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
373
- FORMAT_MESSAGE_IGNORE_INSERTS,
374
- nullptr,
375
- status,
376
- MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
377
- reinterpret_cast<WCHAR*>(&lpMsgBuf),
378
- 0,
379
- nullptr);
380
-
381
- std::wstring ret(reinterpret_cast<WCHAR*>(lpMsgBuf));
382
- std::copy(ret.begin(), ret.end(), std::back_inserter(result));
383
- LocalFree(lpMsgBuf);
384
-
385
- goto cleanup;
386
- }
387
- }
388
-
389
- if (status != ERROR_INSUFFICIENT_BUFFER)
390
- rb_raise(rb_eWinevtQueryError, "ErrorCode: %lu", status);
391
- }
392
-
393
- if (status == ERROR_INSUFFICIENT_BUFFER) {
394
- message.resize(bufferSizeNeeded);
395
- message.shrink_to_fit();
396
-
397
- if (!EvtFormatMessage(hMetadata,
398
- handle,
399
- 0xffffffff,
400
- 0,
401
- nullptr,
402
- EvtFormatMessageEvent,
403
- message.size(),
404
- &message.front(),
405
- &bufferSizeNeeded)) {
406
- status = GetLastError();
407
-
408
- if (status != ERROR_EVT_UNRESOLVED_VALUE_INSERT) {
409
- switch (status) {
410
- case ERROR_EVT_MESSAGE_NOT_FOUND:
411
- case ERROR_EVT_MESSAGE_ID_NOT_FOUND:
412
- case ERROR_EVT_MESSAGE_LOCALE_NOT_FOUND:
413
- case ERROR_RESOURCE_LANG_NOT_FOUND:
414
- case ERROR_MUI_FILE_NOT_FOUND:
415
- case ERROR_EVT_UNRESOLVED_PARAMETER_INSERT:
416
- if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
417
- FORMAT_MESSAGE_FROM_SYSTEM |
418
- FORMAT_MESSAGE_IGNORE_INSERTS,
419
- nullptr,
420
- status,
421
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
422
- reinterpret_cast<WCHAR*>(&lpMsgBuf),
423
- 0,
424
- nullptr) == 0)
425
- FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
426
- FORMAT_MESSAGE_FROM_SYSTEM |
427
- FORMAT_MESSAGE_IGNORE_INSERTS,
428
- nullptr,
429
- status,
430
- MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
431
- reinterpret_cast<WCHAR*>(&lpMsgBuf),
432
- 0,
433
- nullptr);
434
-
435
- std::wstring ret(reinterpret_cast<WCHAR*>(lpMsgBuf));
436
- std::copy(ret.begin(), ret.end(), std::back_inserter(result));
437
- LocalFree(lpMsgBuf);
438
-
439
- goto cleanup;
440
- }
441
-
442
- rb_raise(rb_eWinevtQueryError, "ErrorCode: %lu", status);
443
- }
444
- }
445
- }
446
- }
447
-
448
- result = message;
449
-
450
- cleanup:
451
-
452
- return result;
453
-
454
- #undef BUFSIZE
455
- }
456
-
457
- WCHAR*
458
- get_description(EVT_HANDLE handle, LANGID langID, EVT_HANDLE hRemote)
459
- {
460
- #define BUFSIZE 4096
461
- std::vector<WCHAR> buffer(BUFSIZE);
462
- ULONG bufferSizeNeeded = 0;
463
- ULONG status, count;
464
- std::vector<WCHAR> result;
465
- EVT_HANDLE hMetadata = nullptr;
466
-
467
- static PCWSTR eventProperties[] = { L"Event/System/Provider/@Name" };
468
- EVT_HANDLE renderContext =
469
- EvtCreateRenderContext(1, eventProperties, EvtRenderContextValues);
470
- if (renderContext == nullptr) {
471
- rb_raise(rb_eWinevtQueryError, "Failed to create renderContext");
472
- }
473
-
474
- if (EvtRender(renderContext,
475
- handle,
476
- EvtRenderEventValues,
477
- buffer.size(),
478
- &buffer.front(),
479
- &bufferSizeNeeded,
480
- &count) != FALSE) {
481
- status = ERROR_SUCCESS;
482
- } else {
483
- status = GetLastError();
484
- }
485
-
486
- if (status != ERROR_SUCCESS) {
487
- raise_system_error(rb_eWinevtQueryError, status);
488
- }
489
-
490
- // Obtain buffer as EVT_VARIANT pointer. To avoid ErrorCide 87 in EvtRender.
491
- const PEVT_VARIANT values = reinterpret_cast<PEVT_VARIANT>(&buffer.front());
492
-
493
- // Open publisher metadata
494
- hMetadata = EvtOpenPublisherMetadata(
495
- hRemote,
496
- values[0].StringVal,
497
- nullptr,
498
- MAKELCID(langID, SORT_DEFAULT),
499
- 0);
500
- if (hMetadata == nullptr) {
501
- // When winevt_c cannot open metadata, then give up to obtain
502
- // message file and clean up immediately.
503
- goto cleanup;
504
- }
505
-
506
- result = get_message(hMetadata, handle);
507
-
508
- #undef BUFSIZE
509
-
510
- cleanup:
511
-
512
- if (renderContext)
513
- EvtClose(renderContext);
514
-
515
- if (hMetadata)
516
- EvtClose(hMetadata);
517
-
518
- return _wcsdup(result.data());
519
- }
520
-
521
- VALUE
522
- render_system_event(EVT_HANDLE hEvent, BOOL preserve_qualifiers)
523
- {
524
- DWORD status = ERROR_SUCCESS;
525
- EVT_HANDLE hContext = NULL;
526
- DWORD dwBufferSize = 0;
527
- DWORD dwBufferUsed = 0;
528
- DWORD dwPropertyCount = 0;
529
- VALUE vRenderedValues;
530
- PEVT_VARIANT pRenderedValues = NULL;
531
- WCHAR wsGuid[50];
532
- LPSTR pwsSid = NULL;
533
- ULONGLONG ullTimeStamp = 0;
534
- ULONGLONG ullNanoseconds = 0;
535
- SYSTEMTIME st;
536
- FILETIME ft;
537
- CHAR buffer[32];
538
- VALUE rbstr;
539
- DWORD EventID;
540
- VALUE hash = rb_hash_new();
541
-
542
- hContext = EvtCreateRenderContext(0, NULL, EvtRenderContextSystem);
543
- if (NULL == hContext) {
544
- rb_raise(
545
- rb_eWinevtQueryError, "Failed to create renderContext with %lu\n", GetLastError());
546
- }
547
-
548
- if (!EvtRender(hContext,
549
- hEvent,
550
- EvtRenderEventValues,
551
- dwBufferSize,
552
- pRenderedValues,
553
- &dwBufferUsed,
554
- &dwPropertyCount)) {
555
- status = GetLastError();
556
- if (ERROR_INSUFFICIENT_BUFFER == status) {
557
- dwBufferSize = dwBufferUsed;
558
- pRenderedValues = (PEVT_VARIANT)ALLOCV(vRenderedValues, dwBufferSize);
559
- if (pRenderedValues) {
560
- EvtRender(hContext,
561
- hEvent,
562
- EvtRenderEventValues,
563
- dwBufferSize,
564
- pRenderedValues,
565
- &dwBufferUsed,
566
- &dwPropertyCount);
567
- } else {
568
- EvtClose(hContext);
569
- rb_raise(rb_eRuntimeError, "Failed to malloc memory with %lu\n", status);
570
- }
571
- }
572
-
573
- status = GetLastError();
574
- if (ERROR_SUCCESS != status) {
575
- EvtClose(hContext);
576
- ALLOCV_END(vRenderedValues);
577
-
578
- rb_raise(rb_eWinevtQueryError, "EvtRender failed with %lu\n", status);
579
- }
580
- }
581
-
582
- // EVT_VARIANT value with EvtRenderContextSystem will be decomposed
583
- // as the following enum definition:
584
- // https://docs.microsoft.com/en-us/windows/win32/api/winevt/ne-winevt-evt_system_property_id
585
- rbstr = wstr_to_rb_str(CP_UTF8, pRenderedValues[EvtSystemProviderName].StringVal, -1);
586
- rb_hash_aset(hash, rb_str_new2("ProviderName"), rbstr);
587
- if (NULL != pRenderedValues[EvtSystemProviderGuid].GuidVal) {
588
- const GUID* Guid = pRenderedValues[EvtSystemProviderGuid].GuidVal;
589
- StringFromGUID2(*Guid, wsGuid, _countof(wsGuid));
590
- rbstr = wstr_to_rb_str(CP_UTF8, wsGuid, -1);
591
- rb_hash_aset(hash, rb_str_new2("ProviderGuid"), rbstr);
592
- } else {
593
- rb_hash_aset(hash, rb_str_new2("ProviderGuid"), Qnil);
594
- }
595
-
596
- EventID = pRenderedValues[EvtSystemEventID].UInt16Val;
597
- if (preserve_qualifiers) {
598
- if (EvtVarTypeNull != pRenderedValues[EvtSystemQualifiers].Type) {
599
- rb_hash_aset(hash, rb_str_new2("Qualifiers"),
600
- INT2NUM(pRenderedValues[EvtSystemQualifiers].UInt16Val));
601
- } else {
602
- rb_hash_aset(hash, rb_str_new2("Qualifiers"), rb_str_new2(""));
603
- }
604
-
605
- rb_hash_aset(hash, rb_str_new2("EventID"), INT2NUM(EventID));
606
- } else {
607
- if (EvtVarTypeNull != pRenderedValues[EvtSystemQualifiers].Type) {
608
- EventID = MAKELONG(pRenderedValues[EvtSystemEventID].UInt16Val,
609
- pRenderedValues[EvtSystemQualifiers].UInt16Val);
610
- }
611
-
612
- rb_hash_aset(hash, rb_str_new2("EventID"), ULONG2NUM(EventID));
613
- }
614
-
615
- rb_hash_aset(hash,
616
- rb_str_new2("Version"),
617
- (EvtVarTypeNull == pRenderedValues[EvtSystemVersion].Type)
618
- ? INT2NUM(0)
619
- : INT2NUM(pRenderedValues[EvtSystemVersion].ByteVal));
620
- rb_hash_aset(hash,
621
- rb_str_new2("Level"),
622
- (EvtVarTypeNull == pRenderedValues[EvtSystemLevel].Type)
623
- ? INT2NUM(0)
624
- : INT2NUM(pRenderedValues[EvtSystemLevel].ByteVal));
625
- rb_hash_aset(hash,
626
- rb_str_new2("Task"),
627
- (EvtVarTypeNull == pRenderedValues[EvtSystemTask].Type)
628
- ? INT2NUM(0)
629
- : INT2NUM(pRenderedValues[EvtSystemTask].UInt16Val));
630
- rb_hash_aset(hash,
631
- rb_str_new2("Opcode"),
632
- (EvtVarTypeNull == pRenderedValues[EvtSystemOpcode].Type)
633
- ? INT2NUM(0)
634
- : INT2NUM(pRenderedValues[EvtSystemOpcode].ByteVal));
635
- _snprintf_s(buffer,
636
- _countof(buffer),
637
- _TRUNCATE,
638
- "0x%llx",
639
- pRenderedValues[EvtSystemKeywords].UInt64Val);
640
- rb_hash_aset(hash,
641
- rb_str_new2("Keywords"),
642
- (EvtVarTypeNull == pRenderedValues[EvtSystemKeywords].Type)
643
- ? Qnil
644
- : rb_str_new2(buffer));
645
-
646
- ullTimeStamp = pRenderedValues[EvtSystemTimeCreated].FileTimeVal;
647
- ft.dwHighDateTime = (DWORD)((ullTimeStamp >> 32) & 0xFFFFFFFF);
648
- ft.dwLowDateTime = (DWORD)(ullTimeStamp & 0xFFFFFFFF);
649
-
650
- FileTimeToSystemTime(&ft, &st);
651
- ullNanoseconds =
652
- (ullTimeStamp % 10000000) *
653
- 100; // Display nanoseconds instead of milliseconds for higher resolution
654
- _snprintf_s(buffer,
655
- _countof(buffer),
656
- _TRUNCATE,
657
- "%02d/%02d/%02d %02d:%02d:%02d.%llu",
658
- st.wYear,
659
- st.wMonth,
660
- st.wDay,
661
- st.wHour,
662
- st.wMinute,
663
- st.wSecond,
664
- ullNanoseconds);
665
- rb_hash_aset(hash,
666
- rb_str_new2("TimeCreated"),
667
- (EvtVarTypeNull == pRenderedValues[EvtSystemKeywords].Type)
668
- ? Qnil
669
- : rb_str_new2(buffer));
670
- _snprintf_s(buffer,
671
- _countof(buffer),
672
- _TRUNCATE,
673
- "%llu",
674
- pRenderedValues[EvtSystemEventRecordId].UInt64Val);
675
- rb_hash_aset(hash,
676
- rb_str_new2("EventRecordID"),
677
- (EvtVarTypeNull == pRenderedValues[EvtSystemEventRecordId].UInt64Val)
678
- ? Qnil
679
- : rb_str_new2(buffer));
680
-
681
- if (EvtVarTypeNull != pRenderedValues[EvtSystemActivityID].Type) {
682
- const GUID* Guid = pRenderedValues[EvtSystemActivityID].GuidVal;
683
- StringFromGUID2(*Guid, wsGuid, _countof(wsGuid));
684
- rbstr = wstr_to_rb_str(CP_UTF8, wsGuid, -1);
685
- rb_hash_aset(hash, rb_str_new2("ActivityID"), rbstr);
686
- }
687
-
688
- if (EvtVarTypeNull != pRenderedValues[EvtSystemRelatedActivityID].Type) {
689
- const GUID* Guid = pRenderedValues[EvtSystemRelatedActivityID].GuidVal;
690
- StringFromGUID2(*Guid, wsGuid, _countof(wsGuid));
691
- rbstr = wstr_to_rb_str(CP_UTF8, wsGuid, -1);
692
- rb_hash_aset(hash, rb_str_new2("RelatedActivityID"), rbstr);
693
- }
694
-
695
- rb_hash_aset(hash,
696
- rb_str_new2("ProcessID"),
697
- UINT2NUM(pRenderedValues[EvtSystemProcessID].UInt32Val));
698
- rb_hash_aset(hash,
699
- rb_str_new2("ThreadID"),
700
- UINT2NUM(pRenderedValues[EvtSystemThreadID].UInt32Val));
701
- rbstr = wstr_to_rb_str(CP_UTF8, pRenderedValues[EvtSystemChannel].StringVal, -1);
702
- rb_hash_aset(hash, rb_str_new2("Channel"), rbstr);
703
- rbstr = wstr_to_rb_str(CP_UTF8, pRenderedValues[EvtSystemComputer].StringVal, -1);
704
- rb_hash_aset(hash, rb_str_new2("Computer"), rbstr);
705
-
706
- if (EvtVarTypeNull != pRenderedValues[EvtSystemUserID].Type) {
707
- if (ConvertSidToStringSid(pRenderedValues[EvtSystemUserID].SidVal, &pwsSid)) {
708
- rbstr = rb_utf8_str_new_cstr(pwsSid);
709
- rb_hash_aset(hash, rb_str_new2("UserID"), rbstr);
710
- LocalFree(pwsSid);
711
- }
712
- }
713
-
714
- EvtClose(hContext);
715
- ALLOCV_END(vRenderedValues);
716
-
717
- return hash;
718
- }
1
+ #include <winevt_c.h>
2
+
3
+ #include <sddl.h>
4
+ #include <stdlib.h>
5
+ #include <string>
6
+ #include <vector>
7
+
8
+ VALUE
9
+ wstr_to_rb_str(UINT cp, const WCHAR* wstr, int clen)
10
+ {
11
+ VALUE vstr;
12
+ CHAR* ptr;
13
+ int ret = -1;
14
+ DWORD err = ERROR_SUCCESS;
15
+ if (wstr == NULL) {
16
+ return rb_utf8_str_new_cstr("");
17
+ }
18
+
19
+ int len = WideCharToMultiByte(cp, 0, wstr, clen, nullptr, 0, nullptr, nullptr);
20
+ ptr = ALLOCV_N(CHAR, vstr, len);
21
+ // For memory safety.
22
+ ZeroMemory(ptr, sizeof(CHAR) * len);
23
+ ret = WideCharToMultiByte(cp, 0, wstr, clen, ptr, len, nullptr, nullptr);
24
+ // return 0 should be failure.
25
+ // ref: https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-widechartomultibyte#return-value
26
+ if (ret == 0) {
27
+ err = GetLastError();
28
+ ALLOCV_END(vstr);
29
+ raise_system_error(rb_eRuntimeError, err);
30
+ }
31
+ VALUE str = rb_utf8_str_new_cstr(ptr);
32
+ ALLOCV_END(vstr);
33
+
34
+ return str;
35
+ }
36
+
37
+ void
38
+ raise_system_error(VALUE error, DWORD errorCode)
39
+ {
40
+ WCHAR msgBuf[256] = { 0 };
41
+ VALUE errorMessage;
42
+
43
+ FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
44
+ NULL,
45
+ errorCode,
46
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
47
+ msgBuf,
48
+ _countof(msgBuf),
49
+ NULL);
50
+ errorMessage = wstr_to_rb_str(CP_UTF8, msgBuf, -1);
51
+
52
+ #pragma GCC diagnostic push
53
+ #pragma GCC diagnostic ignored "-Wformat="
54
+ #pragma GCC diagnostic ignored "-Wformat-extra-args"
55
+ rb_raise(error, "ErrorCode: %lu\nError: %" PRIsVALUE "\n", errorCode, errorMessage);
56
+ #pragma GCC diagnostic pop
57
+ }
58
+
59
+ VALUE
60
+ render_to_rb_str(EVT_HANDLE handle, DWORD flags)
61
+ {
62
+ VALUE vbuffer;
63
+ WCHAR* buffer;
64
+ ULONG bufferSize = 0;
65
+ ULONG bufferSizeUsed = 0;
66
+ ULONG count;
67
+ BOOL succeeded;
68
+ VALUE result;
69
+
70
+ if (flags != EvtRenderEventXml && flags != EvtRenderBookmark) {
71
+ return Qnil;
72
+ }
73
+
74
+ // Get the size of the buffer
75
+ EvtRender(nullptr, handle, flags, 0, NULL, &bufferSize, &count);
76
+
77
+ // bufferSize is in bytes, not characters
78
+ buffer = (WCHAR*)ALLOCV(vbuffer, bufferSize);
79
+
80
+ succeeded =
81
+ EvtRender(nullptr, handle, flags, bufferSize, buffer, &bufferSizeUsed, &count);
82
+ if (!succeeded) {
83
+ DWORD status = GetLastError();
84
+ ALLOCV_END(vbuffer);
85
+ raise_system_error(rb_eWinevtQueryError, status);
86
+ }
87
+
88
+ result = wstr_to_rb_str(CP_UTF8, buffer, -1);
89
+ ALLOCV_END(vbuffer);
90
+
91
+ return result;
92
+ }
93
+
94
+ EVT_HANDLE
95
+ connect_to_remote(LPWSTR computerName, LPWSTR domain, LPWSTR username, LPWSTR password,
96
+ EVT_RPC_LOGIN_FLAGS flags, DWORD *error_code)
97
+ {
98
+ EVT_HANDLE hRemote = NULL;
99
+ EVT_RPC_LOGIN Credentials;
100
+
101
+ RtlZeroMemory(&Credentials, sizeof(EVT_RPC_LOGIN));
102
+
103
+ Credentials.Server = computerName;
104
+ Credentials.Domain = domain;
105
+ Credentials.User = username;
106
+ Credentials.Password = password;
107
+ Credentials.Flags = flags;
108
+
109
+ hRemote = EvtOpenSession(EvtRpcLogin, &Credentials, 0, 0);
110
+ if (!hRemote) {
111
+ *error_code = GetLastError();
112
+ return hRemote;
113
+ }
114
+
115
+ SecureZeroMemory(&Credentials, sizeof(EVT_RPC_LOGIN));
116
+
117
+ return hRemote;
118
+ }
119
+
120
+ static std::wstring
121
+ guid_to_wstr(const GUID& guid)
122
+ {
123
+ LPOLESTR p = nullptr;
124
+ if (FAILED(StringFromCLSID(guid, &p))) {
125
+ return nullptr;
126
+ }
127
+ std::wstring s(p);
128
+ CoTaskMemFree(p);
129
+ return s;
130
+ }
131
+
132
+ static VALUE
133
+ make_displayable_binary_string(PBYTE bin, size_t length)
134
+ {
135
+ const char *HEX_TABLE = "0123456789ABCDEF";
136
+ CHAR *buffer;
137
+ int size = length * 2 + 1;
138
+ size_t i, j;
139
+ unsigned int idx = 0;
140
+ VALUE vbuffer;
141
+
142
+ if (length == 0) {
143
+ return rb_str_new2("(NULL)");
144
+ }
145
+
146
+ buffer = ALLOCV_N(CHAR, vbuffer, size);
147
+
148
+ for (i = 0; i < length; i++) {
149
+ for (j = 0; j < 2; j++) {
150
+ idx = (unsigned int)(bin[i] >> (j * 4) & 0x0F);
151
+ buffer[2*i+(1-j)] = HEX_TABLE[idx];
152
+ }
153
+ }
154
+ buffer[size - 1] = '\0';
155
+
156
+ VALUE str = rb_str_new2(buffer);
157
+ ALLOCV_END(vbuffer);
158
+
159
+ return str;
160
+ }
161
+
162
+ static VALUE
163
+ extract_user_evt_variants(PEVT_VARIANT pRenderedValues, DWORD propCount)
164
+ {
165
+ VALUE userValues = rb_ary_new();
166
+ VALUE rbObj;
167
+
168
+ for (DWORD i = 0; i < propCount; i++) {
169
+ switch (pRenderedValues[i].Type) {
170
+ case EvtVarTypeNull:
171
+ rb_ary_push(userValues, Qnil);
172
+ break;
173
+ case EvtVarTypeString:
174
+ if (pRenderedValues[i].StringVal == nullptr) {
175
+ rb_ary_push(userValues, rb_utf8_str_new_cstr("(NULL)"));
176
+ } else {
177
+ std::wstring wStr(pRenderedValues[i].StringVal);
178
+ rbObj = wstr_to_rb_str(CP_UTF8, &wStr[0], -1);
179
+ rb_ary_push(userValues, rbObj);
180
+ }
181
+ break;
182
+ case EvtVarTypeAnsiString:
183
+ if (pRenderedValues[i].AnsiStringVal == nullptr) {
184
+ rb_ary_push(userValues, rb_utf8_str_new_cstr("(NULL)"));
185
+ } else {
186
+ rb_ary_push(
187
+ userValues,
188
+ rb_utf8_str_new_cstr(const_cast<char*>(pRenderedValues[i].AnsiStringVal)));
189
+ }
190
+ break;
191
+ case EvtVarTypeSByte:
192
+ rbObj = INT2NUM(static_cast<UINT32>(pRenderedValues[i].SByteVal));
193
+ rb_ary_push(userValues, rbObj);
194
+ break;
195
+ case EvtVarTypeByte:
196
+ rbObj = INT2NUM(static_cast<UINT32>(pRenderedValues[i].ByteVal));
197
+ rb_ary_push(userValues, rbObj);
198
+ break;
199
+ case EvtVarTypeInt16:
200
+ rbObj = INT2NUM(static_cast<INT32>(pRenderedValues[i].Int16Val));
201
+ rb_ary_push(userValues, rbObj);
202
+ break;
203
+ case EvtVarTypeUInt16:
204
+ rbObj = UINT2NUM(static_cast<UINT32>(pRenderedValues[i].UInt16Val));
205
+ rb_ary_push(userValues, rbObj);
206
+ break;
207
+ case EvtVarTypeInt32:
208
+ rbObj = INT2NUM(pRenderedValues[i].Int32Val);
209
+ rb_ary_push(userValues, rbObj);
210
+ break;
211
+ case EvtVarTypeUInt32:
212
+ rbObj = UINT2NUM(pRenderedValues[i].UInt32Val);
213
+ rb_ary_push(userValues, rbObj);
214
+ break;
215
+ case EvtVarTypeInt64:
216
+ rbObj = LONG2NUM(pRenderedValues[i].Int64Val);
217
+ rb_ary_push(userValues, rbObj);
218
+ break;
219
+ case EvtVarTypeUInt64:
220
+ rbObj = ULONG2NUM(pRenderedValues[i].UInt64Val);
221
+ rb_ary_push(userValues, rbObj);
222
+ break;
223
+ case EvtVarTypeSingle: {
224
+ CHAR sResult[256];
225
+ _snprintf_s(
226
+ sResult, _countof(sResult), _TRUNCATE, "%f", pRenderedValues[i].SingleVal);
227
+ rb_ary_push(userValues, rb_utf8_str_new_cstr(sResult));
228
+ break;
229
+ }
230
+ case EvtVarTypeDouble: {
231
+ CHAR sResult[256];
232
+ _snprintf_s(
233
+ sResult, _countof(sResult), _TRUNCATE, "%lf", pRenderedValues[i].DoubleVal);
234
+ rb_ary_push(userValues, rb_utf8_str_new_cstr(sResult));
235
+ break;
236
+ }
237
+ case EvtVarTypeBoolean:
238
+ rbObj = pRenderedValues[i].BooleanVal ? Qtrue : Qfalse;
239
+ rb_ary_push(userValues, rbObj);
240
+ break;
241
+ case EvtVarTypeGuid:
242
+ if (pRenderedValues[i].GuidVal != nullptr) {
243
+ const GUID guid = *pRenderedValues[i].GuidVal;
244
+ std::wstring wstr = guid_to_wstr(guid);
245
+ rbObj = wstr_to_rb_str(CP_UTF8, wstr.c_str(), -1);
246
+ rb_ary_push(userValues, rbObj);
247
+ } else {
248
+ rb_ary_push(userValues, rb_utf8_str_new_cstr("?"));
249
+ }
250
+ break;
251
+ case EvtVarTypeSizeT:
252
+ rbObj = SIZET2NUM(pRenderedValues[i].SizeTVal);
253
+ rb_ary_push(userValues, rbObj);
254
+ break;
255
+ case EvtVarTypeFileTime: {
256
+ LARGE_INTEGER timestamp;
257
+ CHAR strTime[128];
258
+ FILETIME ft;
259
+ SYSTEMTIME st;
260
+ timestamp.QuadPart = pRenderedValues[i].FileTimeVal;
261
+ ft.dwHighDateTime = timestamp.HighPart;
262
+ ft.dwLowDateTime = timestamp.LowPart;
263
+ if (FileTimeToSystemTime(&ft, &st)) {
264
+ _snprintf_s(strTime,
265
+ _countof(strTime),
266
+ _TRUNCATE,
267
+ "%04d-%02d-%02d %02d:%02d:%02d.%dZ",
268
+ st.wYear,
269
+ st.wMonth,
270
+ st.wDay,
271
+ st.wHour,
272
+ st.wMinute,
273
+ st.wSecond,
274
+ st.wMilliseconds);
275
+ rb_ary_push(userValues, rb_utf8_str_new_cstr(strTime));
276
+ } else {
277
+ rb_ary_push(userValues, rb_utf8_str_new_cstr("?"));
278
+ }
279
+ break;
280
+ }
281
+ case EvtVarTypeSysTime: {
282
+ CHAR strTime[128];
283
+ SYSTEMTIME st;
284
+ if (pRenderedValues[i].SysTimeVal != nullptr) {
285
+ st = *pRenderedValues[i].SysTimeVal;
286
+ _snprintf_s(strTime,
287
+ _countof(strTime),
288
+ _TRUNCATE,
289
+ "%04d-%02d-%02d %02d:%02d:%02d.%dZ",
290
+ st.wYear,
291
+ st.wMonth,
292
+ st.wDay,
293
+ st.wHour,
294
+ st.wMinute,
295
+ st.wSecond,
296
+ st.wMilliseconds);
297
+ rb_ary_push(userValues, rb_utf8_str_new_cstr(strTime));
298
+ } else {
299
+ rb_ary_push(userValues, rb_utf8_str_new_cstr("?"));
300
+ }
301
+ break;
302
+ }
303
+ case EvtVarTypeSid: {
304
+ WCHAR* tmpWChar = nullptr;
305
+ if (ConvertSidToStringSidW(pRenderedValues[i].SidVal, &tmpWChar)) {
306
+ rbObj = wstr_to_rb_str(CP_UTF8, tmpWChar, -1);
307
+ rb_ary_push(userValues, rbObj);
308
+ LocalFree(tmpWChar);
309
+ } else {
310
+ rb_ary_push(userValues, rb_utf8_str_new_cstr("?"));
311
+ }
312
+ break;
313
+ }
314
+ case EvtVarTypeHexInt32:
315
+ rbObj = rb_sprintf("%#x", pRenderedValues[i].UInt32Val);
316
+ rb_ary_push(userValues, rbObj);
317
+ break;
318
+ case EvtVarTypeHexInt64:
319
+ uint32_t high;
320
+ uint32_t low;
321
+
322
+ high = pRenderedValues[i].UInt64Val >> 32;
323
+ low = pRenderedValues[i].UInt64Val & 0x00000000FFFFFFFF;
324
+ rbObj = rb_sprintf("0x%08x%08x", high, low);
325
+ rb_ary_push(userValues, rbObj);
326
+ break;
327
+ case EvtVarTypeEvtXml:
328
+ if (pRenderedValues[i].XmlVal == nullptr) {
329
+ rb_ary_push(userValues, rb_utf8_str_new_cstr("(NULL)"));
330
+ } else {
331
+ rbObj = wstr_to_rb_str(CP_UTF8, pRenderedValues[i].XmlVal, -1);
332
+ rb_ary_push(userValues, rbObj);
333
+ }
334
+ break;
335
+ case EvtVarTypeBinary:
336
+ if (pRenderedValues[i].BinaryVal == nullptr) {
337
+ rb_ary_push(userValues, rb_utf8_str_new_cstr("(NULL)"));
338
+ } else {
339
+ rbObj = make_displayable_binary_string(pRenderedValues[i].BinaryVal, pRenderedValues[i].Count);
340
+ rb_ary_push(userValues, rbObj);
341
+ }
342
+ break;
343
+ default:
344
+ rb_ary_push(userValues, rb_utf8_str_new_cstr("?"));
345
+ break;
346
+ }
347
+ }
348
+
349
+ return userValues;
350
+ }
351
+
352
+ VALUE
353
+ get_values(EVT_HANDLE handle)
354
+ {
355
+ VALUE vbuffer;
356
+ PEVT_VARIANT pRenderedValues;
357
+ ULONG bufferSize = 0;
358
+ ULONG bufferSizeUsed = 0;
359
+ DWORD propCount = 0;
360
+ BOOL succeeded;
361
+ VALUE userValues = Qnil;
362
+
363
+ EVT_HANDLE renderContext = EvtCreateRenderContext(0, nullptr, EvtRenderContextUser);
364
+ if (renderContext == nullptr) {
365
+ rb_raise(rb_eWinevtQueryError, "Failed to create renderContext");
366
+ }
367
+
368
+ // Get the size of the buffer
369
+ EvtRender(
370
+ renderContext, handle, EvtRenderEventValues, 0, NULL, &bufferSize, &propCount);
371
+
372
+ // bufferSize is in bytes, not array size
373
+ pRenderedValues = (PEVT_VARIANT)ALLOCV(vbuffer, bufferSize);
374
+
375
+ succeeded = EvtRender(renderContext,
376
+ handle,
377
+ EvtRenderEventValues,
378
+ bufferSize,
379
+ pRenderedValues,
380
+ &bufferSizeUsed,
381
+ &propCount);
382
+ if (!succeeded) {
383
+ DWORD status = GetLastError();
384
+ ALLOCV_END(vbuffer);
385
+ EvtClose(renderContext);
386
+ raise_system_error(rb_eWinevtQueryError, status);
387
+ }
388
+
389
+ userValues = extract_user_evt_variants(pRenderedValues, propCount);
390
+
391
+ ALLOCV_END(vbuffer);
392
+ EvtClose(renderContext);
393
+
394
+ return userValues;
395
+ }
396
+
397
+ static std::vector<WCHAR>
398
+ get_message(EVT_HANDLE hMetadata, EVT_HANDLE handle)
399
+ {
400
+ #define BUFSIZE 4096
401
+ std::vector<WCHAR> result;
402
+ ULONG status;
403
+ ULONG bufferSizeNeeded = 0;
404
+ LPVOID lpMsgBuf;
405
+ std::vector<WCHAR> message(BUFSIZE);
406
+
407
+ if (!EvtFormatMessage(hMetadata,
408
+ handle,
409
+ 0xffffffff,
410
+ 0,
411
+ nullptr,
412
+ EvtFormatMessageEvent,
413
+ message.size(),
414
+ &message[0],
415
+ &bufferSizeNeeded)) {
416
+ status = GetLastError();
417
+
418
+ if (status != ERROR_EVT_UNRESOLVED_VALUE_INSERT) {
419
+ switch (status) {
420
+ case ERROR_EVT_MESSAGE_NOT_FOUND:
421
+ case ERROR_EVT_MESSAGE_ID_NOT_FOUND:
422
+ case ERROR_EVT_MESSAGE_LOCALE_NOT_FOUND:
423
+ case ERROR_RESOURCE_DATA_NOT_FOUND:
424
+ case ERROR_RESOURCE_TYPE_NOT_FOUND:
425
+ case ERROR_RESOURCE_NAME_NOT_FOUND:
426
+ case ERROR_RESOURCE_LANG_NOT_FOUND:
427
+ case ERROR_MUI_FILE_NOT_FOUND:
428
+ case ERROR_EVT_UNRESOLVED_PARAMETER_INSERT: {
429
+ if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
430
+ FORMAT_MESSAGE_IGNORE_INSERTS,
431
+ nullptr,
432
+ status,
433
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
434
+ reinterpret_cast<WCHAR*>(&lpMsgBuf),
435
+ 0,
436
+ nullptr) == 0)
437
+ FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
438
+ FORMAT_MESSAGE_IGNORE_INSERTS,
439
+ nullptr,
440
+ status,
441
+ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
442
+ reinterpret_cast<WCHAR*>(&lpMsgBuf),
443
+ 0,
444
+ nullptr);
445
+
446
+ std::wstring ret(reinterpret_cast<WCHAR*>(lpMsgBuf));
447
+ std::copy(ret.begin(), ret.end(), std::back_inserter(result));
448
+ LocalFree(lpMsgBuf);
449
+
450
+ goto cleanup;
451
+ }
452
+ }
453
+
454
+ if (status != ERROR_INSUFFICIENT_BUFFER)
455
+ rb_raise(rb_eWinevtQueryError, "ErrorCode: %lu", status);
456
+ }
457
+
458
+ if (status == ERROR_INSUFFICIENT_BUFFER) {
459
+ message.resize(bufferSizeNeeded);
460
+ message.shrink_to_fit();
461
+
462
+ if (!EvtFormatMessage(hMetadata,
463
+ handle,
464
+ 0xffffffff,
465
+ 0,
466
+ nullptr,
467
+ EvtFormatMessageEvent,
468
+ message.size(),
469
+ &message.front(),
470
+ &bufferSizeNeeded)) {
471
+ status = GetLastError();
472
+
473
+ if (status != ERROR_EVT_UNRESOLVED_VALUE_INSERT) {
474
+ switch (status) {
475
+ case ERROR_EVT_MESSAGE_NOT_FOUND:
476
+ case ERROR_EVT_MESSAGE_ID_NOT_FOUND:
477
+ case ERROR_EVT_MESSAGE_LOCALE_NOT_FOUND:
478
+ case ERROR_RESOURCE_DATA_NOT_FOUND:
479
+ case ERROR_RESOURCE_TYPE_NOT_FOUND:
480
+ case ERROR_RESOURCE_NAME_NOT_FOUND:
481
+ case ERROR_RESOURCE_LANG_NOT_FOUND:
482
+ case ERROR_MUI_FILE_NOT_FOUND:
483
+ case ERROR_EVT_UNRESOLVED_PARAMETER_INSERT:
484
+ if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
485
+ FORMAT_MESSAGE_FROM_SYSTEM |
486
+ FORMAT_MESSAGE_IGNORE_INSERTS,
487
+ nullptr,
488
+ status,
489
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
490
+ reinterpret_cast<WCHAR*>(&lpMsgBuf),
491
+ 0,
492
+ nullptr) == 0)
493
+ FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
494
+ FORMAT_MESSAGE_FROM_SYSTEM |
495
+ FORMAT_MESSAGE_IGNORE_INSERTS,
496
+ nullptr,
497
+ status,
498
+ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
499
+ reinterpret_cast<WCHAR*>(&lpMsgBuf),
500
+ 0,
501
+ nullptr);
502
+
503
+ std::wstring ret(reinterpret_cast<WCHAR*>(lpMsgBuf));
504
+ std::copy(ret.begin(), ret.end(), std::back_inserter(result));
505
+ LocalFree(lpMsgBuf);
506
+
507
+ goto cleanup;
508
+ }
509
+
510
+ rb_raise(rb_eWinevtQueryError, "ErrorCode: %lu", status);
511
+ }
512
+ }
513
+ }
514
+ }
515
+
516
+ result = message;
517
+
518
+ cleanup:
519
+
520
+ return result;
521
+
522
+ #undef BUFSIZE
523
+ }
524
+
525
+ WCHAR*
526
+ get_description(EVT_HANDLE handle, LANGID langID, EVT_HANDLE hRemote)
527
+ {
528
+ #define BUFSIZE 4096
529
+ std::vector<WCHAR> buffer(BUFSIZE);
530
+ ULONG bufferSizeNeeded = 0;
531
+ ULONG status, count;
532
+ std::vector<WCHAR> result;
533
+ EVT_HANDLE hMetadata = nullptr;
534
+
535
+ static PCWSTR eventProperties[] = { L"Event/System/Provider/@Name" };
536
+ EVT_HANDLE renderContext =
537
+ EvtCreateRenderContext(1, eventProperties, EvtRenderContextValues);
538
+ if (renderContext == nullptr) {
539
+ rb_raise(rb_eWinevtQueryError, "Failed to create renderContext");
540
+ }
541
+
542
+ if (EvtRender(renderContext,
543
+ handle,
544
+ EvtRenderEventValues,
545
+ buffer.size(),
546
+ &buffer.front(),
547
+ &bufferSizeNeeded,
548
+ &count) != FALSE) {
549
+ status = ERROR_SUCCESS;
550
+ } else {
551
+ status = GetLastError();
552
+ }
553
+
554
+ if (status != ERROR_SUCCESS) {
555
+ raise_system_error(rb_eWinevtQueryError, status);
556
+ }
557
+
558
+ // Obtain buffer as EVT_VARIANT pointer. To avoid ErrorCide 87 in EvtRender.
559
+ const PEVT_VARIANT values = reinterpret_cast<PEVT_VARIANT>(&buffer.front());
560
+
561
+ // Open publisher metadata
562
+ hMetadata = EvtOpenPublisherMetadata(
563
+ hRemote,
564
+ values[0].StringVal,
565
+ nullptr,
566
+ MAKELCID(langID, SORT_DEFAULT),
567
+ 0);
568
+ if (hMetadata == nullptr) {
569
+ // When winevt_c cannot open metadata, then give up to obtain
570
+ // message file and clean up immediately.
571
+ goto cleanup;
572
+ }
573
+
574
+ result = get_message(hMetadata, handle);
575
+
576
+ #undef BUFSIZE
577
+
578
+ cleanup:
579
+
580
+ if (renderContext)
581
+ EvtClose(renderContext);
582
+
583
+ if (hMetadata)
584
+ EvtClose(hMetadata);
585
+
586
+ return _wcsdup(result.data());
587
+ }
588
+
589
+ VALUE
590
+ render_system_event(EVT_HANDLE hEvent, BOOL preserve_qualifiers)
591
+ {
592
+ DWORD status = ERROR_SUCCESS;
593
+ EVT_HANDLE hContext = NULL;
594
+ DWORD dwBufferSize = 0;
595
+ DWORD dwBufferUsed = 0;
596
+ DWORD dwPropertyCount = 0;
597
+ VALUE vRenderedValues;
598
+ PEVT_VARIANT pRenderedValues = NULL;
599
+ WCHAR wsGuid[50];
600
+ LPSTR pwsSid = NULL;
601
+ ULONGLONG ullTimeStamp = 0;
602
+ ULONGLONG ullNanoseconds = 0;
603
+ SYSTEMTIME st;
604
+ FILETIME ft;
605
+ CHAR buffer[32];
606
+ VALUE rbstr;
607
+ DWORD EventID;
608
+ VALUE hash = rb_hash_new();
609
+
610
+ hContext = EvtCreateRenderContext(0, NULL, EvtRenderContextSystem);
611
+ if (NULL == hContext) {
612
+ rb_raise(
613
+ rb_eWinevtQueryError, "Failed to create renderContext with %lu\n", GetLastError());
614
+ }
615
+
616
+ if (!EvtRender(hContext,
617
+ hEvent,
618
+ EvtRenderEventValues,
619
+ dwBufferSize,
620
+ pRenderedValues,
621
+ &dwBufferUsed,
622
+ &dwPropertyCount)) {
623
+ status = GetLastError();
624
+ if (ERROR_INSUFFICIENT_BUFFER == status) {
625
+ dwBufferSize = dwBufferUsed;
626
+ pRenderedValues = (PEVT_VARIANT)ALLOCV(vRenderedValues, dwBufferSize);
627
+ if (pRenderedValues) {
628
+ EvtRender(hContext,
629
+ hEvent,
630
+ EvtRenderEventValues,
631
+ dwBufferSize,
632
+ pRenderedValues,
633
+ &dwBufferUsed,
634
+ &dwPropertyCount);
635
+ status = GetLastError();
636
+ } else {
637
+ EvtClose(hContext);
638
+ rb_raise(rb_eRuntimeError, "Failed to malloc memory with %lu\n", status);
639
+ }
640
+ }
641
+
642
+ if (ERROR_SUCCESS != status) {
643
+ EvtClose(hContext);
644
+ ALLOCV_END(vRenderedValues);
645
+
646
+ rb_raise(rb_eWinevtQueryError, "EvtRender failed with %lu\n", status);
647
+ }
648
+ }
649
+
650
+ // EVT_VARIANT value with EvtRenderContextSystem will be decomposed
651
+ // as the following enum definition:
652
+ // https://docs.microsoft.com/en-us/windows/win32/api/winevt/ne-winevt-evt_system_property_id
653
+ rbstr = wstr_to_rb_str(CP_UTF8, pRenderedValues[EvtSystemProviderName].StringVal, -1);
654
+ rb_hash_aset(hash, rb_str_new2("ProviderName"), rbstr);
655
+ if (NULL != pRenderedValues[EvtSystemProviderGuid].GuidVal) {
656
+ const GUID* Guid = pRenderedValues[EvtSystemProviderGuid].GuidVal;
657
+ StringFromGUID2(*Guid, wsGuid, _countof(wsGuid));
658
+ rbstr = wstr_to_rb_str(CP_UTF8, wsGuid, -1);
659
+ rb_hash_aset(hash, rb_str_new2("ProviderGuid"), rbstr);
660
+ } else {
661
+ rb_hash_aset(hash, rb_str_new2("ProviderGuid"), Qnil);
662
+ }
663
+
664
+ EventID = pRenderedValues[EvtSystemEventID].UInt16Val;
665
+ if (preserve_qualifiers) {
666
+ if (EvtVarTypeNull != pRenderedValues[EvtSystemQualifiers].Type) {
667
+ rb_hash_aset(hash, rb_str_new2("Qualifiers"),
668
+ INT2NUM(pRenderedValues[EvtSystemQualifiers].UInt16Val));
669
+ } else {
670
+ rb_hash_aset(hash, rb_str_new2("Qualifiers"), rb_str_new2(""));
671
+ }
672
+
673
+ rb_hash_aset(hash, rb_str_new2("EventID"), INT2NUM(EventID));
674
+ } else {
675
+ if (EvtVarTypeNull != pRenderedValues[EvtSystemQualifiers].Type) {
676
+ EventID = MAKELONG(pRenderedValues[EvtSystemEventID].UInt16Val,
677
+ pRenderedValues[EvtSystemQualifiers].UInt16Val);
678
+ }
679
+
680
+ rb_hash_aset(hash, rb_str_new2("EventID"), ULONG2NUM(EventID));
681
+ }
682
+
683
+ rb_hash_aset(hash,
684
+ rb_str_new2("Version"),
685
+ (EvtVarTypeNull == pRenderedValues[EvtSystemVersion].Type)
686
+ ? INT2NUM(0)
687
+ : INT2NUM(pRenderedValues[EvtSystemVersion].ByteVal));
688
+ rb_hash_aset(hash,
689
+ rb_str_new2("Level"),
690
+ (EvtVarTypeNull == pRenderedValues[EvtSystemLevel].Type)
691
+ ? INT2NUM(0)
692
+ : INT2NUM(pRenderedValues[EvtSystemLevel].ByteVal));
693
+ rb_hash_aset(hash,
694
+ rb_str_new2("Task"),
695
+ (EvtVarTypeNull == pRenderedValues[EvtSystemTask].Type)
696
+ ? INT2NUM(0)
697
+ : INT2NUM(pRenderedValues[EvtSystemTask].UInt16Val));
698
+ rb_hash_aset(hash,
699
+ rb_str_new2("Opcode"),
700
+ (EvtVarTypeNull == pRenderedValues[EvtSystemOpcode].Type)
701
+ ? INT2NUM(0)
702
+ : INT2NUM(pRenderedValues[EvtSystemOpcode].ByteVal));
703
+ _snprintf_s(buffer,
704
+ _countof(buffer),
705
+ _TRUNCATE,
706
+ "0x%llx",
707
+ pRenderedValues[EvtSystemKeywords].UInt64Val);
708
+ rb_hash_aset(hash,
709
+ rb_str_new2("Keywords"),
710
+ (EvtVarTypeNull == pRenderedValues[EvtSystemKeywords].Type)
711
+ ? Qnil
712
+ : rb_str_new2(buffer));
713
+
714
+ if (EvtVarTypeNull != pRenderedValues[EvtSystemTimeCreated].Type) {
715
+ ullTimeStamp = pRenderedValues[EvtSystemTimeCreated].FileTimeVal;
716
+ ft.dwHighDateTime = (DWORD)((ullTimeStamp >> 32) & 0xFFFFFFFF);
717
+ ft.dwLowDateTime = (DWORD)(ullTimeStamp & 0xFFFFFFFF);
718
+
719
+ FileTimeToSystemTime(&ft, &st);
720
+ ullNanoseconds =
721
+ (ullTimeStamp % 10000000) *
722
+ 100; // Display nanoseconds instead of milliseconds for higher resolution
723
+ _snprintf_s(buffer,
724
+ _countof(buffer),
725
+ _TRUNCATE,
726
+ "%02d/%02d/%02d %02d:%02d:%02d.%llu",
727
+ st.wYear,
728
+ st.wMonth,
729
+ st.wDay,
730
+ st.wHour,
731
+ st.wMinute,
732
+ st.wSecond,
733
+ ullNanoseconds);
734
+ rb_hash_aset(hash,
735
+ rb_str_new2("TimeCreated"),
736
+ rb_str_new2(buffer));
737
+ } else {
738
+ rb_hash_aset(hash,
739
+ rb_str_new2("TimeCreated"),
740
+ Qnil);
741
+ }
742
+ _snprintf_s(buffer,
743
+ _countof(buffer),
744
+ _TRUNCATE,
745
+ "%llu",
746
+ pRenderedValues[EvtSystemEventRecordId].UInt64Val);
747
+ rb_hash_aset(hash,
748
+ rb_str_new2("EventRecordID"),
749
+ (EvtVarTypeNull == pRenderedValues[EvtSystemEventRecordId].UInt64Val)
750
+ ? Qnil
751
+ : rb_str_new2(buffer));
752
+
753
+ if (EvtVarTypeNull != pRenderedValues[EvtSystemActivityID].Type) {
754
+ const GUID* Guid = pRenderedValues[EvtSystemActivityID].GuidVal;
755
+ StringFromGUID2(*Guid, wsGuid, _countof(wsGuid));
756
+ rbstr = wstr_to_rb_str(CP_UTF8, wsGuid, -1);
757
+ rb_hash_aset(hash, rb_str_new2("ActivityID"), rbstr);
758
+ }
759
+
760
+ if (EvtVarTypeNull != pRenderedValues[EvtSystemRelatedActivityID].Type) {
761
+ const GUID* Guid = pRenderedValues[EvtSystemRelatedActivityID].GuidVal;
762
+ StringFromGUID2(*Guid, wsGuid, _countof(wsGuid));
763
+ rbstr = wstr_to_rb_str(CP_UTF8, wsGuid, -1);
764
+ rb_hash_aset(hash, rb_str_new2("RelatedActivityID"), rbstr);
765
+ }
766
+
767
+ rb_hash_aset(hash,
768
+ rb_str_new2("ProcessID"),
769
+ UINT2NUM(pRenderedValues[EvtSystemProcessID].UInt32Val));
770
+ rb_hash_aset(hash,
771
+ rb_str_new2("ThreadID"),
772
+ UINT2NUM(pRenderedValues[EvtSystemThreadID].UInt32Val));
773
+ rbstr = wstr_to_rb_str(CP_UTF8, pRenderedValues[EvtSystemChannel].StringVal, -1);
774
+ rb_hash_aset(hash, rb_str_new2("Channel"), rbstr);
775
+ rbstr = wstr_to_rb_str(CP_UTF8, pRenderedValues[EvtSystemComputer].StringVal, -1);
776
+ rb_hash_aset(hash, rb_str_new2("Computer"), rbstr);
777
+
778
+ if (EvtVarTypeNull != pRenderedValues[EvtSystemUserID].Type) {
779
+ if (ConvertSidToStringSid(pRenderedValues[EvtSystemUserID].SidVal, &pwsSid)) {
780
+ rbstr = rb_utf8_str_new_cstr(pwsSid);
781
+ rb_hash_aset(hash, rb_str_new2("UserID"), rbstr);
782
+ LocalFree(pwsSid);
783
+ }
784
+ }
785
+
786
+ EvtClose(hContext);
787
+ ALLOCV_END(vRenderedValues);
788
+
789
+ return hash;
790
+ }