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
ApphelpAlloc(_In_ ULONG ByteSize)101 ApphelpAlloc(
102 _In_ ULONG ByteSize)
103 {
104 return ExAllocatePoolWithTag(PagedPool, ByteSize, TAG_SHIM);
105 }
106
107 VOID
ApphelpFree(_In_ PVOID Data)108 ApphelpFree(
109 _In_ PVOID Data)
110 {
111 ExFreePoolWithTag(Data, TAG_SHIM);
112 }
113
114 VOID
ApphelpCacheAcquireLock(VOID)115 ApphelpCacheAcquireLock(VOID)
116 {
117 KeEnterCriticalRegion();
118 ExAcquireResourceExclusiveLite(&ApphelpCacheLock, TRUE);
119 }
120
121 BOOLEAN
ApphelpCacheTryAcquireLock(VOID)122 ApphelpCacheTryAcquireLock(VOID)
123 {
124 KeEnterCriticalRegion();
125 if (!ExTryToAcquireResourceExclusiveLite(&ApphelpCacheLock))
126 {
127 KeLeaveCriticalRegion();
128 return FALSE;
129 }
130 return TRUE;
131 }
132
133 VOID
ApphelpCacheReleaseLock(VOID)134 ApphelpCacheReleaseLock(VOID)
135 {
136 ExReleaseResourceLite(&ApphelpCacheLock);
137 KeLeaveCriticalRegion();
138 }
139
140 VOID
ApphelpDuplicateUnicodeString(_Out_ PUNICODE_STRING Destination,_In_ PCUNICODE_STRING Source)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
ApphelpFreeUnicodeString(_Inout_ PUNICODE_STRING String)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
ApphelpCacheQueryInfo(_In_ HANDLE ImageHandle,_Out_ PSHIM_CACHE_ENTRY Entry)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
ApphelpShimCacheCompareRoutine(_In_ struct _RTL_AVL_TABLE * Table,_In_ PVOID FirstStruct,_In_ PVOID SecondStruct)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
ApphelpShimCacheAllocateRoutine(_In_ struct _RTL_AVL_TABLE * Table,_In_ CLONG ByteSize)234 ApphelpShimCacheAllocateRoutine(
235 _In_ struct _RTL_AVL_TABLE *Table,
236 _In_ CLONG ByteSize)
237 {
238 return ApphelpAlloc(ByteSize);
239 }
240
241 VOID
242 NTAPI
ApphelpShimCacheFreeRoutine(_In_ struct _RTL_AVL_TABLE * Table,_In_ PVOID Buffer)243 ApphelpShimCacheFreeRoutine(
244 _In_ struct _RTL_AVL_TABLE *Table,
245 _In_ PVOID Buffer)
246 {
247 ApphelpFree(Buffer);
248 }
249
250 NTSTATUS
ApphelpCacheParse(_In_reads_ (DataLength)PUCHAR Data,_In_ ULONG DataLength)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
ApphelpCacheRead(VOID)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
ApphelpCacheWrite(VOID)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
ApphelpCacheInitialize(VOID)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
ApphelpCacheShutdown(VOID)465 ApphelpCacheShutdown(VOID)
466 {
467 if (ApphelpCacheEnabled)
468 {
469 ApphelpCacheWrite();
470 }
471 }
472
473 NTSTATUS
ApphelpValidateData(_In_opt_ PAPPHELP_CACHE_SERVICE_LOOKUP ServiceData,_Out_ PUNICODE_STRING ImageName,_Out_ PHANDLE ImageHandle)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
ApphelpCacheRemoveEntryNolock(_In_ PSHIM_CACHE_ENTRY Entry)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
ApphelpCacheLookupEntry(_In_ PUNICODE_STRING ImageName,_In_ HANDLE ImageHandle)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
ApphelpCacheRemoveEntry(_In_ PUNICODE_STRING ImageName)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
ApphelpCacheAccessCheck(VOID)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
ApphelpCacheUpdateEntry(_In_ PUNICODE_STRING ImageName,_In_ HANDLE ImageHandle)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
ApphelpCacheFlush(VOID)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
ApphelpCacheDump(VOID)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
NtApphelpCacheControl(_In_ APPHELPCACHESERVICECLASS Service,_In_opt_ PAPPHELP_CACHE_SERVICE_LOOKUP ServiceData)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