[NTOS:PS]

- Implement NtApphelpCacheControl. Patch by Mark Jansen
CORE-9914 #resolve

svn path=/trunk/; revision=69022
This commit is contained in:
Thomas Faber 2015-09-05 11:49:54 +00:00
parent f0988cafd6
commit 2565dcba26
10 changed files with 1133 additions and 11 deletions

View file

@ -1280,6 +1280,9 @@ HKLM,"SYSTEM\CurrentControlSet\Control\Session Manager","GlobalFlag", 0x00010003
HKLM,"SYSTEM\CurrentControlSet\Control\Session Manager","ObjectDirectories",0x00010000, \
"\Windows", \
"\RPC Control"
# This is an empty app compat cache
HKLM,"SYSTEM\CurrentControlSet\Control\Session Manager\AppCompatCache","AppCompatCache", 0x00000001, \
fe,0f,dc,ba,00,00,00,00
; DOS devices
HKLM,"SYSTEM\CurrentControlSet\Control\Session Manager\DOS Devices","AUX",0x00000000,"\DosDevices\COM1"

View file

@ -412,6 +412,18 @@ BOOLEAN
NTAPI
PspIsProcessExiting(IN PEPROCESS Process);
//
// Apphelp functions
//
NTSTATUS
NTAPI
INIT_FUNCTION
ApphelpCacheInitialize(VOID);
VOID
NTAPI
ApphelpCacheShutdown(VOID);
//
// Global data inside the Process Manager
//

View file

