1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: BSD - See COPYING.ARM in the top level directory 4 * FILE: ntoskrnl/ps/apphelp.c 5 * PURPOSE: SHIM engine caching. 6 * This caching speeds up checks for the apphelp compatibility layer. 7 * PROGRAMMERS: Mark Jansen 8 */ 9 10 /* 11 Useful references: 12 https://github.com/mandiant/ShimCacheParser/blob/master/ShimCacheParser.py 13 http://technet.microsoft.com/en-us/library/dd837644(v=ws.10).aspx 14 http://msdn.microsoft.com/en-us/library/bb432182(v=vs.85).aspx 15 http://www.alex-ionescu.com/?p=43 16 http://recxltd.blogspot.nl/2012/04/windows-appcompat-research-notes-part-1.html 17 http://journeyintoir.blogspot.ch/2013/12/revealing-recentfilecachebcf-file.html 18 https://dl.mandiant.com/EE/library/Whitepaper_ShimCacheParser.pdf 19 */ 20 21 /* INCLUDES ******************************************************************/ 22 23 #include <ntoskrnl.h> 24 #define NDEBUG 25 #include <debug.h> 26 27 /* GLOBALS *******************************************************************/ 28 29 static BOOLEAN ApphelpCacheEnabled = FALSE; 30 static ERESOURCE ApphelpCacheLock; 31 static RTL_AVL_TABLE ApphelpShimCache; 32 static LIST_ENTRY ApphelpShimCacheAge; 33 34 extern ULONG InitSafeBootMode; 35 36 static UNICODE_STRING AppCompatCacheKey = RTL_CONSTANT_STRING(L"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager\\AppCompatCache"); 37 static OBJECT_ATTRIBUTES AppCompatKeyAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(&AppCompatCacheKey, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE); 38 static UNICODE_STRING AppCompatCacheValue = RTL_CONSTANT_STRING(L"AppCompatCache"); 39 40 #define EMPTY_SHIM_ENTRY { { 0 }, { { 0 } }, 0 } 41 #define MAX_SHIM_ENTRIES 0x200 42 #define TAG_SHIM 'MIHS' 43 44 #ifndef INVALID_HANDLE_VALUE 45 #define INVALID_HANDLE_VALUE (HANDLE)(-1) 46 #endif 47 48 #include <pshpack1.h> 49 50 typedef struct SHIM_PERSISTENT_CACHE_HEADER_52 51 { 52 ULONG Magic; 53 ULONG NumEntries; 54 } SHIM_PERSISTENT_CACHE_HEADER_52, *PSHIM_PERSISTENT_CACHE_HEADER_52; 55 56 /* The data that is present in the registry (Win2k3 version) */ 57 typedef struct SHIM_PERSISTENT_CACHE_ENTRY_52 58 { 59 UNICODE_STRING ImageName; 60 LARGE_INTEGER DateTime; 61 LARGE_INTEGER FileSize; 62 } SHIM_PERSISTENT_CACHE_ENTRY_52, *PSHIM_PERSISTENT_CACHE_ENTRY_52; 63 64 #include <poppack.h> 65 66 #define CACHE_MAGIC_NT_52 0xbadc0ffe 67 #define CACHE_HEADER_SIZE_NT_52 0x8 68 #define NT52_PERSISTENT_ENTRY_SIZE32 0x18 69 #define NT52_PERSISTENT_ENTRY_SIZE64 0x20 70 71 //#define CACHE_MAGIC_NT_61 0xbadc0fee 72 //#define CACHE_HEADER_SIZE_NT_61 0x80 73 //#define NT61_PERSISTENT_ENTRY_SIZE32 0x20 74 //#define NT61_PERSISTENT_ENTRY_SIZE64 0x30 75 76 #define SHIM_CACHE_MAGIC CACHE_MAGIC_NT_52 77 #define SHIM_CACHE_HEADER_SIZE CACHE_HEADER_SIZE_NT_52 78 #ifdef _WIN64 79 #define SHIM_PERSISTENT_CACHE_ENTRY_SIZE NT52_PERSISTENT_ENTRY_SIZE64 80 #else 81 #define SHIM_PERSISTENT_CACHE_ENTRY_SIZE NT52_PERSISTENT_ENTRY_SIZE32 82 #endif 83 #define SHIM_PERSISTENT_CACHE_HEADER SHIM_PERSISTENT_CACHE_HEADER_52 84 #define PSHIM_PERSISTENT_CACHE_HEADER PSHIM_PERSISTENT_CACHE_HEADER_52 85 #define SHIM_PERSISTENT_CACHE_ENTRY SHIM_PERSISTENT_CACHE_ENTRY_52 86 #define PSHIM_PERSISTENT_CACHE_ENTRY PSHIM_PERSISTENT_CACHE_ENTRY_52 87 88 C_ASSERT(sizeof(SHIM_PERSISTENT_CACHE_ENTRY) == SHIM_PERSISTENT_CACHE_ENTRY_SIZE); 89 C_ASSERT(sizeof(SHIM_PERSISTENT_CACHE_HEADER) == SHIM_CACHE_HEADER_SIZE); 90 91 /* The struct we keep in memory */ 92 typedef struct SHIM_CACHE_ENTRY 93 { 94 LIST_ENTRY List; 95 SHIM_PERSISTENT_CACHE_ENTRY Persistent; 96 ULONG CompatFlags; 97 } SHIM_CACHE_ENTRY, *PSHIM_CACHE_ENTRY; 98 99 /* PRIVATE FUNCTIONS *********************************************************/ 100 101 PVOID 102 ApphelpAlloc( 103 _In_ ULONG ByteSize) 104 { 105 return ExAllocatePoolWithTag(PagedPool, ByteSize, TAG_SHIM); 106 } 107 108 VOID 109 ApphelpFree( 110 _In_ PVOID Data) 111 { 112 ExFreePoolWithTag(Data, TAG_SHIM); 113 } 114 115 VOID 116 ApphelpCacheAcquireLock(VOID) 117 { 118 KeEnterCriticalRegion(); 119 ExAcquireResourceExclusiveLite(&ApphelpCacheLock, TRUE); 120 } 121 122 BOOLEAN 123 ApphelpCacheTryAcquireLock(VOID) 124 { 125 KeEnterCriticalRegion(); 126 if (!ExTryToAcquireResourceExclusiveLite(&ApphelpCacheLock)) 127 { 128 KeLeaveCriticalRegion(); 129 return FALSE; 130 } 131 return TRUE; 132 } 133 134 VOID 135 ApphelpCacheReleaseLock(VOID) 136 { 137 ExReleaseResourceLite(&ApphelpCacheLock); 138 KeLeaveCriticalRegion(); 139 } 140 141 VOID 142 ApphelpDuplicateUnicodeString( 143 _Out_ PUNICODE_STRING Destination, 144 _In_ PCUNICODE_STRING Source) 145 { 146 Destination->Length = Source->Length; 147 if (Destination->Length) 148 { 149 Destination->MaximumLength = Destination->Length + sizeof(WCHAR); 150 Destination->Buffer = ApphelpAlloc(Destination->MaximumLength); 151 RtlCopyMemory(Destination->Buffer, Source->Buffer, Destination->Length); 152 Destination->Buffer[Destination->Length / sizeof(WCHAR)] = UNICODE_NULL; 153 } 154 else 155 { 156 Destination->MaximumLength = 0; 157 Destination->Buffer = NULL; 158 } 159 } 160 161 VOID 162 ApphelpFreeUnicodeString( 163 _Inout_ PUNICODE_STRING String) 164 { 165 if (String->Buffer) 166 { 167 ApphelpFree(String->Buffer); 168 } 169 String->Length = 0; 170 String->MaximumLength = 0; 171 String->Buffer = NULL; 172 } 173 174 /* Query file info from a handle, storing it in Entry */ 175 NTSTATUS 176 ApphelpCacheQueryInfo( 177 _In_ HANDLE ImageHandle, 178 _Out_ PSHIM_CACHE_ENTRY Entry) 179 { 180 IO_STATUS_BLOCK IoStatusBlock; 181 FILE_BASIC_INFORMATION FileBasic; 182 FILE_STANDARD_INFORMATION FileStandard; 183 NTSTATUS Status; 184 185 Status = ZwQueryInformationFile(ImageHandle, 186 &IoStatusBlock, 187 &FileBasic, 188 sizeof(FileBasic), 189 FileBasicInformation); 190 if (!NT_SUCCESS(Status)) 191 { 192 return Status; 193 } 194 195 Status = ZwQueryInformationFile(ImageHandle, 196 &IoStatusBlock, 197 &FileStandard, 198 sizeof(FileStandard), 199 FileStandardInformation); 200 if (NT_SUCCESS(Status)) 201 { 202 Entry->Persistent.DateTime = FileBasic.LastWriteTime; 203 Entry->Persistent.FileSize = FileStandard.EndOfFile; 204 } 205 return Status; 206 } 207 208 RTL_GENERIC_COMPARE_RESULTS 209 NTAPI 210 ApphelpShimCacheCompareRoutine( 211 _In_ struct _RTL_AVL_TABLE *Table, 212 _In_ PVOID FirstStruct, 213 _In_ PVOID SecondStruct) 214 { 215 PSHIM_CACHE_ENTRY FirstEntry = FirstStruct; 216 PSHIM_CACHE_ENTRY SecondEntry = SecondStruct; 217 LONG Result; 218 219 Result = RtlCompareUnicodeString(&FirstEntry->Persistent.ImageName, 220 &SecondEntry->Persistent.ImageName, 221 TRUE); 222 if (Result < 0) 223 { 224 return GenericLessThan; 225 } 226 else if (Result == 0) 227 { 228 return GenericEqual; 229 } 230 return GenericGreaterThan; 231 } 232 233 PVOID 234 NTAPI 235 ApphelpShimCacheAllocateRoutine( 236 _In_ struct _RTL_AVL_TABLE *Table, 237 _In_ CLONG ByteSize) 238 { 239 return ApphelpAlloc(ByteSize); 240 } 241 242 VOID 243 NTAPI 244 ApphelpShimCacheFreeRoutine( 245 _In_ struct _RTL_AVL_TABLE *Table, 246 _In_ PVOID Buffer) 247 { 248 ApphelpFree(Buffer); 249 } 250 251 NTSTATUS 252 ApphelpCacheParse( 253 _In_reads_(DataLength) PUCHAR Data, 254 _In_ ULONG DataLength) 255 { 256 PSHIM_PERSISTENT_CACHE_HEADER Header = (PSHIM_PERSISTENT_CACHE_HEADER)Data; 257 ULONG Cur; 258 ULONG NumEntries; 259 UNICODE_STRING String; 260 SHIM_CACHE_ENTRY Entry = EMPTY_SHIM_ENTRY; 261 PSHIM_CACHE_ENTRY Result; 262 PSHIM_PERSISTENT_CACHE_ENTRY Persistent; 263 264 if (DataLength < CACHE_HEADER_SIZE_NT_52) 265 { 266 DPRINT1("SHIMS: ApphelpCacheParse not enough data for a minimal header (0x%x)\n", DataLength); 267 return STATUS_INVALID_PARAMETER; 268 } 269 270 if (Header->Magic != SHIM_CACHE_MAGIC) 271 { 272 DPRINT1("SHIMS: ApphelpCacheParse found invalid magic (0x%x)\n", Header->Magic); 273 return STATUS_INVALID_PARAMETER; 274 } 275 276 NumEntries = Header->NumEntries; 277 DPRINT("SHIMS: ApphelpCacheParse walking %d entries\n", NumEntries); 278 for (Cur = 0; Cur < NumEntries; ++Cur) 279 { 280 Persistent = (PSHIM_PERSISTENT_CACHE_ENTRY)(Data + SHIM_CACHE_HEADER_SIZE + 281 (Cur * SHIM_PERSISTENT_CACHE_ENTRY_SIZE)); 282 /* The entry in the Persistent storage is not really a UNICODE_STRING, 283 so we have to convert the offset into a real pointer before using it. */ 284 String.Length = Persistent->ImageName.Length; 285 String.MaximumLength = Persistent->ImageName.MaximumLength; 286 String.Buffer = (PWCHAR)((ULONG_PTR)Persistent->ImageName.Buffer + Data); 287 288 /* Now we copy all data to a local buffer, that can be safely duplicated by RtlInsert */ 289 Entry.Persistent = *Persistent; 290 ApphelpDuplicateUnicodeString(&Entry.Persistent.ImageName, &String); 291 Result = RtlInsertElementGenericTableAvl(&ApphelpShimCache, 292 &Entry, 293 sizeof(Entry), 294 NULL); 295 if (!Result) 296 { 297 DPRINT1("SHIMS: ApphelpCacheParse insert failed\n"); 298 ApphelpFreeUnicodeString(&Entry.Persistent.ImageName); 299 return STATUS_INVALID_PARAMETER; 300 } 301 InsertTailList(&ApphelpShimCacheAge, &Result->List); 302 } 303 return STATUS_SUCCESS; 304 } 305 306 BOOLEAN 307 ApphelpCacheRead(VOID) 308 { 309 HANDLE KeyHandle; 310 NTSTATUS Status; 311 KEY_VALUE_PARTIAL_INFORMATION KeyValueObject; 312 PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation = &KeyValueObject; 313 ULONG KeyInfoSize, ResultSize; 314 315 Status = ZwOpenKey(&KeyHandle, KEY_QUERY_VALUE, &AppCompatKeyAttributes); 316 if (!NT_SUCCESS(Status)) 317 { 318 DPRINT1("SHIMS: ApphelpCacheRead could not even open Session Manager\\AppCompatCache (0x%x)\n", Status); 319 return FALSE; 320 } 321 322 Status = ZwQueryValueKey(KeyHandle, 323 &AppCompatCacheValue, 324 KeyValuePartialInformation, 325 KeyValueInformation, 326 sizeof(KeyValueObject), 327 &ResultSize); 328 if (Status == STATUS_BUFFER_OVERFLOW) 329 { 330 KeyInfoSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + KeyValueInformation->DataLength; 331 KeyValueInformation = ApphelpAlloc(KeyInfoSize); 332 if (KeyValueInformation != NULL) 333 { 334 Status = ZwQueryValueKey(KeyHandle, 335 &AppCompatCacheValue, 336 KeyValuePartialInformation, 337 KeyValueInformation, 338 KeyInfoSize, 339 &ResultSize); 340 } 341 } 342 343 if (NT_SUCCESS(Status) && KeyValueInformation->Type == REG_BINARY) 344 { 345 Status = ApphelpCacheParse(KeyValueInformation->Data, 346 KeyValueInformation->DataLength); 347 } 348 else 349 { 350 DPRINT1("SHIMS: ApphelpCacheRead not loaded from registry (0x%x)\n", Status); 351 } 352 353 if (KeyValueInformation != &KeyValueObject && KeyValueInformation != NULL) 354 { 355 ApphelpFree(KeyValueInformation); 356 } 357 358 ZwClose(KeyHandle); 359 return NT_SUCCESS(Status); 360 } 361 362 BOOLEAN 363 ApphelpCacheWrite(VOID) 364 { 365 ULONG Length = SHIM_CACHE_HEADER_SIZE; 366 ULONG NumEntries = 0; 367 PLIST_ENTRY ListEntry; 368 PUCHAR Buffer, BufferNamePos; 369 PSHIM_PERSISTENT_CACHE_HEADER Header; 370 PSHIM_PERSISTENT_CACHE_ENTRY WriteEntry; 371 HANDLE KeyHandle; 372 NTSTATUS Status; 373 374 /* First we have to calculate the required size. */ 375 ApphelpCacheAcquireLock(); 376 ListEntry = ApphelpShimCacheAge.Flink; 377 while (ListEntry != &ApphelpShimCacheAge) 378 { 379 PSHIM_CACHE_ENTRY Entry = CONTAINING_RECORD(ListEntry, SHIM_CACHE_ENTRY, List); 380 Length += SHIM_PERSISTENT_CACHE_ENTRY_SIZE; 381 Length += Entry->Persistent.ImageName.MaximumLength; 382 ++NumEntries; 383 ListEntry = ListEntry->Flink; 384 } 385 DPRINT("SHIMS: ApphelpCacheWrite, %d Entries, total size: %d\n", NumEntries, Length); 386 Length = ROUND_UP(Length, sizeof(ULONGLONG)); 387 DPRINT("SHIMS: ApphelpCacheWrite, Rounded to: %d\n", Length); 388 389 /* Now we allocate and prepare some helpers */ 390 Buffer = ApphelpAlloc(Length); 391 BufferNamePos = Buffer + Length; 392 Header = (PSHIM_PERSISTENT_CACHE_HEADER)Buffer; 393 WriteEntry = (PSHIM_PERSISTENT_CACHE_ENTRY)(Buffer + SHIM_CACHE_HEADER_SIZE); 394 395 Header->Magic = SHIM_CACHE_MAGIC; 396 Header->NumEntries = NumEntries; 397 398 ListEntry = ApphelpShimCacheAge.Flink; 399 while (ListEntry != &ApphelpShimCacheAge) 400 { 401 PSHIM_CACHE_ENTRY Entry = CONTAINING_RECORD(ListEntry, SHIM_CACHE_ENTRY, List); 402 USHORT ImageNameLen = Entry->Persistent.ImageName.MaximumLength; 403 /* Copy the Persistent structure over */ 404 *WriteEntry = Entry->Persistent; 405 BufferNamePos -= ImageNameLen; 406 /* Copy the image name over */ 407 RtlCopyMemory(BufferNamePos, Entry->Persistent.ImageName.Buffer, ImageNameLen); 408 /* Fix the Persistent structure, so that Buffer is once again an offset */ 409 WriteEntry->ImageName.Buffer = (PWCH)(BufferNamePos - Buffer); 410 411 ++WriteEntry; 412 ListEntry = ListEntry->Flink; 413 } 414 ApphelpCacheReleaseLock(); 415 416 Status = ZwOpenKey(&KeyHandle, KEY_SET_VALUE, &AppCompatKeyAttributes); 417 if (NT_SUCCESS(Status)) 418 { 419 Status = ZwSetValueKey(KeyHandle, 420 &AppCompatCacheValue, 421 0, 422 REG_BINARY, 423 Buffer, 424 Length); 425 ZwClose(KeyHandle); 426 } 427 else 428 { 429 DPRINT1("SHIMS: ApphelpCacheWrite could not even open Session Manager\\AppCompatCache (0x%x)\n", Status); 430 } 431 432 ApphelpFree(Buffer); 433 return NT_SUCCESS(Status); 434 } 435 436 437 CODE_SEG("INIT") 438 NTSTATUS 439 NTAPI 440 ApphelpCacheInitialize(VOID) 441 { 442 DPRINT("SHIMS: ApphelpCacheInitialize\n"); 443 /* If we are booting in safemode we do not want to use the apphelp cache */ 444 if (InitSafeBootMode) 445 { 446 DPRINT1("SHIMS: Safe mode detected, disabling cache.\n"); 447 ApphelpCacheEnabled = FALSE; 448 } 449 else 450 { 451 ExInitializeResourceLite(&ApphelpCacheLock); 452 RtlInitializeGenericTableAvl(&ApphelpShimCache, 453 ApphelpShimCacheCompareRoutine, 454 ApphelpShimCacheAllocateRoutine, 455 ApphelpShimCacheFreeRoutine, 456 NULL); 457 InitializeListHead(&ApphelpShimCacheAge); 458 ApphelpCacheEnabled = ApphelpCacheRead(); 459 } 460 DPRINT("SHIMS: ApphelpCacheInitialize: %d\n", ApphelpCacheEnabled); 461 return STATUS_SUCCESS; 462 } 463 464 VOID 465 NTAPI 466 ApphelpCacheShutdown(VOID) 467 { 468 if (ApphelpCacheEnabled) 469 { 470 ApphelpCacheWrite(); 471 } 472 } 473 474 NTSTATUS 475 ApphelpValidateData( 476 _In_opt_ PAPPHELP_CACHE_SERVICE_LOOKUP ServiceData, 477 _Out_ PUNICODE_STRING ImageName, 478 _Out_ PHANDLE ImageHandle) 479 { 480 NTSTATUS Status = STATUS_INVALID_PARAMETER; 481 482 if (ServiceData) 483 { 484 UNICODE_STRING LocalImageName; 485 _SEH2_TRY 486 { 487 ProbeForRead(ServiceData, 488 sizeof(APPHELP_CACHE_SERVICE_LOOKUP), 489 sizeof(ULONG)); 490 LocalImageName = ServiceData->ImageName; 491 *ImageHandle = ServiceData->ImageHandle; 492 if (LocalImageName.Length && LocalImageName.Buffer) 493 { 494 ProbeForRead(LocalImageName.Buffer, 495 LocalImageName.Length * sizeof(WCHAR), 496 1); 497 ApphelpDuplicateUnicodeString(ImageName, &LocalImageName); 498 Status = STATUS_SUCCESS; 499 } 500 } 501 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 502 { 503 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 504 } 505 _SEH2_END; 506 } 507 if (!NT_SUCCESS(Status)) 508 { 509 DPRINT1("SHIMS: ApphelpValidateData: invalid data passed\n"); 510 } 511 return Status; 512 } 513 514 NTSTATUS 515 ApphelpCacheRemoveEntryNolock( 516 _In_ PSHIM_CACHE_ENTRY Entry) 517 { 518 if (Entry) 519 { 520 PWSTR Buffer = Entry->Persistent.ImageName.Buffer; 521 RemoveEntryList(&Entry->List); 522 if (RtlDeleteElementGenericTableAvl(&ApphelpShimCache, Entry)) 523 { 524 ApphelpFree(Buffer); 525 } 526 return STATUS_SUCCESS; 527 } 528 return STATUS_NOT_FOUND; 529 } 530 531 NTSTATUS 532 ApphelpCacheLookupEntry( 533 _In_ PUNICODE_STRING ImageName, 534 _In_ HANDLE ImageHandle) 535 { 536 NTSTATUS Status = STATUS_NOT_FOUND; 537 SHIM_CACHE_ENTRY Lookup = EMPTY_SHIM_ENTRY; 538 PSHIM_CACHE_ENTRY Entry; 539 540 if (!ApphelpCacheTryAcquireLock()) 541 { 542 return Status; 543 } 544 545 Lookup.Persistent.ImageName = *ImageName; 546 Entry = RtlLookupElementGenericTableAvl(&ApphelpShimCache, &Lookup); 547 if (Entry == NULL) 548 { 549 DPRINT("SHIMS: ApphelpCacheLookupEntry: could not find %wZ\n", ImageName); 550 goto Cleanup; 551 } 552 553 DPRINT("SHIMS: ApphelpCacheLookupEntry: found %wZ\n", ImageName); 554 if (ImageHandle == INVALID_HANDLE_VALUE) 555 { 556 DPRINT("SHIMS: ApphelpCacheLookupEntry: ok\n"); 557 /* just return if we know it, do not query file info */ 558 Status = STATUS_SUCCESS; 559 } 560 else 561 { 562 Status = ApphelpCacheQueryInfo(ImageHandle, &Lookup); 563 if (NT_SUCCESS(Status) && 564 Lookup.Persistent.DateTime.QuadPart == Entry->Persistent.DateTime.QuadPart && 565 Lookup.Persistent.FileSize.QuadPart == Entry->Persistent.FileSize.QuadPart) 566 { 567 DPRINT("SHIMS: ApphelpCacheLookupEntry: found & validated\n"); 568 Status = STATUS_SUCCESS; 569 /* move it to the front to keep it alive */ 570 RemoveEntryList(&Entry->List); 571 InsertHeadList(&ApphelpShimCacheAge, &Entry->List); 572 } 573 else 574 { 575 DPRINT1("SHIMS: ApphelpCacheLookupEntry: file info mismatch (%lx)\n", Status); 576 Status = STATUS_NOT_FOUND; 577 /* Could not read file info, or it did not match, drop it from the cache */ 578 ApphelpCacheRemoveEntryNolock(Entry); 579 } 580 } 581 582 Cleanup: 583 ApphelpCacheReleaseLock(); 584 return Status; 585 } 586 587 NTSTATUS 588 ApphelpCacheRemoveEntry( 589 _In_ PUNICODE_STRING ImageName) 590 { 591 PSHIM_CACHE_ENTRY Entry; 592 NTSTATUS Status; 593 594 ApphelpCacheAcquireLock(); 595 Entry = RtlLookupElementGenericTableAvl(&ApphelpShimCache, ImageName); 596 Status = ApphelpCacheRemoveEntryNolock(Entry); 597 ApphelpCacheReleaseLock(); 598 return Status; 599 } 600 601 /* Validate that we are either called from r0, or from a service-like context */ 602 NTSTATUS 603 ApphelpCacheAccessCheck(VOID) 604 { 605 if (ExGetPreviousMode() != KernelMode) 606 { 607 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, UserMode)) 608 { 609 DPRINT1("SHIMS: ApphelpCacheAccessCheck failed\n"); 610 return STATUS_ACCESS_DENIED; 611 } 612 } 613 return STATUS_SUCCESS; 614 } 615 616 NTSTATUS 617 ApphelpCacheUpdateEntry( 618 _In_ PUNICODE_STRING ImageName, 619 _In_ HANDLE ImageHandle) 620 { 621 NTSTATUS Status = STATUS_SUCCESS; 622 SHIM_CACHE_ENTRY Entry = EMPTY_SHIM_ENTRY; 623 PSHIM_CACHE_ENTRY Lookup; 624 PVOID NodeOrParent; 625 TABLE_SEARCH_RESULT SearchResult; 626 627 ApphelpCacheAcquireLock(); 628 629 /* If we got a file handle, query it for info */ 630 if (ImageHandle != INVALID_HANDLE_VALUE) 631 { 632 Status = ApphelpCacheQueryInfo(ImageHandle, &Entry); 633 if (!NT_SUCCESS(Status)) 634 { 635 goto Cleanup; 636 } 637 } 638 639 /* Use ImageName for the lookup, don't actually duplicate it */ 640 Entry.Persistent.ImageName = *ImageName; 641 Lookup = RtlLookupElementGenericTableFullAvl(&ApphelpShimCache, 642 &Entry, 643 &NodeOrParent, &SearchResult); 644 if (Lookup) 645 { 646 DPRINT("SHIMS: ApphelpCacheUpdateEntry: Entry already exists, reusing it\n"); 647 /* Unlink the found item, so we can put it back at the front, 648 and copy the earlier obtained file info*/ 649 RemoveEntryList(&Lookup->List); 650 Lookup->Persistent.DateTime = Entry.Persistent.DateTime; 651 Lookup->Persistent.FileSize = Entry.Persistent.FileSize; 652 } 653 else 654 { 655 DPRINT("SHIMS: ApphelpCacheUpdateEntry: Inserting new Entry\n"); 656 /* Insert a new entry, with its own copy of the ImageName */ 657 ApphelpDuplicateUnicodeString(&Entry.Persistent.ImageName, ImageName); 658 Lookup = RtlInsertElementGenericTableFullAvl(&ApphelpShimCache, 659 &Entry, 660 sizeof(Entry), 661 0, 662 NodeOrParent, 663 SearchResult); 664 if (!Lookup) 665 { 666 ApphelpFreeUnicodeString(&Entry.Persistent.ImageName); 667 Status = STATUS_NO_MEMORY; 668 } 669 } 670 if (Lookup) 671 { 672 /* Either we re-used an existing item, or we inserted a new one, keep it alive */ 673 InsertHeadList(&ApphelpShimCacheAge, &Lookup->List); 674 if (RtlNumberGenericTableElementsAvl(&ApphelpShimCache) > MAX_SHIM_ENTRIES) 675 { 676 PSHIM_CACHE_ENTRY Remove; 677 DPRINT1("SHIMS: ApphelpCacheUpdateEntry: Cache growing too big, dropping oldest item\n"); 678 Remove = CONTAINING_RECORD(ApphelpShimCacheAge.Blink, SHIM_CACHE_ENTRY, List); 679 Status = ApphelpCacheRemoveEntryNolock(Remove); 680 } 681 } 682 683 Cleanup: 684 ApphelpCacheReleaseLock(); 685 return Status; 686 } 687 688 NTSTATUS 689 ApphelpCacheFlush(VOID) 690 { 691 PVOID p; 692 693 DPRINT1("SHIMS: ApphelpCacheFlush\n"); 694 ApphelpCacheAcquireLock(); 695 while ((p = RtlEnumerateGenericTableAvl(&ApphelpShimCache, TRUE))) 696 { 697 ApphelpCacheRemoveEntryNolock((PSHIM_CACHE_ENTRY)p); 698 } 699 ApphelpCacheReleaseLock(); 700 return STATUS_SUCCESS; 701 } 702 703 NTSTATUS 704 ApphelpCacheDump(VOID) 705 { 706 PLIST_ENTRY ListEntry; 707 PSHIM_CACHE_ENTRY Entry; 708 709 DPRINT1("SHIMS: NtApphelpCacheControl( Dumping entries, newest to oldest )\n"); 710 ApphelpCacheAcquireLock(); 711 ListEntry = ApphelpShimCacheAge.Flink; 712 while (ListEntry != &ApphelpShimCacheAge) 713 { 714 Entry = CONTAINING_RECORD(ListEntry, SHIM_CACHE_ENTRY, List); 715 DPRINT1("Entry: %wZ\n", &Entry->Persistent.ImageName); 716 DPRINT1("DateTime: 0x%I64x\n", Entry->Persistent.DateTime.QuadPart); 717 DPRINT1("FileSize: 0x%I64x\n", Entry->Persistent.FileSize.QuadPart); 718 DPRINT1("Flags: 0x%x\n", Entry->CompatFlags); 719 ListEntry = ListEntry->Flink; 720 } 721 ApphelpCacheReleaseLock(); 722 return STATUS_SUCCESS; 723 } 724 725 /* PUBLIC FUNCTIONS **********************************************************/ 726 727 NTSTATUS 728 NTAPI 729 NtApphelpCacheControl( 730 _In_ APPHELPCACHESERVICECLASS Service, 731 _In_opt_ PAPPHELP_CACHE_SERVICE_LOOKUP ServiceData) 732 { 733 NTSTATUS Status = STATUS_INVALID_PARAMETER; 734 UNICODE_STRING ImageName = { 0 }; 735 HANDLE Handle = INVALID_HANDLE_VALUE; 736 737 if (!ApphelpCacheEnabled) 738 { 739 DPRINT1("NtApphelpCacheControl: ApphelpCacheEnabled == 0\n"); 740 return Status; 741 } 742 switch (Service) 743 { 744 case ApphelpCacheServiceLookup: 745 DPRINT("SHIMS: NtApphelpCacheControl( ApphelpCacheServiceLookup )\n"); 746 Status = ApphelpValidateData(ServiceData, &ImageName, &Handle); 747 if (NT_SUCCESS(Status)) 748 Status = ApphelpCacheLookupEntry(&ImageName, Handle); 749 break; 750 case ApphelpCacheServiceRemove: 751 DPRINT("SHIMS: NtApphelpCacheControl( ApphelpCacheServiceRemove )\n"); 752 Status = ApphelpValidateData(ServiceData, &ImageName, &Handle); 753 if (NT_SUCCESS(Status)) 754 Status = ApphelpCacheRemoveEntry(&ImageName); 755 break; 756 case ApphelpCacheServiceUpdate: 757 DPRINT("SHIMS: NtApphelpCacheControl( ApphelpCacheServiceUpdate )\n"); 758 Status = ApphelpCacheAccessCheck(); 759 if (NT_SUCCESS(Status)) 760 { 761 Status = ApphelpValidateData(ServiceData, &ImageName, &Handle); 762 if (NT_SUCCESS(Status)) 763 Status = ApphelpCacheUpdateEntry(&ImageName, Handle); 764 } 765 break; 766 case ApphelpCacheServiceFlush: 767 /* FIXME: Check for admin or system here. */ 768 Status = ApphelpCacheFlush(); 769 break; 770 case ApphelpCacheServiceDump: 771 Status = ApphelpCacheDump(); 772 break; 773 case ApphelpDBGReadRegistry: 774 DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpDBGReadRegistry ): flushing cache.\n"); 775 ApphelpCacheFlush(); 776 DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpDBGReadRegistry ): reading cache.\n"); 777 Status = ApphelpCacheRead() ? STATUS_SUCCESS : STATUS_NOT_FOUND; 778 break; 779 case ApphelpDBGWriteRegistry: 780 DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpDBGWriteRegistry ): writing cache.\n"); 781 Status = ApphelpCacheWrite() ? STATUS_SUCCESS : STATUS_NOT_FOUND; 782 break; 783 default: 784 DPRINT1("SHIMS: NtApphelpCacheControl( Invalid service requested )\n"); 785 break; 786 } 787 if (ImageName.Buffer) 788 { 789 ApphelpFreeUnicodeString(&ImageName); 790 } 791 return Status; 792 } 793 794