d6c24dcb69b74b1f46f2e94e7b8ef43d0b50d90f
[reactos.git] / reactos / ntoskrnl / ps / apphelp.c
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, &IoStatusBlock,
186 &FileBasic, sizeof(FileBasic), FileBasicInformation);
187 if (NT_SUCCESS(Status))
188 {
189 Status = ZwQueryInformationFile(ImageHandle, &IoStatusBlock,
190 &FileStandard, sizeof(FileStandard), FileStandardInformation);
191 if (NT_SUCCESS(Status))
192 {
193 Entry->Persistent.DateTime = FileBasic.LastWriteTime;
194 Entry->Persistent.FileSize = FileStandard.EndOfFile;
195 }
196 }
197 return Status;
198 }
199
200 RTL_GENERIC_COMPARE_RESULTS
201 NTAPI
202 ApphelpShimCacheCompareRoutine(
203 _In_ PRTL_AVL_TABLE Table,
204 _In_ PVOID FirstStruct,
205 _In_ PVOID SecondStruct)
206 {
207 LONG lResult = RtlCompareUnicodeString(
208 &((PSHIM_CACHE_ENTRY)FirstStruct)->Persistent.ImageName,
209 &((PSHIM_CACHE_ENTRY)SecondStruct)->Persistent.ImageName, TRUE);
210
211 if (lResult < 0)
212 return GenericLessThan;
213 else if (lResult == 0)
214 return GenericEqual;
215 return GenericGreaterThan;
216 }
217
218 PVOID
219 NTAPI
220 ApphelpShimCacheAllocateRoutine(
221 _In_ PRTL_AVL_TABLE Table,
222 _In_ CLONG ByteSize)
223 {
224 return ApphelpAlloc(ByteSize);
225 }
226
227 VOID
228 NTAPI
229 ApphelpShimCacheFreeRoutine(
230 _In_ PRTL_AVL_TABLE Table,
231 _In_ PVOID Buffer)
232 {
233 ApphelpFree(Buffer);
234 }
235
236 NTSTATUS
237 ApphelpCacheParse(
238 _In_reads_(DataLength) PUCHAR Data,
239 _In_ ULONG DataLength)
240 {
241 PSHIM_PERSISTENT_CACHE_HEADER Header = (PSHIM_PERSISTENT_CACHE_HEADER)Data;
242
243 if (DataLength < CACHE_HEADER_SIZE_NT_52)
244 {
245 DPRINT1("SHIMS: ApphelpCacheParse not enough data for a minimal header (0x%x)\n", DataLength);
246 return STATUS_INVALID_PARAMETER;
247 }
248 if (Header->Magic == SHIM_CACHE_MAGIC)
249 {
250 ULONG Cur;
251 ULONG NumEntries = Header->NumEntries;
252 DPRINT1("SHIMS: ApphelpCacheParse walking %d entries\n", NumEntries);
253 for (Cur = 0; Cur < NumEntries; ++Cur)
254 {
255 UNICODE_STRING String;
256 SHIM_CACHE_ENTRY Entry = EMPTY_SHIM_ENTRY;
257 PSHIM_CACHE_ENTRY Result;
258 PSHIM_PERSISTENT_CACHE_ENTRY pPersistent =
259 (PSHIM_PERSISTENT_CACHE_ENTRY)(Data + SHIM_CACHE_HEADER_SIZE +
260 (Cur * SHIM_PERSISTENT_CACHE_ENTRY_SIZE));
261 /* The entry in the Persitent storage is not really a UNICODE_STRING,
262 so we have to convert the offset into a real pointer before using it. */
263 String.Length = pPersistent->ImageName.Length;
264 String.MaximumLength = pPersistent->ImageName.MaximumLength;
265 String.Buffer = (PWCHAR)((ULONG_PTR)pPersistent->ImageName.Buffer + Data);
266
267 /* Now we copy all data to a local buffer, that can be safely duplicated by RtlInsert */
268 Entry.Persistent = *pPersistent;
269 ApphelpDuplicateUnicodeString(&Entry.Persistent.ImageName, &String);
270 Result = RtlInsertElementGenericTableAvl(&ApphelpShimCache, &Entry, sizeof(Entry), NULL);
271 if (!Result)
272 {
273 DPRINT1("SHIMS: ApphelpCacheParse insert failed\n");
274 ApphelpFreeUnicodeString(&Entry.Persistent.ImageName);
275 return STATUS_INVALID_PARAMETER;
276 }
277 InsertTailList(&ApphelpShimCacheAge, &Result->List);
278 }
279 return STATUS_SUCCESS;
280 }
281 DPRINT1("SHIMS: ApphelpCacheParse found invalid magic (0x%x)\n", Header->Magic);
282 return STATUS_INVALID_PARAMETER;
283 }
284
285 BOOLEAN
286 ApphelpCacheRead(VOID)
287 {
288 HANDLE KeyHandle;
289 NTSTATUS Status;
290 KEY_VALUE_PARTIAL_INFORMATION KeyValueObject;
291 PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation = &KeyValueObject;
292 ULONG KeyInfoSize, ResultSize;
293
294 Status = ZwOpenKey(&KeyHandle, KEY_QUERY_VALUE, &AppCompatKeyAttributes);
295
296 if (!NT_SUCCESS(Status))
297 {
298 DPRINT1("SHIMS: ApphelpCacheRead could not even open Session Manager\\AppCompatCache (0x%x)\n", Status);
299 return FALSE;
300 }
301
302 Status = ZwQueryValueKey(KeyHandle, &AppCompatCacheValue,
303 KeyValuePartialInformation, KeyValueInformation,
304 sizeof(KeyValueObject), &ResultSize);
305
306 if (Status == STATUS_BUFFER_OVERFLOW)
307 {
308 KeyInfoSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + KeyValueInformation->DataLength;
309 KeyValueInformation = ApphelpAlloc(KeyInfoSize);
310 if (KeyValueInformation != NULL)
311 {
312 Status = ZwQueryValueKey(KeyHandle, &AppCompatCacheValue,
313 KeyValuePartialInformation, KeyValueInformation,
314 KeyInfoSize, &ResultSize);
315 }
316 }
317
318 if (NT_SUCCESS(Status) && KeyValueInformation->Type == REG_BINARY)
319 {
320 Status = ApphelpCacheParse(KeyValueInformation->Data,
321 KeyValueInformation->DataLength);
322 }
323 else
324 {
325 DPRINT1("SHIMS: ApphelpCacheRead not loaded from registry (0x%x)\n", Status);
326 }
327
328 if (KeyValueInformation != &KeyValueObject && KeyValueInformation != NULL)
329 ApphelpFree(KeyValueInformation);
330
331 ZwClose(KeyHandle);
332 return NT_SUCCESS(Status);
333 }
334
335 BOOLEAN
336 ApphelpCacheWrite(VOID)
337 {
338 ULONG Length = SHIM_CACHE_HEADER_SIZE;
339 ULONG NumEntries = 0;
340 PLIST_ENTRY ListEntry;
341 PUCHAR Buffer, BufferNamePos;
342 PSHIM_PERSISTENT_CACHE_HEADER Header;
343 PSHIM_PERSISTENT_CACHE_ENTRY WriteEntry;
344 HANDLE KeyHandle;
345 NTSTATUS Status;
346
347 /* First we have to calculate the required size. */
348 ApphelpCacheAcquireLock();
349 ListEntry = ApphelpShimCacheAge.Flink;
350 while (ListEntry != &ApphelpShimCacheAge)
351 {
352 PSHIM_CACHE_ENTRY Entry = CONTAINING_RECORD(ListEntry, SHIM_CACHE_ENTRY, List);
353 Length += SHIM_PERSISTENT_CACHE_ENTRY_SIZE;
354 Length += Entry->Persistent.ImageName.MaximumLength;
355 ++NumEntries;
356 ListEntry = ListEntry->Flink;
357 }
358 DPRINT1("SHIMS: ApphelpCacheWrite, %d Entries, total size: %d\n", NumEntries, Length);
359 Length = ROUND_UP(Length, sizeof(ULONGLONG));
360 DPRINT1("SHIMS: ApphelpCacheWrite, Rounded to: %d\n", Length);
361
362 /* Now we allocate and prepare some helpers */
363 Buffer = ApphelpAlloc(Length);
364 BufferNamePos = Buffer + Length;
365 Header = (PSHIM_PERSISTENT_CACHE_HEADER)Buffer;
366 WriteEntry = (PSHIM_PERSISTENT_CACHE_ENTRY)(Buffer + SHIM_CACHE_HEADER_SIZE);
367
368 Header->Magic = SHIM_CACHE_MAGIC;
369 Header->NumEntries = NumEntries;
370
371 ListEntry = ApphelpShimCacheAge.Flink;
372 while (ListEntry != &ApphelpShimCacheAge)
373 {
374 PSHIM_CACHE_ENTRY Entry = CONTAINING_RECORD(ListEntry, SHIM_CACHE_ENTRY, List);
375 USHORT ImageNameLen = Entry->Persistent.ImageName.MaximumLength;
376 /* Copy the Persistent structure over */
377 *WriteEntry = Entry->Persistent;
378 BufferNamePos -= ImageNameLen;
379 /* Copy the image name over */
380 RtlCopyMemory(BufferNamePos, Entry->Persistent.ImageName.Buffer, ImageNameLen);
381 /* Fix the Persistent structure, so that Buffer is once again an offset */
382 WriteEntry->ImageName.Buffer = (PWCH)(BufferNamePos - Buffer);
383
384 ++WriteEntry;
385 ListEntry = ListEntry->Flink;
386 }
387 ApphelpCacheReleaseLock();
388
389 Status = ZwOpenKey(&KeyHandle, KEY_SET_VALUE, &AppCompatKeyAttributes);
390 if (NT_SUCCESS(Status))
391 {
392 Status = ZwSetValueKey(KeyHandle, &AppCompatCacheValue, 0, REG_BINARY, Buffer, Length);
393 ZwClose(KeyHandle);
394 }
395 else
396 {
397 DPRINT1("SHIMS: ApphelpCacheWrite could not even open Session Manager\\AppCompatCache (0x%x)\n", Status);
398 }
399
400 ApphelpFree(Buffer);
401 return NT_SUCCESS(Status);
402 }
403
404
405 NTSTATUS
406 NTAPI
407 INIT_FUNCTION
408 ApphelpCacheInitialize(VOID)
409 {
410 DPRINT1("SHIMS: ApphelpCacheInitialize\n");
411 /* If we are booting in safemode we do not want to use the apphelp cache */
412 if (InitSafeBootMode)
413 {
414 DPRINT1("SHIMS: Safe mode detected, disabling cache.\n");
415 ApphelpCacheEnabled = FALSE;
416 }
417 else
418 {
419 ExInitializeResourceLite(&ApphelpCacheLock);
420 RtlInitializeGenericTableAvl(&ApphelpShimCache,
421 ApphelpShimCacheCompareRoutine,
422 ApphelpShimCacheAllocateRoutine,
423 ApphelpShimCacheFreeRoutine,
424 NULL);
425 InitializeListHead(&ApphelpShimCacheAge);
426 ApphelpCacheEnabled = ApphelpCacheRead();
427 }
428 DPRINT1("SHIMS: ApphelpCacheInitialize: %d\n", ApphelpCacheEnabled);
429 return STATUS_SUCCESS;
430 }
431
432 VOID
433 NTAPI
434 ApphelpCacheShutdown(VOID)
435 {
436 if (ApphelpCacheEnabled)
437 {
438 ApphelpCacheWrite();
439 }
440 }
441
442 NTSTATUS
443 ApphelpValidateData(
444 _In_opt_ PAPPHELP_CACHE_SERVICE_LOOKUP ServiceData,
445 _Out_ PUNICODE_STRING ImageName,
446 _Out_ PHANDLE ImageHandle)
447 {
448 NTSTATUS Status = STATUS_INVALID_PARAMETER;
449
450 if (ServiceData)
451 {
452 UNICODE_STRING LocalImageName;
453 _SEH2_TRY
454 {
455 ProbeForRead(ServiceData, sizeof(APPHELP_CACHE_SERVICE_LOOKUP), sizeof(ULONG));
456 LocalImageName = ServiceData->ImageName;
457 *ImageHandle = ServiceData->ImageHandle;
458 if (LocalImageName.Length && LocalImageName.Buffer)
459 {
460 ProbeForRead(LocalImageName.Buffer, LocalImageName.Length * sizeof(WCHAR), 1);
461 ApphelpDuplicateUnicodeString(ImageName, &LocalImageName);
462 Status = STATUS_SUCCESS;
463 }
464 }
465 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
466 {
467 _SEH2_YIELD(return _SEH2_GetExceptionCode());
468 }
469 _SEH2_END;
470 }
471 if (!NT_SUCCESS(Status))
472 {
473 DPRINT1("SHIMS: ApphelpValidateData: invalid data passed\n");
474 }
475 return Status;
476 }
477
478 NTSTATUS
479 ApphelpCacheRemoveEntryNolock(
480 _In_ PSHIM_CACHE_ENTRY Entry)
481 {
482 if (Entry)
483 {
484 PWSTR Buffer = Entry->Persistent.ImageName.Buffer;
485 RemoveEntryList(&Entry->List);
486 if (RtlDeleteElementGenericTableAvl(&ApphelpShimCache, Entry))
487 ApphelpFree(Buffer);
488 return STATUS_SUCCESS;
489 }
490 return STATUS_NOT_FOUND;
491 }
492
493 NTSTATUS
494 ApphelpCacheLookupEntry(
495 _In_ PUNICODE_STRING ImageName,
496 _In_ HANDLE ImageHandle)
497 {
498 NTSTATUS Status = STATUS_NOT_FOUND;
499
500 if (ApphelpCacheTryAcquireLock())
501 {
502 SHIM_CACHE_ENTRY Lookup = EMPTY_SHIM_ENTRY;
503 PSHIM_CACHE_ENTRY Entry;
504 Lookup.Persistent.ImageName = *ImageName;
505 Entry = RtlLookupElementGenericTableAvl(&ApphelpShimCache, &Lookup);
506 if (Entry)
507 {
508 DPRINT1("SHIMS: ApphelpCacheLookupEntry: found %wZ\n", ImageName);
509 if (ImageHandle == INVALID_HANDLE_VALUE)
510 {
511 DPRINT1("SHIMS: ApphelpCacheLookupEntry: ok\n");
512 /* just return if we know it, do not query file info */
513 Status = STATUS_SUCCESS;
514 }
515 else if (NT_SUCCESS(ApphelpCacheQueryInfo(ImageHandle, &Lookup)) &&
516 Lookup.Persistent.DateTime.QuadPart == Entry->Persistent.DateTime.QuadPart &&
517 Lookup.Persistent.FileSize.QuadPart == Entry->Persistent.FileSize.QuadPart)
518 {
519 DPRINT1("SHIMS: ApphelpCacheLookupEntry: found & validated\n");
520 Status = STATUS_SUCCESS;
521 /* move it to the front to keep it alive */
522 RemoveEntryList(&Entry->List);
523 InsertHeadList(&ApphelpShimCacheAge, &Entry->List);
524 }
525 else
526 {
527 DPRINT1("SHIMS: ApphelpCacheLookupEntry: file info mismatch\n");
528 /* Could not read file info, or it did not match, drop it from the cache */
529 ApphelpCacheRemoveEntryNolock(Entry);
530 }
531 }
532 else
533 {
534 DPRINT1("SHIMS: ApphelpCacheLookupEntry: could not find %wZ\n", ImageName);
535 }
536 ApphelpCacheReleaseLock();
537 }
538 return Status;
539 }
540
541 NTSTATUS
542 ApphelpCacheRemoveEntry(
543 _In_ PUNICODE_STRING ImageName)
544 {
545 PSHIM_CACHE_ENTRY Entry;
546 NTSTATUS Status;
547
548 ApphelpCacheAcquireLock();
549 Entry = RtlLookupElementGenericTableAvl(&ApphelpShimCache, ImageName);
550 Status = ApphelpCacheRemoveEntryNolock(Entry);
551 ApphelpCacheReleaseLock();
552 return Status;
553 }
554
555 /* Validate that we are either called from r0, or from a service-like context */
556 NTSTATUS
557 ApphelpCacheAccessCheck(VOID)
558 {
559 if (ExGetPreviousMode() != KernelMode)
560 {
561 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, UserMode))
562 {
563 DPRINT1("SHIMS: ApphelpCacheAccessCheck failed\n");
564 return STATUS_ACCESS_DENIED;
565 }
566 }
567 return STATUS_SUCCESS;
568 }
569
570 NTSTATUS
571 ApphelpCacheUpdateEntry(
572 _In_ PUNICODE_STRING ImageName,
573 _In_ HANDLE ImageHandle)
574 {
575 NTSTATUS Status = STATUS_SUCCESS;
576 SHIM_CACHE_ENTRY Entry = EMPTY_SHIM_ENTRY;
577 PSHIM_CACHE_ENTRY Lookup;
578 PVOID NodeOrParent;
579 TABLE_SEARCH_RESULT SearchResult;
580
581 ApphelpCacheAcquireLock();
582
583 /* If we got a file handle, query it for info */
584 if (ImageHandle != INVALID_HANDLE_VALUE)
585 {
586 Status = ApphelpCacheQueryInfo(ImageHandle, &Entry);
587 }
588
589 if (NT_SUCCESS(Status))
590 {
591 /* Use ImageName for the lookup, don't actually duplicate it */
592 Entry.Persistent.ImageName = *ImageName;
593 Lookup = RtlLookupElementGenericTableFullAvl(&ApphelpShimCache, &Entry,
594 &NodeOrParent, &SearchResult);
595 if (Lookup)
596 {
597 DPRINT1("SHIMS: ApphelpCacheUpdateEntry: Entry already exists, reusing it\n");
598 /* Unlink the found item, so we can put it back at the front,
599 and copy the earlier obtained file info*/
600 RemoveEntryList(&Lookup->List);
601 Lookup->Persistent.DateTime = Entry.Persistent.DateTime;
602 Lookup->Persistent.FileSize = Entry.Persistent.FileSize;
603 }
604 else
605 {
606 DPRINT1("SHIMS: ApphelpCacheUpdateEntry: Inserting new Entry\n");
607 /* Insert a new entry, with its own copy of the ImageName */
608 ApphelpDuplicateUnicodeString(&Entry.Persistent.ImageName, ImageName);
609 Lookup = RtlInsertElementGenericTableFullAvl(&ApphelpShimCache,
610 &Entry, sizeof(Entry), 0, NodeOrParent, SearchResult);
611 if (!Lookup)
612 {
613 ApphelpFreeUnicodeString(&Entry.Persistent.ImageName);
614 Status = STATUS_NO_MEMORY;
615 }
616 }
617 if (Lookup)
618 {
619 /* Either we re-used an existing item, or we inserted a new one, keep it alive */
620 InsertHeadList(&ApphelpShimCacheAge, &Lookup->List);
621 if (RtlNumberGenericTableElementsAvl(&ApphelpShimCache) > MAX_SHIM_ENTRIES)
622 {
623 PSHIM_CACHE_ENTRY Remove;
624 DPRINT1("SHIMS: ApphelpCacheUpdateEntry: Cache growing too big, dropping oldest item\n");
625 Remove = CONTAINING_RECORD(ApphelpShimCacheAge.Blink, SHIM_CACHE_ENTRY, List);
626 Status = ApphelpCacheRemoveEntryNolock(Remove);
627 }
628 }
629 }
630 ApphelpCacheReleaseLock();
631 return Status;
632 }
633
634 NTSTATUS
635 ApphelpCacheFlush(VOID)
636 {
637 PVOID p;
638
639 DPRINT1("SHIMS: ApphelpCacheFlush\n");
640 ApphelpCacheAcquireLock();
641 while ((p = RtlEnumerateGenericTableAvl(&ApphelpShimCache, TRUE)))
642 {
643 ApphelpCacheRemoveEntryNolock((PSHIM_CACHE_ENTRY)p);
644 }
645 ApphelpCacheReleaseLock();
646 return STATUS_SUCCESS;
647 }
648
649 NTSTATUS
650 ApphelpCacheDump(VOID)
651 {
652 PLIST_ENTRY ListEntry;
653
654 DPRINT1("SHIMS: NtApphelpCacheControl( Dumping entries, newset to oldest )\n");
655 ApphelpCacheAcquireLock();
656 ListEntry = ApphelpShimCacheAge.Flink;
657 while (ListEntry != &ApphelpShimCacheAge)
658 {
659 PSHIM_CACHE_ENTRY Entry = CONTAINING_RECORD(ListEntry, SHIM_CACHE_ENTRY, List);
660 DPRINT1("Entry: %S\n", Entry->Persistent.ImageName.Buffer);
661 DPRINT1("DateTime High: 0x%x, Low: 0x%x\n",
662 Entry->Persistent.DateTime.HighPart, Entry->Persistent.DateTime.LowPart);
663 DPRINT1("FileSize High: 0x%x, Low: 0x%x\n",
664 Entry->Persistent.FileSize.HighPart, Entry->Persistent.FileSize.LowPart);
665 DPRINT1("Flags: 0x%x\n", Entry->CompatFlags);
666 ListEntry = ListEntry->Flink;
667 }
668 ApphelpCacheReleaseLock();
669 return STATUS_SUCCESS;
670 }
671
672 /* PUBLIC FUNCTIONS **********************************************************/
673
674 NTSTATUS
675 NTAPI
676 NtApphelpCacheControl(
677 _In_ APPHELPCACHESERVICECLASS Service,
678 _In_opt_ PAPPHELP_CACHE_SERVICE_LOOKUP ServiceData)
679 {
680 NTSTATUS Status = STATUS_INVALID_PARAMETER;
681 UNICODE_STRING ImageName = { 0 };
682 HANDLE Handle = INVALID_HANDLE_VALUE;
683
684 if (!ApphelpCacheEnabled)
685 {
686 DPRINT1("NtApphelpCacheControl: ApphelpCacheEnabled == 0\n");
687 return Status;
688 }
689 switch (Service)
690 {
691 case ApphelpCacheServiceLookup:
692 DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpCacheServiceLookup )\n");
693 Status = ApphelpValidateData(ServiceData, &ImageName, &Handle);
694 if (NT_SUCCESS(Status))
695 Status = ApphelpCacheLookupEntry(&ImageName, Handle);
696 break;
697 case ApphelpCacheServiceRemove:
698 DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpCacheServiceRemove )\n");
699 Status = ApphelpValidateData(ServiceData, &ImageName, &Handle);
700 if (NT_SUCCESS(Status))
701 Status = ApphelpCacheRemoveEntry(&ImageName);
702 break;
703 case ApphelpCacheServiceUpdate:
704 DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpCacheServiceUpdate )\n");
705 Status = ApphelpCacheAccessCheck();
706 if (NT_SUCCESS(Status))
707 {
708 Status = ApphelpValidateData(ServiceData, &ImageName, &Handle);
709 if (NT_SUCCESS(Status))
710 Status = ApphelpCacheUpdateEntry(&ImageName, Handle);
711 }
712 break;
713 case ApphelpCacheServiceFlush:
714 Status = ApphelpCacheFlush();
715 break;
716 case ApphelpCacheServiceDump:
717 Status = ApphelpCacheDump();
718 break;
719 case ApphelpDBGReadRegistry:
720 DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpDBGReadRegistry ): flushing cache.\n");
721 ApphelpCacheFlush();
722 DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpDBGReadRegistry ): reading cache.\n");
723 Status = ApphelpCacheRead() ? STATUS_SUCCESS : STATUS_NOT_FOUND;
724 break;
725 case ApphelpDBGWriteRegistry:
726 DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpDBGWriteRegistry ): writing cache.\n");
727 Status = ApphelpCacheWrite() ? STATUS_SUCCESS : STATUS_NOT_FOUND;
728 break;
729 default:
730 DPRINT1("SHIMS: NtApphelpCacheControl( Invalid service requested )\n");
731 break;
732 }
733 if (ImageName.Buffer)
734 {
735 ApphelpFreeUnicodeString(&ImageName);
736 }
737 return Status;
738 }
739