@ -531,6 +531,9 @@ IoInitSystem(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
/* Initialize PnP manager */
IopInitializePlugPlayServices();
/* Initialize SHIM engine */
ApphelpCacheInitialize();
/* Initialize WMI */
WmiInitialize();

View file

@ -248,6 +248,7 @@ list(APPEND SOURCE
${REACTOS_SOURCE_DIR}/ntoskrnl/ps/job.c
${REACTOS_SOURCE_DIR}/ntoskrnl/ps/kill.c
${REACTOS_SOURCE_DIR}/ntoskrnl/ps/process.c
${REACTOS_SOURCE_DIR}/ntoskrnl/ps/apphelp.c
${REACTOS_SOURCE_DIR}/ntoskrnl/ps/psmgr.c
${REACTOS_SOURCE_DIR}/ntoskrnl/ps/psnotify.c
${REACTOS_SOURCE_DIR}/ntoskrnl/ps/query.c

View file

@ -267,6 +267,9 @@ PopGracefulShutdown(IN PVOID Context)
DPRINT("HAL shutting down\n");
HalEndOfBoot();
/* Shut down the Shim cache if enabled */
ApphelpCacheShutdown();
/* In this step, the I/O manager does first-chance shutdown notification */
DPRINT("I/O manager shutting down in phase 0\n");
IoShutdownSystem(0);

View file

@ -0,0 +1,739 @@
/*
* PROJECT: ReactOS Kernel
* LICENSE: BSD - See COPYING.ARM in the top level directory
* FILE: ntoskrnl/ps/apphelp.c
* PURPOSE: SHIM engine caching.
* This caching speeds up checks for the apphelp compatibility layer.
* PROGRAMMERS: Mark Jansen
*/
/*
Useful references:
https://github.com/mandiant/ShimCacheParser/blob/master/ShimCacheParser.py
http://technet.microsoft.com/en-us/library/dd837644(v=ws.10).aspx
http://msdn.microsoft.com/en-us/library/bb432182(v=vs.85).aspx
http://www.alex-ionescu.com/?p=43
http://recxltd.blogspot.nl/2012/04/windows-appcompat-research-notes-part-1.html
http://journeyintoir.blogspot.ch/2013/12/revealing-recentfilecachebcf-file.html
https://dl.mandiant.com/EE/library/Whitepaper_ShimCacheParser.pdf
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS *******************************************************************/
static BOOLEAN ApphelpCacheEnabled = FALSE;
static ERESOURCE ApphelpCacheLock;
static RTL_AVL_TABLE ApphelpShimCache;
static LIST_ENTRY ApphelpShimCacheAge;
extern ULONG InitSafeBootMode;
static UNICODE_STRING AppCompatCacheKey = RTL_CONSTANT_STRING(L"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager\\AppCompatCache");
static OBJECT_ATTRIBUTES AppCompatKeyAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(&AppCompatCacheKey, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE);
static UNICODE_STRING AppCompatCacheValue = RTL_CONSTANT_STRING(L"AppCompatCache");
#define EMPTY_SHIM_ENTRY { { 0 }, { { 0 } }, 0 }
#define MAX_SHIM_ENTRIES 0x200
#define TAG_SHIM 'MIHS'
#ifndef INVALID_HANDLE_VALUE
#define INVALID_HANDLE_VALUE (HANDLE)(-1)
#endif
#include <pshpack1.h>
typedef struct SHIM_PERSISTENT_CACHE_HEADER_52
{
ULONG Magic;
ULONG NumEntries;
} SHIM_PERSISTENT_CACHE_HEADER_52, *PSHIM_PERSISTENT_CACHE_HEADER_52;
/* The data that is present in the registry (Win2k3 version) */
typedef struct SHIM_PERSISTENT_CACHE_ENTRY_52
{
UNICODE_STRING ImageName;
LARGE_INTEGER DateTime;
LARGE_INTEGER FileSize;
} SHIM_PERSISTENT_CACHE_ENTRY_52, *PSHIM_PERSISTENT_CACHE_ENTRY_52;
#include <poppack.h>
#define CACHE_MAGIC_NT_52 0xbadc0ffe
#define CACHE_HEADER_SIZE_NT_52 0x8
#define NT52_PERSISTENT_ENTRY_SIZE32 0x18
#define NT52_PERSISTENT_ENTRY_SIZE64 0x20
//#define CACHE_MAGIC_NT_61 0xbadc0fee
//#define CACHE_HEADER_SIZE_NT_61 0x80
//#define NT61_PERSISTENT_ENTRY_SIZE32 0x20
//#define NT61_PERSISTENT_ENTRY_SIZE64 0x30
#define SHIM_CACHE_MAGIC CACHE_MAGIC_NT_52
#define SHIM_CACHE_HEADER_SIZE CACHE_HEADER_SIZE_NT_52
#ifdef _WIN64
#define SHIM_PERSISTENT_CACHE_ENTRY_SIZE NT52_PERSISTENT_ENTRY_SIZE64
#else
#define SHIM_PERSISTENT_CACHE_ENTRY_SIZE NT52_PERSISTENT_ENTRY_SIZE32
#endif
#define SHIM_PERSISTENT_CACHE_HEADER SHIM_PERSISTENT_CACHE_HEADER_52
#define PSHIM_PERSISTENT_CACHE_HEADER PSHIM_PERSISTENT_CACHE_HEADER_52
#define SHIM_PERSISTENT_CACHE_ENTRY SHIM_PERSISTENT_CACHE_ENTRY_52
#define PSHIM_PERSISTENT_CACHE_ENTRY PSHIM_PERSISTENT_CACHE_ENTRY_52
C_ASSERT(sizeof(SHIM_PERSISTENT_CACHE_ENTRY) == SHIM_PERSISTENT_CACHE_ENTRY_SIZE);
C_ASSERT(sizeof(SHIM_PERSISTENT_CACHE_HEADER) == SHIM_CACHE_HEADER_SIZE);
/* The struct we keep in memory */
typedef struct SHIM_CACHE_ENTRY
{
LIST_ENTRY List;
SHIM_PERSISTENT_CACHE_ENTRY Persistent;
ULONG CompatFlags;
} SHIM_CACHE_ENTRY, *PSHIM_CACHE_ENTRY;
/* PRIVATE FUNCTIONS *********************************************************/
PVOID
ApphelpAlloc(
_In_ ULONG ByteSize)
{
return ExAllocatePoolWithTag(PagedPool, ByteSize, TAG_SHIM);
}
VOID
ApphelpFree(
_In_ PVOID Data)
{
ExFreePoolWithTag(Data, TAG_SHIM);
}
VOID
ApphelpCacheAcquireLock(VOID)
{
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&ApphelpCacheLock, TRUE);
}
BOOLEAN
ApphelpCacheTryAcquireLock(VOID)
{
KeEnterCriticalRegion();
if (!ExTryToAcquireResourceExclusiveLite(&ApphelpCacheLock))
{
KeLeaveCriticalRegion();
return FALSE;
}
return TRUE;
}
VOID
ApphelpCacheReleaseLock(VOID)
{
ExReleaseResourceLite(&ApphelpCacheLock);
KeLeaveCriticalRegion();
}
VOID
ApphelpDuplicateUnicodeString(
_Out_ PUNICODE_STRING Destination,
_In_ PCUNICODE_STRING Source)
{
Destination->Length = Source->Length;
if (Destination->Length)
{
Destination->MaximumLength = Destination->Length + sizeof(WCHAR);
Destination->Buffer = ApphelpAlloc(Destination->MaximumLength);
RtlCopyMemory(Destination->Buffer, Source->Buffer, Destination->Length);
Destination->Buffer[Destination->Length / sizeof(WCHAR)] = UNICODE_NULL;
}
else
{
Destination->MaximumLength = 0;
Destination->Buffer = NULL;
}
}
VOID
ApphelpFreeUnicodeString(
_Inout_ PUNICODE_STRING String)
{
if (String->Buffer)
{
ApphelpFree(String->Buffer);
}
String->Length = 0;
String->MaximumLength = 0;
String->Buffer = NULL;
}
/* Query file info from a handle, storing it in Entry */
NTSTATUS
ApphelpCacheQueryInfo(
_In_ HANDLE ImageHandle,
_Out_ PSHIM_CACHE_ENTRY Entry)
{
IO_STATUS_BLOCK IoStatusBlock;
FILE_BASIC_INFORMATION FileBasic;
FILE_STANDARD_INFORMATION FileStandard;
NTSTATUS Status;
Status = ZwQueryInformationFile(ImageHandle, &IoStatusBlock,
&FileBasic, sizeof(FileBasic), FileBasicInformation);
if (NT_SUCCESS(Status))
{
Status = ZwQueryInformationFile(ImageHandle, &IoStatusBlock,
&FileStandard, sizeof(FileStandard), FileStandardInformation);
if (NT_SUCCESS(Status))
{
Entry->Persistent.DateTime = FileBasic.LastWriteTime;
Entry->Persistent.FileSize = FileStandard.EndOfFile;
}
}
return Status;
}
RTL_GENERIC_COMPARE_RESULTS
NTAPI
ApphelpShimCacheCompareRoutine(
_In_ PRTL_AVL_TABLE Table,
_In_ PVOID FirstStruct,
_In_ PVOID SecondStruct)
{
LONG lResult = RtlCompareUnicodeString(
&((PSHIM_CACHE_ENTRY)FirstStruct)->Persistent.ImageName,
&((PSHIM_CACHE_ENTRY)SecondStruct)->Persistent.ImageName, TRUE);
if (lResult < 0)
return GenericLessThan;
else if (lResult == 0)
return GenericEqual;
return GenericGreaterThan;
}
PVOID
NTAPI
ApphelpShimCacheAllocateRoutine(
_In_ PRTL_AVL_TABLE Table,
_In_ CLONG ByteSize)
{
return ApphelpAlloc(ByteSize);
}
VOID
NTAPI
ApphelpShimCacheFreeRoutine(
_In_ PRTL_AVL_TABLE Table,
_In_ PVOID Buffer)
{
ApphelpFree(Buffer);
}
NTSTATUS
ApphelpCacheParse(
_In_reads_(DataLength) PUCHAR Data,
_In_ ULONG DataLength)
{
PSHIM_PERSISTENT_CACHE_HEADER Header = (PSHIM_PERSISTENT_CACHE_HEADER)Data;
if (DataLength < CACHE_HEADER_SIZE_NT_52)
{
DPRINT1("SHIMS: ApphelpCacheParse not enough data for a minimal header (0x%x)\n", DataLength);
return STATUS_INVALID_PARAMETER;
}
if (Header->Magic == SHIM_CACHE_MAGIC)
{
ULONG Cur;
ULONG NumEntries = Header->NumEntries;
DPRINT1("SHIMS: ApphelpCacheParse walking %d entries\n", NumEntries);
for (Cur = 0; Cur < NumEntries; ++Cur)
{
UNICODE_STRING String;
SHIM_CACHE_ENTRY Entry = EMPTY_SHIM_ENTRY;
PSHIM_CACHE_ENTRY Result;
PSHIM_PERSISTENT_CACHE_ENTRY pPersistent =
(PSHIM_PERSISTENT_CACHE_ENTRY)(Data + SHIM_CACHE_HEADER_SIZE +
(Cur * SHIM_PERSISTENT_CACHE_ENTRY_SIZE));
/* The entry in the Persitent storage is not really a UNICODE_STRING,
so we have to convert the offset into a real pointer before using it. */
String.Length = pPersistent->ImageName.Length;
String.MaximumLength = pPersistent->ImageName.MaximumLength;
String.Buffer = (PWCHAR)((ULONG_PTR)pPersistent->ImageName.Buffer + Data);
/* Now we copy all data to a local buffer, that can be safely duplicated by RtlInsert */
Entry.Persistent = *pPersistent;
ApphelpDuplicateUnicodeString(&Entry.Persistent.ImageName, &String);
Result = RtlInsertElementGenericTableAvl(&ApphelpShimCache, &Entry, sizeof(Entry), NULL);
if (!Result)
{
DPRINT1("SHIMS: ApphelpCacheParse insert failed\n");
ApphelpFreeUnicodeString(&Entry.Persistent.ImageName);
return STATUS_INVALID_PARAMETER;
}
InsertTailList(&ApphelpShimCacheAge, &Result->List);
}
return STATUS_SUCCESS;
}
DPRINT1("SHIMS: ApphelpCacheParse found invalid magic (0x%x)\n", Header->Magic);
return STATUS_INVALID_PARAMETER;
}
BOOLEAN
ApphelpCacheRead(VOID)
{
HANDLE KeyHandle;
NTSTATUS Status;
KEY_VALUE_PARTIAL_INFORMATION KeyValueObject;
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation = &KeyValueObject;
ULONG KeyInfoSize, ResultSize;
Status = ZwOpenKey(&KeyHandle, KEY_QUERY_VALUE, &AppCompatKeyAttributes);
if (!NT_SUCCESS(Status))
{
DPRINT1("SHIMS: ApphelpCacheRead could not even open Session Manager\\AppCompatCache (0x%x)\n", Status);
return FALSE;
}
Status = ZwQueryValueKey(KeyHandle, &AppCompatCacheValue,
KeyValuePartialInformation, KeyValueInformation,
sizeof(KeyValueObject), &ResultSize);
if (Status == STATUS_BUFFER_OVERFLOW)
{
KeyInfoSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + KeyValueInformation->DataLength;
KeyValueInformation = ApphelpAlloc(KeyInfoSize);
if (KeyValueInformation != NULL)
{
Status = ZwQueryValueKey(KeyHandle, &AppCompatCacheValue,
KeyValuePartialInformation, KeyValueInformation,
KeyInfoSize, &ResultSize);
}
}
if (NT_SUCCESS(Status) && KeyValueInformation->Type == REG_BINARY)
{
Status = ApphelpCacheParse(KeyValueInformation->Data,
KeyValueInformation->DataLength);
}
else
{
DPRINT1("SHIMS: ApphelpCacheRead not loaded from registry (0x%x)\n", Status);
}
if (KeyValueInformation != &KeyValueObject && KeyValueInformation != NULL)
ApphelpFree(KeyValueInformation);
ZwClose(KeyHandle);
return NT_SUCCESS(Status);
}
BOOLEAN
ApphelpCacheWrite(VOID)
{
ULONG Length = SHIM_CACHE_HEADER_SIZE;
ULONG NumEntries = 0;
PLIST_ENTRY ListEntry;
PUCHAR Buffer, BufferNamePos;
PSHIM_PERSISTENT_CACHE_HEADER Header;
PSHIM_PERSISTENT_CACHE_ENTRY WriteEntry;
HANDLE KeyHandle;
NTSTATUS Status;
/* First we have to calculate the required size. */
ApphelpCacheAcquireLock();
ListEntry = ApphelpShimCacheAge.Flink;
while (ListEntry != &ApphelpShimCacheAge)
{
PSHIM_CACHE_ENTRY Entry = CONTAINING_RECORD(ListEntry, SHIM_CACHE_ENTRY, List);
Length += SHIM_PERSISTENT_CACHE_ENTRY_SIZE;
Length += Entry->Persistent.ImageName.MaximumLength;
++NumEntries;
ListEntry = ListEntry->Flink;
}
DPRINT1("SHIMS: ApphelpCacheWrite, %d Entries, total size: %d\n", NumEntries, Length);
Length = ROUND_UP(Length, sizeof(ULONGLONG));
DPRINT1("SHIMS: ApphelpCacheWrite, Rounded to: %d\n", Length);
/* Now we allocate and prepare some helpers */
Buffer = ApphelpAlloc(Length);
BufferNamePos = Buffer + Length;
Header = (PSHIM_PERSISTENT_CACHE_HEADER)Buffer;
WriteEntry = (PSHIM_PERSISTENT_CACHE_ENTRY)(Buffer + SHIM_CACHE_HEADER_SIZE);
Header->Magic = SHIM_CACHE_MAGIC;
Header->NumEntries = NumEntries;
ListEntry = ApphelpShimCacheAge.Flink;
while (ListEntry != &ApphelpShimCacheAge)
{
PSHIM_CACHE_ENTRY Entry = CONTAINING_RECORD(ListEntry, SHIM_CACHE_ENTRY, List);
USHORT ImageNameLen = Entry->Persistent.ImageName.MaximumLength;
/* Copy the Persistent structure over */
*WriteEntry = Entry->Persistent;
BufferNamePos -= ImageNameLen;
/* Copy the image name over */
RtlCopyMemory(BufferNamePos, Entry->Persistent.ImageName.Buffer, ImageNameLen);
/* Fix the Persistent structure, so that Buffer is once again an offset */
WriteEntry->ImageName.Buffer = (PWCH)(BufferNamePos - Buffer);
++WriteEntry;
ListEntry = ListEntry->Flink;
}
ApphelpCacheReleaseLock();
Status = ZwOpenKey(&KeyHandle, KEY_SET_VALUE, &AppCompatKeyAttributes);
if (NT_SUCCESS(Status))
{
Status = ZwSetValueKey(KeyHandle, &AppCompatCacheValue, 0, REG_BINARY, Buffer, Length);
ZwClose(KeyHandle);
}
else
{
DPRINT1("SHIMS: ApphelpCacheWrite could not even open Session Manager\\AppCompatCache (0x%x)\n", Status);
}
ApphelpFree(Buffer);
return NT_SUCCESS(Status);
}
NTSTATUS
NTAPI
INIT_FUNCTION
ApphelpCacheInitialize(VOID)
{
DPRINT1("SHIMS: ApphelpCacheInitialize\n");
/* If we are booting in safemode we do not want to use the apphelp cache */
if (InitSafeBootMode)
{
DPRINT1("SHIMS: Safe mode detected, disabling cache.\n");
ApphelpCacheEnabled = FALSE;
}
else
{
ExInitializeResourceLite(&ApphelpCacheLock);
RtlInitializeGenericTableAvl(&ApphelpShimCache,
ApphelpShimCacheCompareRoutine,
ApphelpShimCacheAllocateRoutine,
ApphelpShimCacheFreeRoutine,
NULL);
InitializeListHead(&ApphelpShimCacheAge);
ApphelpCacheEnabled = ApphelpCacheRead();
}
DPRINT1("SHIMS: ApphelpCacheInitialize: %d\n", ApphelpCacheEnabled);
return STATUS_SUCCESS;
}
VOID
NTAPI
ApphelpCacheShutdown(VOID)
{
if (ApphelpCacheEnabled)
{
ApphelpCacheWrite();
}
}
NTSTATUS
ApphelpValidateData(
_In_opt_ PAPPHELP_CACHE_SERVICE_LOOKUP ServiceData,
_Out_ PUNICODE_STRING ImageName,
_Out_ PHANDLE ImageHandle)
{
NTSTATUS Status = STATUS_INVALID_PARAMETER;
if (ServiceData)
{
UNICODE_STRING LocalImageName;
_SEH2_TRY
{
ProbeForRead(ServiceData, sizeof(APPHELP_CACHE_SERVICE_LOOKUP), sizeof(ULONG));
LocalImageName = ServiceData->ImageName;
*ImageHandle = ServiceData->ImageHandle;
if (LocalImageName.Length && LocalImageName.Buffer)
{
ProbeForRead(LocalImageName.Buffer, LocalImageName.Length * sizeof(WCHAR), 1);
ApphelpDuplicateUnicodeString(ImageName, &LocalImageName);
Status = STATUS_SUCCESS;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
if (!NT_SUCCESS(Status))
{
DPRINT1("SHIMS: ApphelpValidateData: invalid data passed\n");
}
return Status;
}
NTSTATUS
ApphelpCacheRemoveEntryNolock(
_In_ PSHIM_CACHE_ENTRY Entry)
{
if (Entry)
{
PWSTR Buffer = Entry->Persistent.ImageName.Buffer;
RemoveEntryList(&Entry->List);
if (RtlDeleteElementGenericTableAvl(&ApphelpShimCache, Entry))
ApphelpFree(Buffer);
return STATUS_SUCCESS;
}
return STATUS_NOT_FOUND;
}
NTSTATUS
ApphelpCacheLookupEntry(
_In_ PUNICODE_STRING ImageName,
_In_ HANDLE ImageHandle)
{
NTSTATUS Status = STATUS_NOT_FOUND;
if (ApphelpCacheTryAcquireLock())
{
SHIM_CACHE_ENTRY Lookup = EMPTY_SHIM_ENTRY;
PSHIM_CACHE_ENTRY Entry;
Lookup.Persistent.ImageName = *ImageName;
Entry = RtlLookupElementGenericTableAvl(&ApphelpShimCache, &Lookup);
if (Entry)
{
DPRINT1("SHIMS: ApphelpCacheLookupEntry: found %wZ\n", ImageName);
if (ImageHandle == INVALID_HANDLE_VALUE)
{
DPRINT1("SHIMS: ApphelpCacheLookupEntry: ok\n");
/* just return if we know it, do not query file info */
Status = STATUS_SUCCESS;
}
else if (NT_SUCCESS(ApphelpCacheQueryInfo(ImageHandle, &Lookup)) &&
Lookup.Persistent.DateTime.QuadPart == Entry->Persistent.DateTime.QuadPart &&
Lookup.Persistent.FileSize.QuadPart == Entry->Persistent.FileSize.QuadPart)
{
DPRINT1("SHIMS: ApphelpCacheLookupEntry: found & validated\n");
Status = STATUS_SUCCESS;
/* move it to the front to keep it alive */
RemoveEntryList(&Entry->List);
InsertHeadList(&ApphelpShimCacheAge, &Entry->List);
}
else
{
DPRINT1("SHIMS: ApphelpCacheLookupEntry: file info mismatch\n");
/* Could not read file info, or it did not match, drop it from the cache */
ApphelpCacheRemoveEntryNolock(Entry);
}
}
else
{
DPRINT1("SHIMS: ApphelpCacheLookupEntry: could not find %wZ\n", ImageName);
}
ApphelpCacheReleaseLock();
}
return Status;
}
NTSTATUS
ApphelpCacheRemoveEntry(
_In_ PUNICODE_STRING ImageName)
{
PSHIM_CACHE_ENTRY Entry;
NTSTATUS Status;
ApphelpCacheAcquireLock();
Entry = RtlLookupElementGenericTableAvl(&ApphelpShimCache, ImageName);
Status = ApphelpCacheRemoveEntryNolock(Entry);
ApphelpCacheReleaseLock();
return Status;
}
/* Validate that we are either called from r0, or from a service-like context */
NTSTATUS
ApphelpCacheAccessCheck(VOID)
{
if (ExGetPreviousMode() != KernelMode)
{
if (!SeSinglePrivilegeCheck(SeTcbPrivilege, UserMode))
{
DPRINT1("SHIMS: ApphelpCacheAccessCheck failed\n");
return STATUS_ACCESS_DENIED;
}
}
return STATUS_SUCCESS;
}
NTSTATUS
ApphelpCacheUpdateEntry(
_In_ PUNICODE_STRING ImageName,
_In_ HANDLE ImageHandle)
{
NTSTATUS Status = STATUS_SUCCESS;
SHIM_CACHE_ENTRY Entry = EMPTY_SHIM_ENTRY;
PSHIM_CACHE_ENTRY Lookup;
PVOID NodeOrParent;
TABLE_SEARCH_RESULT SearchResult;
ApphelpCacheAcquireLock();
/* If we got a file handle, query it for info */
if (ImageHandle != INVALID_HANDLE_VALUE)
{
Status = ApphelpCacheQueryInfo(ImageHandle, &Entry);
}
if (NT_SUCCESS(Status))
{
/* Use ImageName for the lookup, don't actually duplicate it */
Entry.Persistent.ImageName = *ImageName;
Lookup = RtlLookupElementGenericTableFullAvl(&ApphelpShimCache, &Entry,
&NodeOrParent, &SearchResult);
if (Lookup)
{
DPRINT1("SHIMS: ApphelpCacheUpdateEntry: Entry already exists, reusing it\n");
/* Unlink the found item, so we can put it back at the front,
and copy the earlier obtained file info*/
RemoveEntryList(&Lookup->List);
Lookup->Persistent.DateTime = Entry.Persistent.DateTime;
Lookup->Persistent.FileSize = Entry.Persistent.FileSize;
}
else
{
DPRINT1("SHIMS: ApphelpCacheUpdateEntry: Inserting new Entry\n");
/* Insert a new entry, with its own copy of the ImageName */
ApphelpDuplicateUnicodeString(&Entry.Persistent.ImageName, ImageName);
Lookup = RtlInsertElementGenericTableFullAvl(&ApphelpShimCache,
&Entry, sizeof(Entry), 0, NodeOrParent, SearchResult);
if (!Lookup)
{
ApphelpFreeUnicodeString(&Entry.Persistent.ImageName);
Status = STATUS_NO_MEMORY;
}
}
if (Lookup)
{
/* Either we re-used an existing item, or we inserted a new one, keep it alive */
InsertHeadList(&ApphelpShimCacheAge, &Lookup->List);
if (RtlNumberGenericTableElementsAvl(&ApphelpShimCache) > MAX_SHIM_ENTRIES)
{
PSHIM_CACHE_ENTRY Remove;
DPRINT1("SHIMS: ApphelpCacheUpdateEntry: Cache growing too big, dropping oldest item\n");
Remove = CONTAINING_RECORD(ApphelpShimCacheAge.Blink, SHIM_CACHE_ENTRY, List);
Status = ApphelpCacheRemoveEntryNolock(Remove);
}
}
}
ApphelpCacheReleaseLock();
return Status;
}
NTSTATUS
ApphelpCacheFlush(VOID)
{
PVOID p;
DPRINT1("SHIMS: ApphelpCacheFlush\n");
ApphelpCacheAcquireLock();
while ((p = RtlEnumerateGenericTableAvl(&ApphelpShimCache, TRUE)))
{
ApphelpCacheRemoveEntryNolock((PSHIM_CACHE_ENTRY)p);
}
ApphelpCacheReleaseLock();
return STATUS_SUCCESS;
}
NTSTATUS
ApphelpCacheDump(VOID)
{
PLIST_ENTRY ListEntry;
DPRINT1("SHIMS: NtApphelpCacheControl( Dumping entries, newset to oldest )\n");
ApphelpCacheAcquireLock();
ListEntry = ApphelpShimCacheAge.Flink;
while (ListEntry != &ApphelpShimCacheAge)
{
PSHIM_CACHE_ENTRY Entry = CONTAINING_RECORD(ListEntry, SHIM_CACHE_ENTRY, List);
DPRINT1("Entry: %S\n", Entry->Persistent.ImageName.Buffer);
DPRINT1("DateTime High: 0x%x, Low: 0x%x\n",
Entry->Persistent.DateTime.HighPart, Entry->Persistent.DateTime.LowPart);
DPRINT1("FileSize High: 0x%x, Low: 0x%x\n",
Entry->Persistent.FileSize.HighPart, Entry->Persistent.FileSize.LowPart);
DPRINT1("Flags: 0x%x\n", Entry->CompatFlags);
ListEntry = ListEntry->Flink;
}
ApphelpCacheReleaseLock();
return STATUS_SUCCESS;
}
/* PUBLIC FUNCTIONS **********************************************************/
NTSTATUS
NTAPI
NtApphelpCacheControl(
_In_ APPHELPCACHESERVICECLASS Service,
_In_opt_ PAPPHELP_CACHE_SERVICE_LOOKUP ServiceData)
{
NTSTATUS Status = STATUS_INVALID_PARAMETER;
UNICODE_STRING ImageName = { 0 };
HANDLE Handle = INVALID_HANDLE_VALUE;
if (!ApphelpCacheEnabled)
{
DPRINT1("NtApphelpCacheControl: ApphelpCacheEnabled == 0\n");
return Status;
}
switch (Service)
{
case ApphelpCacheServiceLookup:
DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpCacheServiceLookup )\n");
Status = ApphelpValidateData(ServiceData, &ImageName, &Handle);
if (NT_SUCCESS(Status))
Status = ApphelpCacheLookupEntry(&ImageName, Handle);
break;
case ApphelpCacheServiceRemove:
DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpCacheServiceRemove )\n");
Status = ApphelpValidateData(ServiceData, &ImageName, &Handle);
if (NT_SUCCESS(Status))
Status = ApphelpCacheRemoveEntry(&ImageName);
break;
case ApphelpCacheServiceUpdate:
DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpCacheServiceUpdate )\n");
Status = ApphelpCacheAccessCheck();
if (NT_SUCCESS(Status))
{
Status = ApphelpValidateData(ServiceData, &ImageName, &Handle);
if (NT_SUCCESS(Status))
Status = ApphelpCacheUpdateEntry(&ImageName, Handle);
}
break;
case ApphelpCacheServiceFlush:
Status = ApphelpCacheFlush();
break;
case ApphelpCacheServiceDump:
Status = ApphelpCacheDump();
break;
case ApphelpDBGReadRegistry:
DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpDBGReadRegistry ): flushing cache.\n");
ApphelpCacheFlush();
DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpDBGReadRegistry ): reading cache.\n");
Status = ApphelpCacheRead() ? STATUS_SUCCESS : STATUS_NOT_FOUND;
break;
case ApphelpDBGWriteRegistry:
DPRINT1("SHIMS: NtApphelpCacheControl( ApphelpDBGWriteRegistry ): writing cache.\n");
Status = ApphelpCacheWrite() ? STATUS_SUCCESS : STATUS_NOT_FOUND;
break;
default:
DPRINT1("SHIMS: NtApphelpCacheControl( Invalid service requested )\n");
break;
}
if (ImageName.Buffer)
{
ApphelpFreeUnicodeString(&ImageName);
}
return Status;
}

View file

@ -250,7 +250,7 @@ PspMapSystemDll(IN PEPROCESS Process,
LARGE_INTEGER Offset = {{0, 0}};
SIZE_T ViewSize = 0;
PVOID ImageBase = 0;
/* Map the System DLL */
Status = MmMapViewOfSection(PspSystemDllSection,
Process,
@ -267,7 +267,7 @@ PspMapSystemDll(IN PEPROCESS Process,
/* Normalize status code */
Status = STATUS_CONFLICTING_ADDRESSES;
}
/* Write the image base and return status */
if (DllBase) *DllBase = ImageBase;
return Status;
@ -677,13 +677,4 @@ PsGetVersion(OUT PULONG MajorVersion OPTIONAL,
return (NtBuildNumber >> 28) == 0xC;
}
NTSTATUS
NTAPI
NtApphelpCacheControl(IN APPHELPCACHESERVICECLASS Service,
IN PAPPHELP_CACHE_SERVICE_LOOKUP ServiceData)
{
UNIMPLEMENTED;
return STATUS_NOT_IMPLEMENTED;
}
/* EOF */

View file

@ -2,6 +2,7 @@
list(APPEND SOURCE
LdrEnumResources.c
NtAllocateVirtualMemory.c
NtApphelpCacheControl.c
NtContinue.c
NtCreateFile.c
NtCreateThread.c

View file

@ -0,0 +1,367 @@
/*
* PROJECT: ReactOS API Tests
* LICENSE: LGPL - See COPYING.LIB in the top level directory
* PURPOSE: Tests for SHIM engine caching.
* PROGRAMMER: Mark Jansen
*/
#include <apitest.h>
#include <windows.h>
#define WIN32_NO_STATUS
#include <ntndk.h>
enum ServiceCommands
{
RegisterShimCacheWithHandle = 128,
RegisterShimCacheWithoutHandle = 129,
};
NTSTATUS CallCacheControl(UNICODE_STRING* PathName, BOOLEAN WithMapping, APPHELPCACHESERVICECLASS Service)
{
APPHELP_CACHE_SERVICE_LOOKUP CacheEntry = { {0} };
NTSTATUS Status;
CacheEntry.ImageName = *PathName;
if (WithMapping)
{
OBJECT_ATTRIBUTES LocalObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
InitializeObjectAttributes(&LocalObjectAttributes, PathName,
OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = NtOpenFile(&CacheEntry.ImageHandle,
SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_READ_DATA | FILE_EXECUTE,
&LocalObjectAttributes, &IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
ok_ntstatus(Status, STATUS_SUCCESS);
}
else
{
CacheEntry.ImageHandle = INVALID_HANDLE_VALUE;
}
Status = NtApphelpCacheControl(Service, &CacheEntry);
if (CacheEntry.ImageHandle != INVALID_HANDLE_VALUE)
NtClose(CacheEntry.ImageHandle);
return Status;
}
int InitEnv(UNICODE_STRING* PathName)
{
NTSTATUS Status = CallCacheControl(PathName, FALSE, ApphelpCacheServiceRemove);
if (Status == STATUS_INVALID_PARAMETER)
{
/* Windows Vista+ has a different layout for APPHELP_CACHE_SERVICE_LOOKUP */
return 0;
}
ok(Status == STATUS_SUCCESS || Status == STATUS_NOT_FOUND,
"Wrong value for Status, expected: SUCCESS or NOT_FOUND, got: 0x%lx\n",
Status);
return 1;
}
void CheckValidation(UNICODE_STRING* PathName)
{
APPHELP_CACHE_SERVICE_LOOKUP CacheEntry = { {0} };
NTSTATUS Status;
/* Validate the handling of a NULL pointer */
Status = NtApphelpCacheControl(ApphelpCacheServiceRemove, NULL);
ok_ntstatus(Status, STATUS_INVALID_PARAMETER);
Status = NtApphelpCacheControl(ApphelpCacheServiceLookup, NULL);
ok_ntstatus(Status, STATUS_INVALID_PARAMETER);
/* Validate the handling of a NULL pointer inside the struct */
Status = NtApphelpCacheControl(ApphelpCacheServiceRemove, &CacheEntry);
ok_ntstatus(Status, STATUS_INVALID_PARAMETER);
Status = NtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry);
ok_ntstatus(Status, STATUS_INVALID_PARAMETER);
/* Just call the dump function */
Status = NtApphelpCacheControl(ApphelpCacheServiceDump, NULL);
ok_ntstatus(Status, STATUS_SUCCESS);
/* Validate the handling of an invalid handle inside the struct */
CacheEntry.ImageName = *PathName;
CacheEntry.ImageHandle = (HANDLE)2;
Status = NtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry);
ok_ntstatus(Status, STATUS_NOT_FOUND);
/* Validate the handling of an invalid service number */
Status = NtApphelpCacheControl(999, NULL);
ok_ntstatus(Status, STATUS_INVALID_PARAMETER);
Status = NtApphelpCacheControl(999, &CacheEntry);
ok_ntstatus(Status, STATUS_INVALID_PARAMETER);
}
static BOOLEAN RequestAddition(SC_HANDLE service_handle, BOOLEAN WithMapping)
{
SERVICE_STATUS Status;
ControlService(service_handle, WithMapping ? RegisterShimCacheWithHandle :
RegisterShimCacheWithoutHandle, &Status);
/* TODO: how to get a return code from the service? */
return TRUE;
}
static void RunApphelpCacheControlTests(SC_HANDLE service_handle)
{
WCHAR szPath[MAX_PATH];
UNICODE_STRING ntPath;
BOOLEAN Result;
NTSTATUS Status;
APPHELP_CACHE_SERVICE_LOOKUP CacheEntry;
GetModuleFileNameW(NULL, szPath, sizeof(szPath) / sizeof(szPath[0]));
Result = RtlDosPathNameToNtPathName_U(szPath, &ntPath, NULL, NULL);
ok(Result == TRUE, "RtlDosPathNameToNtPathName_U\n");
if (!InitEnv(&ntPath))
{
skip("NtApphelpCacheControl expects a different structure layout\n");
return;
}
/* At this point we have made sure that our binary is not present in the cache,
and that the NtApphelpCacheControl function expects the struct layout we use. */
CheckValidation(&ntPath);
/* We expect not to find it */
Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup);
ok_ntstatus(Status, STATUS_NOT_FOUND);
Status = CallCacheControl(&ntPath, FALSE, ApphelpCacheServiceLookup);
ok_ntstatus(Status, STATUS_NOT_FOUND);
/* First we add our process without a file handle (so it will be registered without file info) */
RequestAddition(service_handle, FALSE);
/* now we try to find it without validating file info */
Status = CallCacheControl(&ntPath, FALSE, ApphelpCacheServiceLookup);
ok_ntstatus(Status, STATUS_SUCCESS);
/* when validating file info the cache notices the file is wrong, so it is dropped from the cache */
Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup);
ok_ntstatus(Status, STATUS_NOT_FOUND);
/* making the second check without info also fail. */
Status = CallCacheControl(&ntPath, FALSE, ApphelpCacheServiceLookup);
ok_ntstatus(Status, STATUS_NOT_FOUND);
/* Now we add the file with file info */
RequestAddition(service_handle, TRUE);
/* so both checks should succeed */
Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup);
ok_ntstatus(Status, STATUS_SUCCESS);
Status = CallCacheControl(&ntPath, FALSE, ApphelpCacheServiceLookup);
ok_ntstatus(Status, STATUS_SUCCESS);
/* We know the file is in the cache now (assuming previous tests succeeded,
let's test invalid handle behavior */
CacheEntry.ImageName = ntPath;
CacheEntry.ImageHandle = 0;
Status = NtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry);
ok_ntstatus(Status, STATUS_NOT_FOUND);
/* re-add it for the next test */
RequestAddition(service_handle, TRUE);
Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup);
ok_ntstatus(Status, STATUS_SUCCESS);
CacheEntry.ImageHandle = (HANDLE)1;
Status = NtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry);
ok_ntstatus(Status, STATUS_NOT_FOUND);
/* and again */
RequestAddition(service_handle, TRUE);
Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup);
ok_ntstatus(Status, STATUS_SUCCESS);
CacheEntry.ImageHandle = (HANDLE)0x80000000;
Status = NtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry);
ok_ntstatus(Status, STATUS_NOT_FOUND);
RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath.Buffer);
}
/* Most service related code was taken from services_winetest:service and modified for usage here
The rest came from MSDN */
static SERVICE_STATUS_HANDLE (WINAPI *pRegisterServiceCtrlHandlerExA)(LPCSTR,LPHANDLER_FUNCTION_EX,LPVOID);
static char service_name[100] = "apphelp_test_service";
static HANDLE service_stop_event;
static SERVICE_STATUS_HANDLE service_status;
static BOOLEAN RegisterInShimCache(BOOLEAN WithMapping)
{
WCHAR szPath[MAX_PATH];
UNICODE_STRING ntPath;
BOOLEAN Result;
NTSTATUS Status;
GetModuleFileNameW(NULL, szPath, sizeof(szPath) / sizeof(szPath[0]));
Result = RtlDosPathNameToNtPathName_U(szPath, &ntPath, NULL, NULL);
if (!Result)
{
DbgPrint("RegisterInShimCache: RtlDosPathNameToNtPathName_U failed\n");
return FALSE;
}
Status = CallCacheControl(&ntPath, WithMapping, ApphelpCacheServiceUpdate);
if (!NT_SUCCESS(Status))
{
DbgPrint("RegisterInShimCache: CallCacheControl failed\n");
RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath.Buffer);
return FALSE;
}
RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath.Buffer);
return TRUE;
}
static DWORD WINAPI service_handler(DWORD ctrl, DWORD event_type, void *event_data, void *context)
{
SERVICE_STATUS status = {0};
status.dwServiceType = SERVICE_WIN32;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
switch(ctrl)
{
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
status.dwCurrentState = SERVICE_STOP_PENDING;
status.dwControlsAccepted = 0;
SetServiceStatus(service_status, &status);
SetEvent(service_stop_event);
return NO_ERROR;
case RegisterShimCacheWithHandle:
if (!RegisterInShimCache(TRUE))
{
/* TODO: how should we communicate a failure? */
}
break;
case RegisterShimCacheWithoutHandle:
if (!RegisterInShimCache(FALSE))
{
/* TODO: how should we communicate a failure? */
}
break;
default:
DbgPrint("Unhandled: %d\n", ctrl);
break;
}
status.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(service_status, &status);
return NO_ERROR;
}
static void WINAPI service_main(DWORD argc, char **argv)
{
SERVICE_STATUS status = {0};
service_status = pRegisterServiceCtrlHandlerExA(service_name, service_handler, NULL);
if(!service_status)
return;
status.dwServiceType = SERVICE_WIN32;
status.dwCurrentState = SERVICE_RUNNING;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
SetServiceStatus(service_status, &status);
WaitForSingleObject(service_stop_event, INFINITE);
status.dwCurrentState = SERVICE_STOPPED;
status.dwControlsAccepted = 0;
SetServiceStatus(service_status, &status);
}
static SC_HANDLE InstallService(SC_HANDLE scm_handle)
{
char service_cmd[MAX_PATH+150], *ptr;
SC_HANDLE service;
ptr = service_cmd + GetModuleFileNameA(NULL, service_cmd, MAX_PATH);
strcpy(ptr, " NtApphelpCacheControl service");
ptr += strlen(ptr);
service = CreateServiceA(scm_handle, service_name, service_name, GENERIC_ALL,
SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE,
service_cmd, NULL, NULL, NULL, NULL, NULL);
if (!service)
{
skip("Could not create helper service\n");
return NULL;
}
return service;
}
static void WaitService(SC_HANDLE service_handle, DWORD Status, SERVICE_STATUS_PROCESS* ssp)
{
DWORD dwBytesNeeded;
DWORD dwStartTime = GetTickCount();
while (ssp->dwCurrentState != Status)
{
Sleep(40);
if (!QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO,
(LPBYTE)ssp, sizeof(SERVICE_STATUS_PROCESS), &dwBytesNeeded ))
{
ok(0, "QueryServiceStatusEx failed waiting for %lu\n", Status);
break;
}
if ((GetTickCount() - dwStartTime) > 1000)
{
ok(0, "Timeout waiting for (%lu) from service, is: %lu.\n",
Status, ssp->dwCurrentState);
break;
}
}
}
static void RunTest()
{
SC_HANDLE scm_handle = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
SC_HANDLE service_handle = InstallService(scm_handle);
if (service_handle)
{
SERVICE_STATUS_PROCESS ssp = {0};
BOOL res = StartServiceA(service_handle, 0, NULL);
if (res)
{
WaitService(service_handle, SERVICE_RUNNING, &ssp);
RunApphelpCacheControlTests(service_handle);
ControlService(service_handle, SERVICE_CONTROL_STOP, (LPSERVICE_STATUS)&ssp);
WaitService(service_handle, SERVICE_STOPPED, &ssp);
}
else
{
skip("Could not start helper service\n");
}
DeleteService(service_handle);
}
CloseServiceHandle(scm_handle);
}
START_TEST(NtApphelpCacheControl)
{
char **argv;
int argc;
pRegisterServiceCtrlHandlerExA = (void*)GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegisterServiceCtrlHandlerExA");
if (!pRegisterServiceCtrlHandlerExA)
{
win_skip("RegisterServiceCtrlHandlerExA not available, skipping tests\n");
return;
}
argc = winetest_get_mainargs(&argv);
if(argc < 3)
{
RunTest();
}
else
{
SERVICE_TABLE_ENTRYA servtbl[] = {
{service_name, service_main},
{NULL, NULL}
};
service_stop_event = CreateEventA(NULL, TRUE, FALSE, NULL);
StartServiceCtrlDispatcherA(servtbl);
Sleep(50);
CloseHandle(service_stop_event);
}
}

View file

@ -5,6 +5,7 @@
extern void func_LdrEnumResources(void);
extern void func_NtAllocateVirtualMemory(void);
extern void func_NtApphelpCacheControl(void);
extern void func_NtContinue(void);
extern void func_NtCreateFile(void);
extern void func_NtCreateThread(void);
@ -40,6 +41,7 @@ const struct test winetest_testlist[] =
{
{ "LdrEnumResources", func_LdrEnumResources },
{ "NtAllocateVirtualMemory", func_NtAllocateVirtualMemory },
{ "NtApphelpCacheControl", func_NtApphelpCacheControl },
{ "NtContinue", func_NtContinue },
{ "NtCreateFile", func_NtCreateFile },
{ "NtCreateThread", func_NtCreateThread },