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