1 /* etw_message.h
2  *
3  * Copyright 2020, Odysseus Yang
4  *
5  * Wireshark - Network traffic analyzer
6  * By Gerald Combs <gerald@wireshark.org>
7  * Copyright 1998 Gerald Combs
8  *
9  * SPDX-License-Identifier: GPL-2.0-or-later
10  */
11 #include "config.h"
12 
13 #include "etw_message.h"
14 #include <wsutil/wslog.h>
15 ULONGLONG g_num_events = 0;
16 
17 VOID format_message(WCHAR* lpszMessage, PROPERTY_KEY_VALUE* propArray, DWORD dwPropertyCount, WCHAR* lpszOutBuffer, DWORD dwOutBufferCount)
18 {
19     DWORD startLoc = 0;
20     int percent_loc = 0;
21 
22     for (int i = 0; lpszMessage[i] != L'\0';)
23     {
24         if (lpszMessage[i] != L'%')
25         {
26             i++;
27             continue;
28         }
29         if (lpszMessage[i + 1] == '%')
30         {
31             i += 2;
32             continue;
33         }
34 
35         percent_loc = i;
36         i++;
37 
38         if (iswdigit(lpszMessage[i]))
39         {
40             DWORD dwDigitalCount = 0;
41             WCHAR smallBuffer[MAX_SMALL_BUFFER] = { 0 };
42             while (iswdigit(lpszMessage[i]))
43             {
44                 if (dwDigitalCount < (MAX_SMALL_BUFFER - 1))
45                 {
46                     smallBuffer[dwDigitalCount] = lpszMessage[i];
47                 }
48                 dwDigitalCount++;
49                 i++;
50             }
51 
52             /* We are not parsing this */
53             if (dwDigitalCount >= (MAX_SMALL_BUFFER - 1))
54             {
55                 continue;
56             }
57             DWORD num = _wtoi(smallBuffer);
58             /* We are not parsing this */
59             if (num == 0 || num > dwPropertyCount || propArray[num - 1].value[0] == L'\0')
60             {
61                 continue;
62             }
63 
64             if (lpszMessage[i] == L'!' && lpszMessage[i + 1] == L'S' && lpszMessage[i + 2] == L'!')
65             {
66                 i += 3;
67             }
68 
69             /* We have everything */
70             lpszMessage[percent_loc] = L'\0';
71             StringCbCat(lpszOutBuffer, dwOutBufferCount, lpszMessage + startLoc);
72             StringCbCat(lpszOutBuffer, dwOutBufferCount, propArray[num - 1].value);
73             startLoc = i;
74             continue; // for
75         }
76     }
77     StringCbCat(lpszOutBuffer, dwOutBufferCount, lpszMessage + startLoc);
78 }
79 
80 /*
81 * Get the length of the property data. For MOF-based events, the size is inferred from the data type
82 * of the property. For manifest-based events, the property can specify the size of the property value
83 * using the length attribute. The length attribue can specify the size directly or specify the name
84 * of another property in the event data that contains the size. If the property does not include the
85 * length attribute, the size is inferred from the data type. The length will be zero for variable
86 * length, null-terminated strings and structures.
87 */
88 DWORD GetPropertyLength(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, USHORT i, PUSHORT PropertyLength)
89 {
90     DWORD status = ERROR_SUCCESS;
91     PROPERTY_DATA_DESCRIPTOR DataDescriptor = { 0 };
92     DWORD PropertySize = 0;
93 
94     /*
95     * If the property is a binary blob and is defined in a manifest, the property can
96     * specify the blob's size or it can point to another property that defines the
97     * blob's size. The PropertyParamLength flag tells you where the blob's size is defined.
98     */
99     if ((pInfo->EventPropertyInfoArray[i].Flags & PropertyParamLength) == PropertyParamLength)
100     {
101         DWORD Length = 0;  // Expects the length to be defined by a UINT16 or UINT32
102         DWORD j = pInfo->EventPropertyInfoArray[i].lengthPropertyIndex;
103         DataDescriptor.PropertyName = ((ULONGLONG)(pInfo)+(ULONGLONG)pInfo->EventPropertyInfoArray[j].NameOffset);
104         DataDescriptor.ArrayIndex = ULONG_MAX;
105         status = TdhGetPropertySize(pEvent, 0, NULL, 1, &DataDescriptor, &PropertySize);
106         status = TdhGetProperty(pEvent, 0, NULL, 1, &DataDescriptor, PropertySize, (PBYTE)&Length);
107         *PropertyLength = (USHORT)Length;
108     }
109     else
110     {
111         if (pInfo->EventPropertyInfoArray[i].length > 0)
112         {
113             *PropertyLength = pInfo->EventPropertyInfoArray[i].length;
114         }
115         else
116         {
117             /*
118             * If the property is a binary blob and is defined in a MOF class, the extension
119             * qualifier is used to determine the size of the blob. However, if the extension
120             * is IPAddrV6, you must set the PropertyLength variable yourself because the
121             * EVENT_PROPERTY_INFO.length field will be zero.
122             */
123             if (TDH_INTYPE_BINARY == pInfo->EventPropertyInfoArray[i].nonStructType.InType &&
124                 TDH_OUTTYPE_IPV6 == pInfo->EventPropertyInfoArray[i].nonStructType.OutType)
125             {
126                 *PropertyLength = (USHORT)sizeof(IN6_ADDR);
127             }
128             else if (TDH_INTYPE_UNICODESTRING == pInfo->EventPropertyInfoArray[i].nonStructType.InType ||
129                 TDH_INTYPE_ANSISTRING == pInfo->EventPropertyInfoArray[i].nonStructType.InType ||
130                 (pInfo->EventPropertyInfoArray[i].Flags & PropertyStruct) == PropertyStruct)
131             {
132                 *PropertyLength = pInfo->EventPropertyInfoArray[i].length;
133             }
134             else
135             {
136                 ws_debug("Event %d Unexpected length of 0 for intype %d and outtype %d", g_num_events,
137                     pInfo->EventPropertyInfoArray[i].nonStructType.InType,
138                     pInfo->EventPropertyInfoArray[i].nonStructType.OutType);
139 
140                 status = ERROR_EVT_INVALID_EVENT_DATA;
141                 goto cleanup;
142             }
143         }
144     }
145 cleanup:
146     return status;
147 }
148 
149 DWORD GetArraySize(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, USHORT i, PUSHORT ArraySize)
150 {
151     DWORD status = ERROR_SUCCESS;
152     PROPERTY_DATA_DESCRIPTOR DataDescriptor = { 0 };
153     DWORD PropertySize = 0;
154 
155     if ((pInfo->EventPropertyInfoArray[i].Flags & PropertyParamCount) == PropertyParamCount)
156     {
157         /* Expects the count to be defined by a UINT16 or UINT32 */
158         DWORD Count = 0;
159         DWORD j = pInfo->EventPropertyInfoArray[i].countPropertyIndex;
160         DataDescriptor.PropertyName = ((ULONGLONG)(pInfo)+(ULONGLONG)(pInfo->EventPropertyInfoArray[j].NameOffset));
161         DataDescriptor.ArrayIndex = ULONG_MAX;
162         status = TdhGetPropertySize(pEvent, 0, NULL, 1, &DataDescriptor, &PropertySize);
163         status = TdhGetProperty(pEvent, 0, NULL, 1, &DataDescriptor, PropertySize, (PBYTE)&Count);
164         *ArraySize = (USHORT)Count;
165     }
166     else
167     {
168         *ArraySize = pInfo->EventPropertyInfoArray[i].count;
169     }
170     return status;
171 }
172 
173 DWORD GetMapInfo(PEVENT_RECORD pEvent, LPWSTR pMapName, PEVENT_MAP_INFO* pMapInfo)
174 {
175     DWORD status = ERROR_SUCCESS;
176     DWORD MapSize = 0;
177 
178     /* Retrieve the required buffer size for the map info. */
179     status = TdhGetEventMapInformation(pEvent, pMapName, *pMapInfo, &MapSize);
180     if (ERROR_INSUFFICIENT_BUFFER == status)
181     {
182         *pMapInfo = (PEVENT_MAP_INFO)g_malloc(MapSize);
183         if (*pMapInfo == NULL)
184         {
185             status = ERROR_OUTOFMEMORY;
186             goto cleanup;
187         }
188         /* Retrieve the map info. */
189         status = TdhGetEventMapInformation(pEvent, pMapName, *pMapInfo, &MapSize);
190     }
191 
192     if (ERROR_NOT_FOUND == status)
193     {
194         /* This case is okay. */
195         status = ERROR_SUCCESS;
196     }
197 
198 cleanup:
199 
200     return status;
201 }
202 
203 
204 PBYTE extract_properties(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, DWORD PointerSize, USHORT i, PBYTE pUserData, PBYTE pEndOfUserData, PROPERTY_KEY_VALUE* pExtract)
205 {
206     TDHSTATUS status = ERROR_SUCCESS;
207     USHORT PropertyLength = 0;
208     USHORT UserDataConsumed = 0;
209     /* Last member of a structure */
210     DWORD LastMember = 0;
211     USHORT ArraySize = 0;
212     PEVENT_MAP_INFO pMapInfo = NULL;
213     WCHAR formatted_data[MAX_LOG_LINE_LENGTH];
214     DWORD formatted_data_size = sizeof(formatted_data);
215     LPWSTR oversize_formatted_data = NULL;
216 
217     do
218     {
219         StringCbCopy(pExtract->key, sizeof(pExtract->key), (PWCHAR)((PBYTE)(pInfo)+pInfo->EventPropertyInfoArray[i].NameOffset));
220         /* Get the length of the property. */
221         status = GetPropertyLength(pEvent, pInfo, i, &PropertyLength);
222         if (ERROR_SUCCESS != status)
223         {
224             StringCbPrintf(pExtract->value, sizeof(pExtract->value), L"%s: GetPropertyLength failed 0x%x", pExtract->key, status);
225             break;
226         }
227 
228         /* Get the size of the array if the property is an array. */
229         status = GetArraySize(pEvent, pInfo, i, &ArraySize);
230         if (ERROR_SUCCESS != status)
231         {
232             StringCbPrintf(pExtract->value, sizeof(pExtract->value), L"%s: GetArraySize failed 0x%x", pExtract->key, status);
233             break;
234         }
235 
236         /* Add [] for an array property */
237         if (ArraySize > 1)
238         {
239             StringCbCat(pExtract->value, sizeof(pExtract->value), L"[");
240         }
241 
242         for (USHORT k = 0; k < ArraySize; k++)
243         {
244             /* Add array item separator "," */
245             if (k > 0)
246             {
247                 StringCbCat(pExtract->value, sizeof(pExtract->value), L",");
248             }
249             /* If the property is a structure, print the members of the structure. */
250             if ((pInfo->EventPropertyInfoArray[i].Flags & PropertyStruct) == PropertyStruct)
251             {
252                 /* Add {} for an array property */
253                 StringCbCat(pExtract->value, sizeof(pExtract->value), L"{");
254                 /* Add struct member separator ";" */
255                 if (k > 0)
256                 {
257                     StringCbCat(pExtract->value, sizeof(pExtract->value), L";");
258                 }
259                 LastMember = pInfo->EventPropertyInfoArray[i].structType.StructStartIndex +
260                     pInfo->EventPropertyInfoArray[i].structType.NumOfStructMembers;
261 
262                 for (USHORT j = pInfo->EventPropertyInfoArray[i].structType.StructStartIndex; j < LastMember; j++)
263                 {
264                     pUserData = extract_properties(pEvent, pInfo, PointerSize, j, pUserData, pEndOfUserData, pExtract);
265                     if (NULL == pUserData)
266                     {
267                         StringCbPrintf(pExtract->value, sizeof(pExtract->value), L"%s: extract_properties of member %d failed 0x%x", pExtract->key, j, status);
268                         break;
269                     }
270                 }
271                 StringCbCat(pExtract->value, sizeof(pExtract->value), L"}");
272             }
273             else
274             {
275                 /* Get the name/value mapping only at the first time if the property specifies a value map. */
276                 if (pMapInfo == NULL)
277                 {
278                     status = GetMapInfo(pEvent,
279                         (PWCHAR)((PBYTE)(pInfo)+pInfo->EventPropertyInfoArray[i].nonStructType.MapNameOffset),
280                         &pMapInfo);
281 
282                     if (ERROR_SUCCESS != status)
283                     {
284                         StringCbPrintf(pExtract->value, sizeof(pExtract->value), L"%s: GetMapInfo failed 0x%x", pExtract->key, status);
285                         break;
286                     }
287                 }
288 
289                 /* Get the size of the buffer required for the formatted data. */
290 
291                 status = TdhFormatProperty(
292                     pInfo,
293                     pMapInfo,
294                     PointerSize,
295                     pInfo->EventPropertyInfoArray[i].nonStructType.InType,
296                     pInfo->EventPropertyInfoArray[i].nonStructType.OutType,
297                     PropertyLength,
298                     (USHORT)(pEndOfUserData - pUserData),
299                     pUserData,
300                     &formatted_data_size,
301                     formatted_data,
302                     &UserDataConsumed);
303 
304                 if (ERROR_INSUFFICIENT_BUFFER == status)
305                 {
306                     if (oversize_formatted_data)
307                     {
308                         g_free(oversize_formatted_data);
309                         oversize_formatted_data = NULL;
310                     }
311 
312                     oversize_formatted_data = (LPWSTR)g_malloc(formatted_data_size);
313                     if (oversize_formatted_data == NULL)
314                     {
315                         status = ERROR_OUTOFMEMORY;
316                         StringCbPrintf(pExtract->value, sizeof(pExtract->value), L"%s: Allocate FormattedData memory (size %d) for array item %d failed 0x%x", pExtract->key, formatted_data_size, k, status);
317                         break;
318                     }
319 
320                     /* Retrieve the formatted data. */
321                     status = TdhFormatProperty(
322                         pInfo,
323                         pMapInfo,
324                         PointerSize,
325                         pInfo->EventPropertyInfoArray[i].nonStructType.InType,
326                         pInfo->EventPropertyInfoArray[i].nonStructType.OutType,
327                         PropertyLength,
328                         (USHORT)(pEndOfUserData - pUserData),
329                         pUserData,
330                         &formatted_data_size,
331                         oversize_formatted_data,
332                         &UserDataConsumed);
333                 }
334 
335                 if (ERROR_SUCCESS == status)
336                 {
337                     if (formatted_data_size > sizeof(formatted_data) && oversize_formatted_data != NULL)
338                     {
339                         /* Any oversize FormattedData will be truncated */
340                         StringCbCat(pExtract->value, sizeof(pExtract->value), oversize_formatted_data);
341                     }
342                     else
343                     {
344                         StringCbCat(pExtract->value, sizeof(pExtract->value), formatted_data);
345                     }
346                     pUserData += UserDataConsumed;
347                 }
348                 else
349                 {
350                     StringCbPrintf(pExtract->value, sizeof(pExtract->value), L"%s: TdhFormatProperty for array item %d failed 0x%x", pExtract->key, k, status);
351                     break;
352                 }
353             }
354         }
355         /* Add [] for an array property */
356         if (ArraySize > 1)
357         {
358             StringCbCat(pExtract->value, sizeof(pExtract->value), L"]");
359         }
360     } while (FALSE);
361 
362     if (oversize_formatted_data)
363     {
364         g_free(oversize_formatted_data);
365         oversize_formatted_data = NULL;
366     }
367     if (pMapInfo)
368     {
369         g_free(pMapInfo);
370         pMapInfo = NULL;
371     }
372 
373     return (ERROR_SUCCESS == status) ? pUserData : NULL;
374 }
375 
376 
377 BOOL get_event_information(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO* pInfo)
378 {
379     BOOL bReturn = FALSE;
380     DWORD status;
381     DWORD BufferSize = 0;
382 
383     /* Retrieve the required buffer size for the event metadata. */
384     status = TdhGetEventInformation(pEvent, 0, NULL, *pInfo, &BufferSize);
385     if (ERROR_INSUFFICIENT_BUFFER == status)
386     {
387         *pInfo = (TRACE_EVENT_INFO*)g_malloc(BufferSize);
388         if (*pInfo == NULL)
389         {
390             ws_debug("Event %d GetEventInformation Failed to allocate memory for event info (size=%lu).", g_num_events, BufferSize);
391             goto Exit;
392         }
393         /* Retrieve the event metadata. */
394         status = TdhGetEventInformation(pEvent, 0, NULL, *pInfo, &BufferSize);
395     }
396 
397     if (ERROR_SUCCESS != status)
398     {
399         goto Exit;
400     }
401     bReturn = TRUE;
402 Exit:
403 
404     return bReturn;
405 }
406 
407 /*
408  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
409  *
410  * Local variables:
411  * c-basic-offset: 4
412  * tab-width: 8
413  * indent-tabs-mode: nil
414  * End:
415  *
416  * vi: set shiftwidth=4 tabstop=8 expandtab:
417  * :indentSize=4:tabSize=8:noTabs=true:
418  */
419