winevt_c 0.9.1 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }