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