You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4314 lines
124 KiB
4314 lines
124 KiB
/*++
|
|
|
|
Copyright (c) 1989-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ntbaseplus.c
|
|
|
|
Abstract:
|
|
|
|
This module implements low level primitives. They should never be
|
|
called by anything other than this module.
|
|
|
|
Author:
|
|
|
|
dmunsil created sometime in 1999
|
|
|
|
Revision History:
|
|
|
|
several people contributed (vadimb, clupu, ...)
|
|
|
|
kinshu - 01/10/2002 - Fixed a bug in the circular linked list
|
|
insertion and deletion routines(InsertLookup,
|
|
RemoveLookup)
|
|
|
|
--*/
|
|
|
|
#include "sdbp.h"
|
|
|
|
extern const UNICODE_STRING g_ustrDatabasePath = RTL_CONSTANT_STRING(L"DatabasePath");
|
|
extern const UNICODE_STRING g_ustrDatabaseType = RTL_CONSTANT_STRING(L"DatabaseType");
|
|
extern const UNICODE_STRING g_ustrDatabaseDescription = RTL_CONSTANT_STRING(L"DatabaseDescription");
|
|
extern const UNICODE_STRING g_ustrInstallTimeStamp = RTL_CONSTANT_STRING(L"DatabaseInstallTimeStamp");
|
|
extern const UNICODE_STRING g_ustrShimDbgLevelVar = RTL_CONSTANT_STRING(L"SHIM_DEBUG_LEVEL");
|
|
extern const TCHAR g_szCompatLayer[];
|
|
|
|
void
|
|
SdbpRtlInitUnicodeStringBuffer(
|
|
RTL_UNICODE_STRING_BUFFER* pBuff,
|
|
UCHAR* StatBuff,
|
|
SIZE_T StatSize
|
|
);
|
|
|
|
//
|
|
// Global flag for wow64. On Win2k we zero this flag out
|
|
//
|
|
DWORD volatile g_dwWow64Key = (DWORD)-1;
|
|
|
|
|
|
typedef NTSTATUS
|
|
(SDBAPI* PFNEnsureBufferSize)(
|
|
IN ULONG Flags,
|
|
IN OUT PRTL_BUFFER Buffer,
|
|
IN SIZE_T Size);
|
|
|
|
PFNEnsureBufferSize volatile g_pfnEnsureBufferSize = NULL;
|
|
|
|
NTSTATUS
|
|
SDBAPI
|
|
SdbEnsureBufferSizeFunction(
|
|
IN ULONG Flags,
|
|
IN OUT PRTL_BUFFER Buffer,
|
|
IN SIZE_T Size);
|
|
|
|
//
|
|
// to work on win2k, we have to modify default buffer-manipulation macro
|
|
//
|
|
#undef RtlEnsureBufferSize
|
|
#define RtlEnsureBufferSize(Flags, Buff, NewSizeBytes) \
|
|
( ((Buff) != NULL && (NewSizeBytes) <= (Buff)->Size) \
|
|
? STATUS_SUCCESS \
|
|
: SdbEnsureBufferSizeFunction((Flags), (Buff), (NewSizeBytes)) \
|
|
)
|
|
|
|
|
|
|
|
|
|
//
|
|
// This routine was stolen from buffer.c in ntdll so that we can support downlevel platforms
|
|
//
|
|
NTSTATUS
|
|
SDBAPI
|
|
SdbpEnsureBufferSize(
|
|
IN ULONG Flags,
|
|
IN OUT PRTL_BUFFER Buffer,
|
|
IN SIZE_T Size
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PUCHAR Temp = NULL;
|
|
|
|
if ((Flags & ~(RTL_ENSURE_BUFFER_SIZE_NO_COPY)) != 0) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
if (Buffer == NULL) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
if (Size <= Buffer->Size) {
|
|
Status = STATUS_SUCCESS;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Size <= Buffer->StaticSize does not imply static allocation, it
|
|
// could be heap allocation that the client poked smaller.
|
|
//
|
|
if (Buffer->Buffer == Buffer->StaticBuffer && Size <= Buffer->StaticSize) {
|
|
Buffer->Size = Size;
|
|
Status = STATUS_SUCCESS;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// The realloc case was messed up in Whistler, and got removed.
|
|
// Put it back in Blackcomb.
|
|
//
|
|
Temp = (PUCHAR)RtlAllocateHeap(RtlProcessHeap(), 0, (Size));
|
|
if (Temp == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
if ((Flags & RTL_ENSURE_BUFFER_SIZE_NO_COPY) == 0) {
|
|
RtlCopyMemory(Temp, Buffer->Buffer, Buffer->Size);
|
|
}
|
|
|
|
if (RTLP_BUFFER_IS_HEAP_ALLOCATED(Buffer)) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Buffer->Buffer);
|
|
Buffer->Buffer = NULL;
|
|
}
|
|
|
|
ASSERT(Temp != NULL);
|
|
Buffer->Buffer = Temp;
|
|
Buffer->Size = Size;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Exit:
|
|
return Status;
|
|
}
|
|
|
|
|
|
#define KEY_MACHINE TEXT("\\Registry\\Machine")
|
|
|
|
|
|
BOOL
|
|
SdbpBuildMachineKeyPath(
|
|
IN LPCWSTR pwszPath,
|
|
OUT PUNICODE_STRING pmachineKeyPath
|
|
)
|
|
/*++
|
|
Return: TRUE on success, FALSE otherwise.
|
|
|
|
Desc: Given a path to the key it builds it up for HKEY_LOCAL_MACHINE
|
|
registry. The buffer used by pmachineKeyPath will have to be freed
|
|
using SdbFree!
|
|
--*/
|
|
{
|
|
BOOL bReturn = FALSE;
|
|
UNICODE_STRING machineKey = {0};
|
|
|
|
RtlInitUnicodeString(&machineKey, KEY_MACHINE);
|
|
|
|
pmachineKeyPath->Length = 0;
|
|
pmachineKeyPath->MaximumLength = (USHORT)(wcslen(pwszPath) * sizeof(*pwszPath) +
|
|
machineKey.Length +
|
|
sizeof(UNICODE_NULL));
|
|
|
|
pmachineKeyPath->Buffer = SdbAlloc(pmachineKeyPath->MaximumLength);
|
|
|
|
if (pmachineKeyPath->Buffer == NULL) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpBuildUserKeyPath",
|
|
"Failed to allocate %d bytes for user key buffer.\n",
|
|
pmachineKeyPath->MaximumLength));
|
|
goto out;
|
|
}
|
|
|
|
RtlAppendUnicodeStringToString(pmachineKeyPath, &machineKey);
|
|
RtlAppendUnicodeToString(pmachineKeyPath, pwszPath);
|
|
bReturn = TRUE;
|
|
|
|
out:
|
|
return bReturn;
|
|
}
|
|
|
|
BOOL
|
|
SdbpBuildUserKeyPath(
|
|
IN LPCWSTR pwszPath,
|
|
OUT PUNICODE_STRING puserKeyPath
|
|
)
|
|
/*++
|
|
Return: TRUE on success, FALSE otherwise.
|
|
|
|
Desc: Given a path to the key it builds it up for HKEY_CURRENT_USER
|
|
registry. The buffer used by puserKeyPath will have to be freed
|
|
using SdbFree!
|
|
--*/
|
|
{
|
|
BOOL bReturn = FALSE;
|
|
NTSTATUS Status;
|
|
UNICODE_STRING userKey = {0};
|
|
|
|
Status = RtlFormatCurrentUserKeyPath(&userKey);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpBuildUserKeyPath",
|
|
"Failed to format current user key path 0x%x\n",
|
|
Status));
|
|
goto out;
|
|
}
|
|
|
|
puserKeyPath->Length = 0;
|
|
puserKeyPath->MaximumLength = (USHORT)(wcslen(pwszPath) * sizeof(*pwszPath) +
|
|
userKey.Length +
|
|
sizeof(UNICODE_NULL));
|
|
|
|
puserKeyPath->Buffer = SdbAlloc(puserKeyPath->MaximumLength);
|
|
|
|
if (puserKeyPath->Buffer == NULL) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpBuildUserKeyPath",
|
|
"Failed to allocate %d bytes for user key buffer.\n",
|
|
puserKeyPath->MaximumLength));
|
|
goto out;
|
|
}
|
|
|
|
RtlAppendUnicodeStringToString(puserKeyPath, &userKey);
|
|
RtlAppendUnicodeToString(puserKeyPath, pwszPath);
|
|
bReturn = TRUE;
|
|
|
|
out:
|
|
RtlFreeUnicodeString(&userKey);
|
|
return bReturn;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Custom SDB cache code
|
|
//
|
|
|
|
PUSERSDBLOOKUP
|
|
SdbpFindSDBLookupEntry(
|
|
IN PSDBCONTEXT pSdbContext, // sdb context for searches
|
|
IN LPCWSTR pwszItemName, // item name foo.exe for instance
|
|
IN BOOL bLayer // true if Layer
|
|
)
|
|
{
|
|
PUSERSDBLOOKUP pLookup = pSdbContext->pLookupHead;
|
|
|
|
if (pLookup == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
do {
|
|
if (!_wcsicmp(pLookup->pwszItemName, pwszItemName) && pLookup->bLayer == bLayer) {
|
|
return pLookup;
|
|
}
|
|
|
|
pLookup = pLookup->pNext;
|
|
|
|
} while (pLookup != pSdbContext->pLookupHead);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void
|
|
RemoveLookup(
|
|
PSDBCONTEXT pSdbContext,
|
|
PUSERSDBLOOKUP pLookup
|
|
)
|
|
{
|
|
assert(pLookup);
|
|
|
|
if (pLookup->pPrev == pLookup) {
|
|
//
|
|
// There is only one element
|
|
//
|
|
pSdbContext->pLookupHead = NULL;
|
|
SdbFree(pLookup);
|
|
return;
|
|
}
|
|
|
|
if (pSdbContext->pLookupHead == pLookup) {
|
|
//
|
|
// We are deleting the first element
|
|
//
|
|
pSdbContext->pLookupHead = pLookup->pNext;
|
|
}
|
|
|
|
pLookup->pPrev->pNext = pLookup->pNext;
|
|
pLookup->pNext->pPrev = pLookup->pPrev;
|
|
|
|
SdbFree(pLookup);
|
|
}
|
|
|
|
void
|
|
InsertLookup(
|
|
PSDBCONTEXT pSdbContext,
|
|
PUSERSDBLOOKUP pLookup
|
|
)
|
|
{
|
|
assert(pLookup);
|
|
|
|
if (pSdbContext->pLookupHead == NULL) {
|
|
pSdbContext->pLookupHead = pLookup;
|
|
pLookup->pPrev = pLookup;
|
|
pLookup->pNext = pLookup;
|
|
return;
|
|
}
|
|
|
|
pLookup->pNext = pSdbContext->pLookupHead;
|
|
pLookup->pPrev = pSdbContext->pLookupHead->pPrev;
|
|
|
|
//
|
|
// The pNext of the last element (pSdbContext->pLookupHead->pPrev) should point to the
|
|
// new element
|
|
//
|
|
pSdbContext->pLookupHead->pPrev->pNext = pLookup;
|
|
pSdbContext->pLookupHead->pPrev = pLookup;
|
|
|
|
pSdbContext->pLookupHead = pLookup;
|
|
}
|
|
|
|
PUSERSDBLOOKUP
|
|
SdbpCreateSDBLookupEntry(
|
|
IN PSDBCONTEXT pSdbContext,
|
|
IN DWORD dwCount,
|
|
IN LPCWSTR pwszItemName,
|
|
IN BOOL bLayer
|
|
)
|
|
{
|
|
int nLen;
|
|
DWORD dwSize;
|
|
PUSERSDBLOOKUP pLookup;
|
|
|
|
pLookup = SdbpFindSDBLookupEntry(pSdbContext, pwszItemName, bLayer);
|
|
if (pLookup != NULL) {
|
|
|
|
//
|
|
// cornel: If we already have it shouldn't we just return here ?
|
|
//
|
|
assert(pLookup->dwCount == dwCount);
|
|
RemoveLookup(pSdbContext, pLookup);
|
|
}
|
|
|
|
//
|
|
// One entry is already included. See the structure definition.
|
|
//
|
|
// The memory layout is:
|
|
//
|
|
// USERSDBLOOKUP
|
|
// (dwCount - 1) USERSDBLOOKUPENTRY structures
|
|
// the string that pLookup->pwszItemName points to
|
|
//
|
|
nLen = (int)wcslen(pwszItemName) + 1;
|
|
|
|
dwSize = (DWORD)(sizeof(USERSDBLOOKUP) + nLen * sizeof(WCHAR) +
|
|
(dwCount - 1) * sizeof(USERSDBLOOKUPENTRY)); // one is already included
|
|
|
|
pLookup = (PUSERSDBLOOKUP)SdbAlloc(dwSize);
|
|
|
|
if (pLookup == NULL) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpCreateSDBLookupEntry",
|
|
"Failed to allocate 0x%lx bytes for sdb lookup buffer\n",
|
|
dwSize));
|
|
return NULL;
|
|
}
|
|
|
|
pLookup->pwszItemName = (LPWSTR)((PBYTE)(pLookup + 1) +
|
|
(dwCount - 1) * sizeof(USERSDBLOOKUPENTRY));
|
|
|
|
pLookup->bLayer = bLayer;
|
|
pLookup->dwCount = dwCount;
|
|
|
|
StringCchCopy(pLookup->pwszItemName, nLen, pwszItemName);
|
|
|
|
InsertLookup(pSdbContext, pLookup);
|
|
|
|
return pLookup;
|
|
}
|
|
|
|
|
|
void
|
|
SdbpCleanupUserSDBCache(
|
|
IN PSDBCONTEXT pSdbContext
|
|
)
|
|
{
|
|
while (pSdbContext->pLookupHead) {
|
|
RemoveLookup(pSdbContext, pSdbContext->pLookupHead);
|
|
}
|
|
}
|
|
|
|
//
|
|
// We built the array of these in accordance with ValueName's corresponding to a
|
|
// list of custom databases we have -- and query existing values for this particular entry
|
|
// ValueBuffer shall be allocated to be of a maximum length considering the number
|
|
// of the entries that we have
|
|
//
|
|
typedef int (__cdecl* PFNQSORTCOMPARE)(const void* pElem1, const void* pElem2);
|
|
|
|
int __cdecl
|
|
SdbpUserSDBLookupCompareEntries(
|
|
LPCVOID pEntry1,
|
|
LPCVOID pEntry2
|
|
)
|
|
{
|
|
//
|
|
// last-installed goes first
|
|
//
|
|
PUSERSDBLOOKUPENTRY pLookupEntry1 = (PUSERSDBLOOKUPENTRY)pEntry1;
|
|
PUSERSDBLOOKUPENTRY pLookupEntry2 = (PUSERSDBLOOKUPENTRY)pEntry2;
|
|
|
|
|
|
if (pLookupEntry1->liTimeStamp.QuadPart < pLookupEntry2->liTimeStamp.QuadPart) {
|
|
return 1;
|
|
}
|
|
|
|
if (pLookupEntry1->liTimeStamp.QuadPart == pLookupEntry2->liTimeStamp.QuadPart) {
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
SDBAPI
|
|
SdbpFindCharInUnicodeString(
|
|
ULONG Flags,
|
|
PCUNICODE_STRING StringToSearch,
|
|
PCUNICODE_STRING CharSet,
|
|
USHORT* NonInclusivePrefixLength
|
|
)
|
|
{
|
|
LPCWSTR pch;
|
|
|
|
//
|
|
// Implement only the case when we move backwards
|
|
//
|
|
if (Flags != RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END) {
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
pch = StringToSearch->Buffer + StringToSearch->Length / sizeof(WCHAR);
|
|
|
|
while (pch >= StringToSearch->Buffer) {
|
|
|
|
if (_tcschr(CharSet->Buffer, *pch)) {
|
|
//
|
|
// Got the char
|
|
//
|
|
if (NonInclusivePrefixLength) {
|
|
*NonInclusivePrefixLength = (USHORT)(pch - StringToSearch->Buffer) * sizeof(WCHAR);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
pch--;
|
|
}
|
|
|
|
//
|
|
// We haven't found it. Return failure.
|
|
//
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SdbpEnumUserSdb(
|
|
IN PSDBCONTEXT pSdbContext,
|
|
IN LPCWSTR wszItemName, // file name of an exe or the layer name
|
|
IN BOOL bLayer,
|
|
OUT PUSERSDBLOOKUP* ppSdbLookup
|
|
)
|
|
{
|
|
//
|
|
// This is how we figure:
|
|
// path + \\layers\\ + MAX_PATH (for name)
|
|
//
|
|
WCHAR FullKeyBuffer[sizeof(APPCOMPAT_KEY_PATH_MACHINE_CUSTOM)/sizeof(WCHAR) + 8 + MAX_PATH];
|
|
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
|
|
struct {
|
|
KEY_FULL_INFORMATION KeyFullInformation;
|
|
WCHAR Buffer[MAX_PATH];
|
|
} KeyInformationBuffer;
|
|
|
|
PKEY_FULL_INFORMATION pFullInfo = &KeyInformationBuffer.KeyFullInformation;
|
|
|
|
struct {
|
|
KEY_VALUE_FULL_INFORMATION KeyValueFullInformation;
|
|
BYTE Buffer[MAX_PATH * 2 * sizeof(WCHAR)];
|
|
} KeyValueInformationBuffer;
|
|
|
|
PKEY_VALUE_FULL_INFORMATION pValue = &KeyValueInformationBuffer.KeyValueFullInformation;
|
|
|
|
PBYTE pData;
|
|
PUSERSDBLOOKUPENTRY pEntry;
|
|
ULONG ResultLength;
|
|
ULONG KeyValueLength;
|
|
DWORD dwIndexValue = 0;
|
|
DWORD dwIndexEntries = 0;
|
|
NTSTATUS Status;
|
|
HANDLE KeyHandle = NULL;
|
|
UNICODE_STRING ustrDbGuid;
|
|
USHORT uPrefix = 0;
|
|
UNICODE_STRING ustrFullKey;
|
|
UNICODE_STRING ustrValue;
|
|
UNICODE_STRING ustrCharDot = RTL_CONSTANT_STRING(L".");
|
|
GUID guidDB;
|
|
PUSERSDBLOOKUP pLookup = NULL;
|
|
BOOL bSuccess = FALSE;
|
|
ULARGE_INTEGER liTimeStamp = {0};
|
|
|
|
ustrFullKey.Length = 0;
|
|
ustrFullKey.Buffer = FullKeyBuffer;
|
|
ustrFullKey.MaximumLength = sizeof(FullKeyBuffer);
|
|
|
|
RtlAppendUnicodeToString(&ustrFullKey, APPCOMPAT_KEY_PATH_MACHINE_CUSTOM);
|
|
|
|
if (bLayer) {
|
|
RtlAppendUnicodeToString(&ustrFullKey, L"\\Layers\\");
|
|
} else {
|
|
RtlAppendUnicodeToString(&ustrFullKey, L"\\");
|
|
}
|
|
|
|
RtlAppendUnicodeToString(&ustrFullKey, wszItemName);
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&ustrFullKey,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenKey(&KeyHandle, GENERIC_READ|SdbpGetWow64Flag(), &ObjectAttributes);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlWarning,
|
|
"SdbpEnumUserSdb",
|
|
"Failed to open key \"%s\" Status 0x%lx\n",
|
|
ustrFullKey.Buffer,
|
|
Status));
|
|
goto cleanup;
|
|
}
|
|
|
|
Status = NtQueryKey(KeyHandle,
|
|
KeyFullInformation,
|
|
&KeyInformationBuffer,
|
|
sizeof(KeyInformationBuffer),
|
|
&ResultLength);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlWarning,
|
|
"SdbpEnumUserSdb",
|
|
"Failed to query key \"%s\" Status 0x%lx\n",
|
|
ustrFullKey.Buffer,
|
|
Status));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (pFullInfo->Values == 0) {
|
|
goto cleanup; // nothing to look at
|
|
}
|
|
|
|
//
|
|
// Create new lookup item
|
|
//
|
|
pLookup = SdbpCreateSDBLookupEntry(pSdbContext, pFullInfo->Values, wszItemName, bLayer);
|
|
if (pLookup == NULL) {
|
|
//
|
|
// Oops, can't have an entry -- can't do lookups
|
|
//
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Load all the values
|
|
//
|
|
for (dwIndexValue = 0; dwIndexValue < pFullInfo->Values; dwIndexValue++) {
|
|
|
|
Status = NtEnumerateValueKey(KeyHandle,
|
|
dwIndexValue,
|
|
KeyValueFullInformation,
|
|
&KeyValueInformationBuffer,
|
|
sizeof(KeyValueInformationBuffer),
|
|
&KeyValueLength);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlWarning,
|
|
"SdbpEnumUserSdb",
|
|
"Failed to enum value index 0x%lx Status 0x%lx\n",
|
|
dwIndexValue,
|
|
Status));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Extract database guid
|
|
//
|
|
ustrDbGuid.Buffer = &pValue->Name[0];
|
|
ustrDbGuid.MaximumLength =
|
|
ustrDbGuid.Length = (USHORT)pValue->NameLength;
|
|
|
|
Status = SdbpFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
|
|
&ustrDbGuid,
|
|
&ustrCharDot,
|
|
&uPrefix);
|
|
if (NT_SUCCESS(Status)) {
|
|
ustrDbGuid.Length = uPrefix;
|
|
ustrDbGuid.Buffer[uPrefix / sizeof(WCHAR)] = 0;
|
|
}
|
|
|
|
//
|
|
// Convert to guid
|
|
//
|
|
if (!SdbGUIDFromStringN(ustrDbGuid.Buffer, ustrDbGuid.Length, &guidDB)) {
|
|
DBGPRINT((sdlWarning, "SdbpEnumUserSdb",
|
|
"Failed to convert db name \"%s\" to guid Status 0x%lx\n",
|
|
ustrDbGuid.Buffer, Status));
|
|
|
|
continue; // We have failed to convert, skip and do the next
|
|
}
|
|
|
|
//
|
|
// Data may not be aligned, we store here timestamp
|
|
//
|
|
pData = (PBYTE)pValue + pValue->DataOffset;
|
|
|
|
//
|
|
// Check out value type
|
|
//
|
|
switch (pValue->Type) {
|
|
|
|
case REG_BINARY:
|
|
//
|
|
// The value is likely a timestamp
|
|
//
|
|
if (pValue->DataLength < sizeof(liTimeStamp.QuadPart)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpEnumUserSdb",
|
|
"Data length (0x%lx) does not match timestamp length for index 0x%lx\n",
|
|
pValue->DataLength,
|
|
dwIndexValue));
|
|
liTimeStamp.QuadPart = 0;
|
|
} else {
|
|
RtlCopyMemory(&liTimeStamp.QuadPart, (LPCVOID)pData, sizeof(liTimeStamp.QuadPart));
|
|
}
|
|
break;
|
|
|
|
case REG_QWORD:
|
|
//
|
|
// Naturally supported type, not aligned though
|
|
//
|
|
liTimeStamp.QuadPart = *(PULONGLONG UNALIGNED)pData;
|
|
break;
|
|
|
|
case REG_SZ:
|
|
|
|
//
|
|
// Hack: in case we need to enforce a particular order on some odd entry -- we
|
|
// use this hack
|
|
//
|
|
liTimeStamp.QuadPart = 0;
|
|
|
|
if (pValue->DataLength > 0) {
|
|
ustrValue.Buffer = (WCHAR*)pData;
|
|
ustrValue.Length = (USHORT)pValue->DataLength - sizeof(UNICODE_NULL);
|
|
ustrValue.MaximumLength = (USHORT)pValue->DataLength;
|
|
|
|
Status = RtlUnicodeStringToInteger(&ustrValue, 0, &liTimeStamp.LowPart);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlWarning,
|
|
"SdbpEnumUserSdb",
|
|
"Bad value for \"%s\" -> \"%s\" for index 0x%lx\n",
|
|
pValue->Name,
|
|
(LPCWSTR)pData,
|
|
dwIndexValue));
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Error case
|
|
//
|
|
DBGPRINT((sdlError,
|
|
"SdbpEnumUserSdb",
|
|
"Bad value type 0x%lx for index 0x%lx\n",
|
|
pValue->Type,
|
|
dwIndexValue));
|
|
Status = STATUS_INVALID_PARAMETER; // bugbug
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check the status
|
|
//
|
|
if (!NT_SUCCESS(Status)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Fill-in this entry
|
|
//
|
|
pEntry = &pLookup->rgEntries[dwIndexEntries];
|
|
++dwIndexEntries;
|
|
|
|
RtlCopyMemory(&pEntry->guidDB, &guidDB, sizeof(GUID));
|
|
pEntry->liTimeStamp.QuadPart = liTimeStamp.QuadPart;
|
|
}
|
|
|
|
//
|
|
// After this, we are basically ready to lookup entries in sdbs
|
|
//
|
|
pLookup->dwCount = dwIndexEntries; // adjust count based on actually filled-in entries
|
|
|
|
if (dwIndexEntries > 1) {
|
|
//
|
|
// Optimization: only sort if more than one entry.
|
|
//
|
|
qsort((void*)&pLookup->rgEntries[0],
|
|
(size_t)pLookup->dwCount,
|
|
(size_t)sizeof(USERSDBLOOKUPENTRY),
|
|
(PFNQSORTCOMPARE)SdbpUserSDBLookupCompareEntries);
|
|
}
|
|
|
|
if (ppSdbLookup) {
|
|
*ppSdbLookup = pLookup;
|
|
}
|
|
|
|
bSuccess = TRUE;
|
|
|
|
cleanup:
|
|
|
|
if (KeyHandle) {
|
|
NtClose(KeyHandle);
|
|
}
|
|
|
|
if (!bSuccess && pLookup != NULL) {
|
|
RemoveLookup(pSdbContext, pLookup);
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
BOOL
|
|
SdbGetNthUserSdb(
|
|
IN HSDB hSDB,
|
|
IN LPCWSTR wszItemName, // item name (foo.exe or layer name)
|
|
IN BOOL bLayer,
|
|
IN OUT LPDWORD pdwIndex, // (0-based)
|
|
OUT GUID* pGuidDB
|
|
)
|
|
{
|
|
PSDBCONTEXT pSdbContext = (PSDBCONTEXT)hSDB;
|
|
PUSERSDBLOOKUP pLookup = NULL;
|
|
BOOL bSuccess = FALSE;
|
|
DWORD nIndex = *pdwIndex;
|
|
|
|
//
|
|
// Context already may have a user sdb cache -- do not touch it
|
|
//
|
|
if (nIndex == 0) {
|
|
|
|
//
|
|
// The first call, we have to init our system
|
|
//
|
|
if (!SdbpEnumUserSdb(pSdbContext, wszItemName, bLayer, &pLookup)) {
|
|
goto cleanup; // no custom sdb will be accessible
|
|
}
|
|
|
|
} else {
|
|
pLookup = SdbpFindSDBLookupEntry(pSdbContext, wszItemName, bLayer);
|
|
}
|
|
|
|
if (pLookup == NULL || nIndex >= pLookup->dwCount) {
|
|
return FALSE;
|
|
}
|
|
|
|
RtlCopyMemory(pGuidDB, &pLookup->rgEntries[nIndex].guidDB, sizeof(*pGuidDB));
|
|
++nIndex;
|
|
bSuccess = TRUE;
|
|
|
|
*pdwIndex = nIndex;
|
|
|
|
cleanup:
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
|
|
#ifndef WIN32U_MODE
|
|
|
|
BOOL
|
|
SDBAPI
|
|
SdbpGetLongPathName(
|
|
IN LPCWSTR pwszPath,
|
|
OUT PRTL_UNICODE_STRING_BUFFER pBuffer
|
|
)
|
|
{
|
|
DWORD dwBufferSize;
|
|
NTSTATUS Status;
|
|
|
|
RtlSyncStringToBuffer(pBuffer);
|
|
|
|
dwBufferSize = GetLongPathNameW(pwszPath,
|
|
pBuffer->String.Buffer,
|
|
RTL_STRING_GET_MAX_LENGTH_CHARS(&pBuffer->String));
|
|
if (!dwBufferSize) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpGetLongPathName",
|
|
"Failed to convert short path to long path error 0x%lx\n",
|
|
GetLastError()));
|
|
return FALSE;
|
|
}
|
|
|
|
if (dwBufferSize < RTL_STRING_GET_MAX_LENGTH_CHARS(&pBuffer->String)) {
|
|
goto Done;
|
|
}
|
|
|
|
Status = RtlEnsureUnicodeStringBufferSizeChars(pBuffer, dwBufferSize);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpGetLongPathName",
|
|
"Failed to obtain sufficient buffer for long path (0x%lx chars) status 0x%lx\n",
|
|
dwBufferSize,
|
|
Status));
|
|
return FALSE;
|
|
}
|
|
|
|
dwBufferSize = GetLongPathNameW(pwszPath,
|
|
pBuffer->String.Buffer,
|
|
RTL_STRING_GET_MAX_LENGTH_CHARS(&pBuffer->String));
|
|
|
|
if (!dwBufferSize || dwBufferSize > RTL_STRING_GET_MAX_LENGTH_CHARS(&pBuffer->String)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpGetLongPathName",
|
|
"Failed to convert short path to long path (0x%lx chars) error 0x%lx\n",
|
|
dwBufferSize,
|
|
GetLastError()));
|
|
return FALSE;
|
|
}
|
|
|
|
Done:
|
|
|
|
pBuffer->String.Length = (USHORT)dwBufferSize;
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
SdbpGetPermLayersInternal(
|
|
IN PUNICODE_STRING pstrExePath,
|
|
OUT LPWSTR wszLayers, // output receives layers string if success
|
|
IN OUT LPDWORD pdwBytes, // pointer to the size of wszLayers buffer
|
|
IN BOOL bMachine
|
|
)
|
|
{
|
|
UNICODE_STRING strKeyPath = { 0 };
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE KeyHandle;
|
|
ULONG KeyValueBuffer[256];
|
|
ULONG KeyValueLength;
|
|
NTSTATUS Status;
|
|
DWORD dwBytes = *pdwBytes;
|
|
|
|
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
|
|
|
|
*pdwBytes = 0;
|
|
|
|
if (bMachine) {
|
|
//
|
|
// Build the machine path
|
|
//
|
|
if (!SdbpBuildMachineKeyPath(APPCOMPAT_PERM_LAYER_PATH, &strKeyPath)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpGetPermLayersInternal",
|
|
"Failed to format machine key path for \"%S\"\n",
|
|
APPCOMPAT_PERM_LAYER_PATH));
|
|
goto out;
|
|
}
|
|
|
|
} else {
|
|
if (!SdbpBuildUserKeyPath(APPCOMPAT_PERM_LAYER_PATH, &strKeyPath)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpGetPermLayersInternal",
|
|
"Failed to format user key path for \"%S\"\n",
|
|
APPCOMPAT_PERM_LAYER_PATH));
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&strKeyPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenKey(&KeyHandle, GENERIC_READ|SdbpGetWow64Flag(), &ObjectAttributes);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlInfo,
|
|
"SdbpGetPermLayersInternal",
|
|
"Failed to open Key \"%S\" Status 0x%x\n",
|
|
strKeyPath.Buffer,
|
|
Status));
|
|
goto out;
|
|
}
|
|
|
|
KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)KeyValueBuffer;
|
|
|
|
Status = NtQueryValueKey(KeyHandle,
|
|
pstrExePath,
|
|
KeyValuePartialInformation,
|
|
KeyValueInformation,
|
|
sizeof(KeyValueBuffer),
|
|
&KeyValueLength);
|
|
|
|
NtClose(KeyHandle);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlInfo,
|
|
"SdbpGetPermLayersInternal",
|
|
"Failed to read value info from Key \"%S\" Status 0x%x\n",
|
|
strKeyPath.Buffer,
|
|
Status));
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// Check for the value type.
|
|
//
|
|
if (KeyValueInformation->Type != REG_SZ) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpGetPermLayersInternal",
|
|
"Unexpected value type 0x%x for Key \"%S\".\n",
|
|
KeyValueInformation->Type,
|
|
strKeyPath.Buffer));
|
|
goto out;
|
|
}
|
|
|
|
if (wszLayers == NULL || KeyValueInformation->DataLength > dwBytes) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpGetPermLayersInternal",
|
|
"Value length %d too long for key \"%S\".\n",
|
|
KeyValueInformation->DataLength,
|
|
strKeyPath.Buffer));
|
|
|
|
*pdwBytes = KeyValueInformation->DataLength;
|
|
goto out;
|
|
}
|
|
|
|
RtlMoveMemory(wszLayers, KeyValueInformation->Data, KeyValueInformation->DataLength);
|
|
*pdwBytes = KeyValueInformation->DataLength;
|
|
|
|
out:
|
|
if (strKeyPath.Buffer != NULL) {
|
|
SdbFree(strKeyPath.Buffer);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
SdbGetPermLayerKeys(
|
|
IN LPCWSTR pwszPath, // exe path
|
|
OUT LPWSTR pwszLayers, // output receives layers string if success
|
|
IN LPDWORD pdwBytes, // pointer to the size of wszLayers buffer
|
|
IN DWORD dwFlags // per machine, per user flags
|
|
)
|
|
/*++
|
|
Return: TRUE on success, FALSE otherwise.
|
|
|
|
Desc: Retrieves layers string associated with a given exe.
|
|
wszLayers may be NULL. In that case pdwBytes will receive necessary
|
|
size if the size provided in pdwBytes is not sufficient. The function
|
|
will return TRUE and pdwBytes will contain the necessary size.
|
|
--*/
|
|
{
|
|
UNICODE_STRING ustrPath;
|
|
BOOL bRet = FALSE;
|
|
WCHAR wszSignPath[MAX_PATH];
|
|
LPCWSTR pwszNewPath;
|
|
UCHAR PathBuffer[MAX_PATH * 2];
|
|
DWORD dwBytesMachine, dwBytesUser;
|
|
RTL_UNICODE_STRING_BUFFER LongPath;
|
|
|
|
SdbpRtlInitUnicodeStringBuffer(&LongPath, PathBuffer, sizeof(PathBuffer));
|
|
|
|
if (!pwszPath || !pdwBytes) {
|
|
DBGPRINT((sdlError,
|
|
"SdbGetPermLayerKeys",
|
|
"NULL parameter passed for wszPath or pdwBytes.\n"));
|
|
goto out;
|
|
}
|
|
|
|
if (!SdbpGetLongPathName(pwszPath, &LongPath)) {
|
|
goto out;
|
|
}
|
|
|
|
pwszPath = LongPath.String.Buffer;
|
|
|
|
if (SdbpIsPathOnCdRom(pwszPath)) {
|
|
|
|
SdbpBuildSignature(pwszPath, wszSignPath, CHARCOUNT(wszSignPath));
|
|
|
|
pwszNewPath = wszSignPath;
|
|
} else {
|
|
pwszNewPath = pwszPath;
|
|
}
|
|
|
|
RtlInitUnicodeString(&ustrPath, pwszNewPath);
|
|
|
|
dwBytesMachine = 0;
|
|
dwBytesUser = 0;
|
|
|
|
if (dwFlags & GPLK_MACHINE) {
|
|
|
|
dwBytesMachine = *pdwBytes;
|
|
|
|
SdbpGetPermLayersInternal(&ustrPath, pwszLayers, &dwBytesMachine, TRUE);
|
|
}
|
|
|
|
if (dwFlags & GPLK_USER) {
|
|
|
|
if (dwBytesMachine != 0) {
|
|
if (pwszLayers != NULL) {
|
|
pwszLayers += (dwBytesMachine / sizeof(TCHAR) - 1);
|
|
*pwszLayers++ = TEXT(' ');
|
|
}
|
|
|
|
dwBytesMachine += sizeof(TCHAR);
|
|
}
|
|
|
|
if (pwszLayers != NULL) {
|
|
dwBytesUser = *pdwBytes - dwBytesMachine;
|
|
}
|
|
|
|
SdbpGetPermLayersInternal(&ustrPath, pwszLayers, &dwBytesUser, FALSE);
|
|
|
|
if (dwBytesUser == 0 && dwBytesMachine != 0) {
|
|
dwBytesMachine -= sizeof(TCHAR);
|
|
|
|
if (pwszLayers != NULL) {
|
|
*(--pwszLayers) = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pwszLayers != NULL) {
|
|
if (*pdwBytes >= dwBytesMachine + dwBytesUser) {
|
|
bRet = TRUE;
|
|
}
|
|
} else {
|
|
bRet = TRUE;
|
|
}
|
|
|
|
*pdwBytes = dwBytesMachine + dwBytesUser;
|
|
|
|
if (*pdwBytes == 0) {
|
|
bRet = FALSE;
|
|
}
|
|
|
|
out:
|
|
|
|
RtlFreeUnicodeStringBuffer(&LongPath);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
#else // WIN32U_MODE case, stub the perm layer api
|
|
|
|
BOOL
|
|
SdbGetPermLayerKeys(
|
|
IN LPCTSTR szPath,
|
|
OUT LPTSTR szLayers,
|
|
IN LPDWORD pdwBytes,
|
|
IN DWORD dwFlags
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER(szPath);
|
|
UNREFERENCED_PARAMETER(szLayers);
|
|
UNREFERENCED_PARAMETER(pdwBytes);
|
|
UNREFERENCED_PARAMETER(dwFlags);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#endif // WIN32U_MODE
|
|
|
|
HANDLE
|
|
SdbpCreateKeyPath(
|
|
LPCWSTR pwszPath,
|
|
BOOL bMachine
|
|
)
|
|
/*++
|
|
Return: The handle to the registry key created.
|
|
|
|
Desc: Given a path to the key relative to HKEY_CURRENT_USER open/create
|
|
the key returns the handle to the key or NULL on failure.
|
|
--*/
|
|
{
|
|
UNICODE_STRING strKeyPath = { 0 };
|
|
HANDLE KeyHandle = NULL;
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
ULONG CreateDisposition;
|
|
|
|
if (bMachine) {
|
|
if (!SdbpBuildMachineKeyPath(pwszPath, &strKeyPath)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbCreateKeyPath",
|
|
"Failed to format machine key path for \"%s\"\n",
|
|
pwszPath));
|
|
goto out;
|
|
}
|
|
} else {
|
|
if (!SdbpBuildUserKeyPath(pwszPath, &strKeyPath)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbCreateKeyPath",
|
|
"Failed to format user key path for \"%s\"\n",
|
|
pwszPath));
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&strKeyPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtCreateKey(&KeyHandle,
|
|
STANDARD_RIGHTS_WRITE |
|
|
KEY_QUERY_VALUE |
|
|
KEY_ENUMERATE_SUB_KEYS |
|
|
KEY_SET_VALUE |
|
|
KEY_CREATE_SUB_KEY|
|
|
SdbpGetWow64Flag(),
|
|
&ObjectAttributes,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
&CreateDisposition);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbCreateKeyPath",
|
|
"NtCreateKey failed for \"%s\" Status 0x%x\n",
|
|
pwszPath,
|
|
Status));
|
|
KeyHandle = NULL;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
if (strKeyPath.Buffer != NULL) {
|
|
SdbFree(strKeyPath.Buffer);
|
|
}
|
|
|
|
return KeyHandle;
|
|
}
|
|
|
|
#ifndef WIN32U_MODE
|
|
|
|
BOOL
|
|
SdbSetPermLayerKeys(
|
|
IN LPCWSTR wszPath, // application path
|
|
IN LPCWSTR wszLayers, // layers to associate with this application
|
|
IN BOOL bMachine // TRUE if the layers should be set per machine
|
|
)
|
|
/*++
|
|
Return: TRUE on success, FALSE otherwise.
|
|
|
|
Desc: Associates wszLayers string with application wszPath.
|
|
--*/
|
|
{
|
|
UNICODE_STRING ustrPath;
|
|
NTSTATUS Status;
|
|
HANDLE KeyHandle;
|
|
BOOL bRet = FALSE;
|
|
WCHAR wszSignPath[MAX_PATH];
|
|
LPCWSTR pwszPath;
|
|
RTL_UNICODE_STRING_BUFFER LongPath;
|
|
UCHAR PathBuffer[MAX_PATH * 2];
|
|
|
|
if (!wszLayers || !wszLayers[0]) {
|
|
//
|
|
// They passed in an empty string -- delete the layer keys
|
|
//
|
|
return SdbDeletePermLayerKeys(wszPath, bMachine);
|
|
}
|
|
|
|
SdbpRtlInitUnicodeStringBuffer(&LongPath, PathBuffer, sizeof(PathBuffer));
|
|
|
|
if (!SdbpGetLongPathName(wszPath, &LongPath)) {
|
|
goto out;
|
|
}
|
|
|
|
wszPath = LongPath.String.Buffer;
|
|
|
|
if (SdbpIsPathOnCdRom(wszPath)) {
|
|
|
|
SdbpBuildSignature(wszPath, wszSignPath, CHARCOUNT(wszSignPath));
|
|
|
|
pwszPath = wszSignPath;
|
|
} else {
|
|
pwszPath = wszPath;
|
|
}
|
|
|
|
RtlInitUnicodeString(&ustrPath, pwszPath);
|
|
|
|
KeyHandle = SdbpCreateKeyPath(APPCOMPAT_KEY_PATH_NT, bMachine);
|
|
|
|
if (KeyHandle == NULL) {
|
|
DBGPRINT((sdlError,
|
|
"SdbSetPermLayerKeys",
|
|
"Failed to create user key path for \"%s\"\n",
|
|
APPCOMPAT_KEY_PATH_NT));
|
|
goto out;
|
|
}
|
|
|
|
NtClose(KeyHandle);
|
|
|
|
KeyHandle = SdbpCreateKeyPath(APPCOMPAT_PERM_LAYER_PATH, bMachine);
|
|
|
|
if (KeyHandle == NULL) {
|
|
DBGPRINT((sdlError,
|
|
"SdbSetPermLayerKeys",
|
|
"Failed to create user key path \"%s\"\n",
|
|
APPCOMPAT_PERM_LAYER_PATH));
|
|
goto out;
|
|
}
|
|
|
|
Status = NtSetValueKey(KeyHandle,
|
|
&ustrPath,
|
|
0,
|
|
REG_SZ,
|
|
(PVOID)wszLayers,
|
|
(ULONG)(wcslen(wszLayers) + 1) * sizeof(WCHAR));
|
|
NtClose(KeyHandle);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbSetPermLayerKeys",
|
|
"Failed NtSetValueKey status 0x%x\n",
|
|
Status));
|
|
goto out;
|
|
}
|
|
|
|
bRet = TRUE;
|
|
|
|
out:
|
|
|
|
RtlFreeUnicodeStringBuffer(&LongPath);
|
|
return bRet;
|
|
}
|
|
|
|
BOOL
|
|
SdbDeletePermLayerKeys(
|
|
IN LPCWSTR wszPath,
|
|
IN BOOL bMachine
|
|
)
|
|
/*++
|
|
Return: TRUE if success
|
|
|
|
Desc: This function is used to disable permanent layer storage entry
|
|
for the application.
|
|
--*/
|
|
{
|
|
UNICODE_STRING strKeyPath = { 0 };
|
|
UNICODE_STRING ustrPath;
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE KeyHandle;
|
|
BOOL bRet = FALSE;
|
|
WCHAR wszSignPath[MAX_PATH];
|
|
LPCWSTR pwszPath;
|
|
|
|
RTL_UNICODE_STRING_BUFFER LongPath;
|
|
UCHAR PathBuffer[MAX_PATH * 2];
|
|
|
|
SdbpRtlInitUnicodeStringBuffer(&LongPath, PathBuffer, sizeof(PathBuffer));
|
|
|
|
if (!SdbpGetLongPathName(wszPath, &LongPath)) {
|
|
goto out;
|
|
}
|
|
|
|
wszPath = LongPath.String.Buffer;
|
|
|
|
if (SdbpIsPathOnCdRom(wszPath)) {
|
|
|
|
SdbpBuildSignature(wszPath, wszSignPath, CHARCOUNT(wszSignPath));
|
|
|
|
pwszPath = wszSignPath;
|
|
} else {
|
|
pwszPath = wszPath;
|
|
}
|
|
|
|
RtlInitUnicodeString(&ustrPath, pwszPath);
|
|
|
|
if (bMachine) {
|
|
if (!SdbpBuildMachineKeyPath(APPCOMPAT_PERM_LAYER_PATH, &strKeyPath)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbDeletePermLayerKeys",
|
|
"Failed to format machine key path for \"%S\"\n",
|
|
APPCOMPAT_PERM_LAYER_PATH));
|
|
goto out;
|
|
}
|
|
} else {
|
|
if (!SdbpBuildUserKeyPath(APPCOMPAT_PERM_LAYER_PATH, &strKeyPath)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbDeletePermLayerKeys",
|
|
"Failed to format user key path for \"%S\"\n",
|
|
APPCOMPAT_PERM_LAYER_PATH));
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&strKeyPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenKey(&KeyHandle,
|
|
KEY_WRITE|SdbpGetWow64Flag(),
|
|
&ObjectAttributes);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlInfo,
|
|
"SdbDeletePermLayerKeys",
|
|
"Failed to open Key for \"%S\" Status 0x%x\n",
|
|
strKeyPath.Buffer,
|
|
Status));
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
bRet = TRUE;
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
Status = NtDeleteValueKey(KeyHandle,
|
|
&ustrPath);
|
|
|
|
NtClose(KeyHandle);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlInfo,
|
|
"SdbDeletePermLayerKeys",
|
|
"Failed to delete value from Key \"%S\" Status 0x%x\n",
|
|
strKeyPath.Buffer,
|
|
Status));
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
bRet = TRUE;
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
bRet = TRUE;
|
|
|
|
out:
|
|
|
|
if (strKeyPath.Buffer != NULL) {
|
|
SdbFree(strKeyPath.Buffer);
|
|
}
|
|
|
|
RtlFreeUnicodeStringBuffer(&LongPath);
|
|
return bRet;
|
|
|
|
}
|
|
|
|
#endif // WIN32U_MODE
|
|
|
|
BOOL
|
|
SdbSetEntryFlags(
|
|
IN GUID* pGuidID,
|
|
IN DWORD dwFlags
|
|
)
|
|
/*++
|
|
Return: TRUE if success
|
|
|
|
Desc: This function is used to disable apphelp or shim entries.
|
|
--*/
|
|
{
|
|
|
|
UNICODE_STRING ustrExeID = { 0 };
|
|
NTSTATUS Status;
|
|
HANDLE KeyHandle;
|
|
BOOL bReturn = FALSE;
|
|
|
|
Status = GUID_TO_UNICODE_STRING(pGuidID, &ustrExeID);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbSetEntryFlags",
|
|
"Failed to convert GUID to string 0x%x\n",
|
|
Status));
|
|
goto out;
|
|
}
|
|
|
|
KeyHandle = SdbpCreateKeyPath(APPCOMPAT_KEY_PATH_NT, FALSE);
|
|
|
|
if (KeyHandle == NULL) {
|
|
DBGPRINT((sdlError,
|
|
"SdbSetEntryFlags",
|
|
"Failed to create user key path for \"%s\"\n",
|
|
APPCOMPAT_KEY_PATH_NT));
|
|
goto out;
|
|
}
|
|
|
|
Status = NtSetValueKey(KeyHandle,
|
|
&ustrExeID,
|
|
0,
|
|
REG_DWORD,
|
|
&dwFlags,
|
|
sizeof(DWORD));
|
|
NtClose(KeyHandle);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbSetEntryFlags",
|
|
"Failed NtSetValueKey status 0x%x\n",
|
|
Status));
|
|
goto out;
|
|
}
|
|
|
|
bReturn = TRUE;
|
|
|
|
out:
|
|
FREE_GUID_STRING(&ustrExeID);
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SdbpGetLongFileName(
|
|
IN LPCWSTR szFullPath, // a full UNC or DOS path & filename, "c:\foo\mylong~1.ext"
|
|
OUT LPWSTR szLongFileName, // the long filename portion "mylongfilename.ext",
|
|
IN DWORD cchSize // size (in characters) of szLongFileName
|
|
)
|
|
/*++
|
|
Return: TRUE if successful, FALSE otherwise.
|
|
|
|
Desc: Retrieves the long filename (without the path) of a potentially
|
|
short full-path filename.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
BOOL bSuccess = FALSE;
|
|
UNICODE_STRING FileName;
|
|
UNICODE_STRING PathName;
|
|
OBJECT_ATTRIBUTES ObjPathName;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
HANDLE hDirectoryHandle = NULL;
|
|
PFILE_BOTH_DIR_INFORMATION DirectoryInfo;
|
|
|
|
struct SEARCH_BUFFER {
|
|
FILE_BOTH_DIR_INFORMATION DirInfo;
|
|
WCHAR Names[MAX_PATH-1];
|
|
} Buffer;
|
|
|
|
PathName.Buffer = NULL;
|
|
|
|
if (!RtlDosPathNameToNtPathName_U(szFullPath,
|
|
&PathName,
|
|
&FileName.Buffer,
|
|
NULL)) {
|
|
PathName.Buffer = NULL;
|
|
|
|
DBGPRINT((sdlError,
|
|
"SdbpGetLongFileName",
|
|
"Failed to get NT path name for \"%s\"\n",
|
|
szFullPath));
|
|
goto out;
|
|
}
|
|
|
|
if (FileName.Buffer == NULL) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpGetLongFileName",
|
|
"Filename buffer is NULL for \"%s\"\n",
|
|
szFullPath));
|
|
goto out;
|
|
}
|
|
|
|
FileName.Length = PathName.Length -
|
|
(USHORT)((ULONG_PTR)FileName.Buffer - (ULONG_PTR)PathName.Buffer);
|
|
|
|
FileName.MaximumLength = FileName.Length;
|
|
|
|
PathName.Length = (USHORT)((ULONG_PTR)FileName.Buffer - (ULONG_PTR)PathName.Buffer);
|
|
PathName.MaximumLength = PathName.Length;
|
|
|
|
InitializeObjectAttributes(&ObjPathName,
|
|
&PathName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
status = NtOpenFile(&hDirectoryHandle,
|
|
FILE_LIST_DIRECTORY | SYNCHRONIZE,
|
|
&ObjPathName,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_DIRECTORY_FILE |
|
|
FILE_SYNCHRONOUS_IO_NONALERT |
|
|
FILE_OPEN_FOR_BACKUP_INTENT);
|
|
|
|
if (STATUS_SUCCESS != status) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpGetLongFileName",
|
|
"Failed to open directory file. Status 0x%x\n",
|
|
status));
|
|
goto out;
|
|
}
|
|
|
|
DirectoryInfo = &(Buffer.DirInfo);
|
|
|
|
status = NtQueryDirectoryFile(hDirectoryHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
DirectoryInfo,
|
|
sizeof(Buffer),
|
|
FileBothDirectoryInformation,
|
|
TRUE,
|
|
&FileName,
|
|
FALSE);
|
|
|
|
if (STATUS_SUCCESS != status) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpGetLongFileName",
|
|
"NtQueryDirectoryFile Failed 0x%x\n",
|
|
status));
|
|
goto out;
|
|
}
|
|
|
|
if (DirectoryInfo->FileNameLength > MAX_PATH - 2) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpGetLongFileName",
|
|
"File name longer than MAX_PATH - 2\n"));
|
|
goto out;
|
|
}
|
|
|
|
StringCchCopyN(szLongFileName,
|
|
cchSize,
|
|
DirectoryInfo->FileName,
|
|
DirectoryInfo->FileNameLength / sizeof(WCHAR));
|
|
|
|
bSuccess = TRUE;
|
|
|
|
out:
|
|
if (PathName.Buffer) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, PathName.Buffer);
|
|
}
|
|
|
|
if (hDirectoryHandle) {
|
|
NtClose(hDirectoryHandle);
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
void
|
|
SdbpGetWinDir(
|
|
OUT LPWSTR pwszDir,
|
|
IN DWORD cchSize
|
|
)
|
|
/*++
|
|
Return: void.
|
|
|
|
Desc: Retrieves the system windows directory.
|
|
--*/
|
|
{
|
|
StringCchCopy(pwszDir, cchSize, USER_SHARED_DATA->NtSystemRoot);
|
|
}
|
|
|
|
|
|
void
|
|
SdbpGetAppPatchDir(
|
|
IN HSDB hSDB,
|
|
OUT LPWSTR szAppPatchPath,
|
|
IN DWORD cchSize
|
|
)
|
|
/*++
|
|
Return: void.
|
|
|
|
Desc: Retrieves the %windir%\AppPatch directory.
|
|
--*/
|
|
{
|
|
PSDBCONTEXT pContext = (PSDBCONTEXT)hSDB;
|
|
int nLen;
|
|
|
|
StringCchCopy(szAppPatchPath, cchSize, USER_SHARED_DATA->NtSystemRoot);
|
|
|
|
nLen = (int)wcslen(szAppPatchPath);
|
|
|
|
if (nLen > 0 && L'\\' == szAppPatchPath[nLen-1]) {
|
|
szAppPatchPath[nLen-1] = L'\0';
|
|
}
|
|
|
|
if (pContext != NULL && pContext->uExeType == IMAGE_FILE_MACHINE_IA64) {
|
|
StringCchCat(szAppPatchPath, cchSize, L"\\AppPatch\\IA64");
|
|
} else {
|
|
StringCchCat(szAppPatchPath, cchSize, L"\\AppPatch");
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
SdbpGetCurrentTime(
|
|
OUT LPSYSTEMTIME lpTime
|
|
)
|
|
/*++
|
|
Return: void.
|
|
|
|
Desc: Retrieves the current local time.
|
|
--*/
|
|
{
|
|
LARGE_INTEGER LocalTime;
|
|
LARGE_INTEGER SystemTime;
|
|
LARGE_INTEGER Bias;
|
|
TIME_FIELDS TimeFields;
|
|
|
|
volatile KSYSTEM_TIME* pRealBias = &(USER_SHARED_DATA->TimeZoneBias);
|
|
|
|
//
|
|
// Read system time from shared region.
|
|
//
|
|
do {
|
|
SystemTime.HighPart = USER_SHARED_DATA->SystemTime.High1Time;
|
|
SystemTime.LowPart = USER_SHARED_DATA->SystemTime.LowPart;
|
|
} while (SystemTime.HighPart != USER_SHARED_DATA->SystemTime.High2Time);
|
|
|
|
//
|
|
// Read time zone bias from shared region.
|
|
// If it's terminal server session use client bias.
|
|
//
|
|
do {
|
|
Bias.HighPart = pRealBias->High1Time;
|
|
Bias.LowPart = pRealBias->LowPart;
|
|
} while (Bias.HighPart != pRealBias->High2Time);
|
|
|
|
LocalTime.QuadPart = SystemTime.QuadPart - Bias.QuadPart;
|
|
|
|
RtlTimeToTimeFields(&LocalTime, &TimeFields);
|
|
|
|
lpTime->wYear = TimeFields.Year;
|
|
lpTime->wMonth = TimeFields.Month;
|
|
lpTime->wDayOfWeek = TimeFields.Weekday;
|
|
lpTime->wDay = TimeFields.Day;
|
|
lpTime->wHour = TimeFields.Hour;
|
|
lpTime->wMinute = TimeFields.Minute;
|
|
lpTime->wSecond = TimeFields.Second;
|
|
lpTime->wMilliseconds = TimeFields.Milliseconds;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SdbpGetEnvVar(
|
|
IN LPCWSTR pEnvironment,
|
|
IN LPCWSTR pszVariableName,
|
|
OUT LPWSTR pszVariableValue,
|
|
OUT LPDWORD pdwBufferSize
|
|
)
|
|
/*++
|
|
Return:
|
|
|
|
Desc: Retrieves the value of the specified environment variable.
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_VARIABLE_NOT_FOUND;
|
|
UNICODE_STRING ustrVariableName;
|
|
UNICODE_STRING ustrVariableValue;
|
|
DWORD dwBufferSize = 0;
|
|
|
|
if (pdwBufferSize && pszVariableValue) {
|
|
dwBufferSize = *pdwBufferSize;
|
|
}
|
|
|
|
RtlInitUnicodeString(&ustrVariableName, pszVariableName);
|
|
|
|
ustrVariableValue.Length = 0;
|
|
ustrVariableValue.MaximumLength = (USHORT)dwBufferSize * sizeof(WCHAR);
|
|
ustrVariableValue.Buffer = (WCHAR*)pszVariableValue;
|
|
|
|
Status = RtlQueryEnvironmentVariable_U((LPVOID)pEnvironment,
|
|
&ustrVariableName,
|
|
&ustrVariableValue);
|
|
|
|
if (pdwBufferSize) {
|
|
*pdwBufferSize = (DWORD)ustrVariableValue.Length / sizeof(WCHAR);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SdbpMapFile(
|
|
IN HANDLE hFile, // handle to the open file (this is done previously)
|
|
OUT PIMAGEFILEDATA pImageData
|
|
)
|
|
/*++
|
|
Return: TRUE on success, FALSE otherwise.
|
|
|
|
Desc: Maps a file into memory.
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE hSection = NULL;
|
|
SIZE_T ViewSize = 0;
|
|
PVOID pBase = NULL;
|
|
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
FILE_STANDARD_INFORMATION StandardInfo;
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
DBGPRINT((sdlError, "SdbpMapFile", "Invalid argument\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Query the file's size.
|
|
//
|
|
Status = NtQueryInformationFile(hFile,
|
|
&IoStatusBlock,
|
|
&StandardInfo,
|
|
sizeof(StandardInfo),
|
|
FileStandardInformation);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpMapFile",
|
|
"NtQueryInformationFile (EOF) failed Status = 0x%x\n",
|
|
Status));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Create a view of the file.
|
|
//
|
|
Status = NtCreateSection(&hSection,
|
|
SECTION_MAP_READ,
|
|
NULL,
|
|
0,
|
|
PAGE_READONLY,
|
|
SEC_COMMIT,
|
|
hFile);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpMapFile",
|
|
"NtCreateSection failed Status 0x%x\n",
|
|
Status));
|
|
//
|
|
// Can't create section, thus no way to map the file.
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
Status = NtMapViewOfSection(hSection,
|
|
NtCurrentProcess(),
|
|
&pBase,
|
|
0,
|
|
0,
|
|
NULL,
|
|
&ViewSize,
|
|
ViewUnmap,
|
|
0,
|
|
PAGE_READONLY);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
NtClose(hSection);
|
|
DBGPRINT((sdlError,
|
|
"SdbpMapFile",
|
|
"NtMapViewOfSection failed Status 0x%x\n",
|
|
Status));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// We have made it. The file is mapped.
|
|
//
|
|
pImageData->hFile = hFile;
|
|
pImageData->hSection = hSection;
|
|
pImageData->pBase = pBase;
|
|
pImageData->ViewSize = ViewSize;
|
|
pImageData->FileSize = StandardInfo.EndOfFile.QuadPart;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
SdbpUnmapFile(
|
|
IN PIMAGEFILEDATA pImageData
|
|
)
|
|
/*++
|
|
Return: TRUE on success, FALSE otherwise.
|
|
|
|
Desc: Unmaps a file from memory.
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
BOOL bReturn = TRUE;
|
|
|
|
if (pImageData->pBase != NULL) {
|
|
Status = NtUnmapViewOfSection(NtCurrentProcess(), pImageData->pBase);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpUnmapFile",
|
|
"NtUnmapViewOfSection failed Status 0x%x\n",
|
|
Status));
|
|
bReturn = FALSE;
|
|
}
|
|
pImageData->pBase = NULL;
|
|
}
|
|
|
|
if (pImageData->hSection) {
|
|
NtClose(pImageData->hSection);
|
|
pImageData->hSection = NULL;
|
|
}
|
|
|
|
pImageData->hFile = INVALID_HANDLE_VALUE; // hFile is not owned by us
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
#ifndef _WIN64
|
|
|
|
void
|
|
SdbResetStackOverflow(
|
|
void
|
|
)
|
|
/*++
|
|
Return: void.
|
|
|
|
Desc: This function sets the guard page to its position before the
|
|
stack overflow.
|
|
|
|
This is a copy of the following CRT routine:
|
|
void _resetstkoflw(void) - Recovers from Stack Overflow
|
|
--*/
|
|
{
|
|
LPBYTE pStack, pGuard, pStackBase, pCommitBase;
|
|
MEMORY_BASIC_INFORMATION mbi;
|
|
SYSTEM_BASIC_INFORMATION BasicInfo;
|
|
DWORD PageSize;
|
|
NTSTATUS Status;
|
|
SIZE_T ReturnLength;
|
|
DWORD OldProtect;
|
|
SIZE_T RegionSize;
|
|
|
|
//
|
|
// Use alloca() to get the current stack pointer
|
|
// the call below DOES NOT fail
|
|
//
|
|
__try {
|
|
pStack = _alloca(1);
|
|
} __except(GetExceptionCode() == EXCEPTION_STACK_OVERFLOW ?
|
|
EXCEPTION_EXECUTE_HANDLER:EXCEPTION_CONTINUE_SEARCH) {
|
|
pStack = (LPBYTE)&pStack; // hack: we just grab the address of our internal variable
|
|
}
|
|
|
|
//
|
|
// Find the base of the stack.
|
|
//
|
|
Status = NtQueryVirtualMemory(NtCurrentProcess(),
|
|
pStack,
|
|
MemoryBasicInformation,
|
|
&mbi,
|
|
sizeof(mbi),
|
|
&ReturnLength);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpResetStackOverflow",
|
|
"NtQueryVirtualMemory failed on stack 0x%x Status 0x%x\n",
|
|
pStack,
|
|
Status));
|
|
return;
|
|
}
|
|
|
|
pStackBase = mbi.AllocationBase;
|
|
|
|
Status = NtQueryVirtualMemory(NtCurrentProcess(),
|
|
pStackBase,
|
|
MemoryBasicInformation,
|
|
&mbi,
|
|
sizeof(mbi),
|
|
&ReturnLength);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpResetStackOverflow",
|
|
"NtQueryVirtualMemory failed on stack base 0x%x Status 0x%x\n",
|
|
pStackBase,
|
|
Status));
|
|
return;
|
|
}
|
|
|
|
if (mbi.State & MEM_RESERVE) {
|
|
pCommitBase = (LPBYTE)mbi.AllocationBase + mbi.RegionSize;
|
|
|
|
Status = NtQueryVirtualMemory(NtCurrentProcess(),
|
|
pCommitBase,
|
|
MemoryBasicInformation,
|
|
&mbi,
|
|
sizeof(mbi),
|
|
&ReturnLength);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpResetStackOverflow",
|
|
"NtQueryVirtualMemory failed on stack commit base 0x%x Status 0x%x\n",
|
|
pCommitBase,
|
|
Status));
|
|
return;
|
|
}
|
|
|
|
} else {
|
|
pCommitBase = pStackBase;
|
|
}
|
|
|
|
//
|
|
// Find the page size.
|
|
//
|
|
Status = NtQuerySystemInformation(SystemBasicInformation,
|
|
&BasicInfo,
|
|
sizeof(BasicInfo),
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpResetStackOverflow",
|
|
"NtQuerySystemInformation failed Status 0x%x\n",
|
|
Status));
|
|
return;
|
|
}
|
|
|
|
PageSize = BasicInfo.PageSize;
|
|
|
|
//
|
|
// Find the page just below where stack pointer currently points.
|
|
//
|
|
pGuard = (LPBYTE)(((DWORD_PTR)pStack & ~(DWORD_PTR)(PageSize -1)) - PageSize);
|
|
|
|
if (pGuard < pStackBase) {
|
|
//
|
|
// We can't save this.
|
|
//
|
|
DBGPRINT((sdlError,
|
|
"SdbpResetStackOverflow",
|
|
"Bad guard page 0x%x base 0x%x\n",
|
|
pGuard,
|
|
pStackBase));
|
|
return;
|
|
}
|
|
|
|
if (pGuard > pStackBase) {
|
|
|
|
RegionSize = pGuard - pStackBase;
|
|
|
|
Status = NtFreeVirtualMemory(NtCurrentProcess(),
|
|
&pStackBase,
|
|
&RegionSize,
|
|
MEM_DECOMMIT);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpResetStackOverflow",
|
|
"NtFreeVirtualMemory on 0x%x failed Status 0x%x\n",
|
|
pStackBase,
|
|
Status));
|
|
}
|
|
}
|
|
|
|
Status = NtAllocateVirtualMemory(NtCurrentProcess(),
|
|
&pGuard,
|
|
0,
|
|
&PageSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpResetStackOverflow",
|
|
"NtAllocateVirtualMemory on 0x%x failed Status 0x%x\n",
|
|
pGuard,
|
|
Status));
|
|
return;
|
|
}
|
|
|
|
Status = NtProtectVirtualMemory(NtCurrentProcess(),
|
|
&pGuard,
|
|
&PageSize,
|
|
PAGE_READWRITE|PAGE_GUARD,
|
|
&OldProtect);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpResetStackOverflow",
|
|
"NtProtectVirtualMemory on 0x%x failed Status 0x%x\n",
|
|
pGuard,
|
|
Status));
|
|
}
|
|
}
|
|
|
|
#endif // _WIN64
|
|
|
|
|
|
BOOL
|
|
SdbpQueryFileDirectoryAttributes(
|
|
LPCWSTR FilePath,
|
|
PFILEDIRECTORYATTRIBUTES pFileDirectoryAttributes
|
|
)
|
|
/*++
|
|
Return: BUGBUG: ?
|
|
|
|
Desc: BUGBUG: ?
|
|
--*/
|
|
{
|
|
OBJECT_ATTRIBUTES DirObjectAttributes;
|
|
UNICODE_STRING FileName; // filename part
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
HANDLE FileHandle = INVALID_HANDLE_VALUE;
|
|
UNICODE_STRING Path = { 0 }; // nt path name
|
|
|
|
struct tagDirectoryInformationBuffer { // directory information (see ntioapi)
|
|
FILE_DIRECTORY_INFORMATION DirInfo;
|
|
WCHAR name[MAX_PATH];
|
|
} DirectoryInformationBuf;
|
|
|
|
PFILE_DIRECTORY_INFORMATION pDirInfo = &DirectoryInformationBuf.DirInfo;
|
|
|
|
USHORT nPathLength;
|
|
NTSTATUS Status = STATUS_INVALID_PARAMETER;
|
|
BOOL bTranslationStatus;
|
|
|
|
RtlZeroMemory(pFileDirectoryAttributes, sizeof(*pFileDirectoryAttributes));
|
|
|
|
bTranslationStatus = RtlDosPathNameToNtPathName_U(FilePath,
|
|
&Path,
|
|
&FileName.Buffer,
|
|
NULL);
|
|
if (!bTranslationStatus) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpGetFileDirectoryAttributes",
|
|
"RtlDosPathNameToNtPathName_U failed for \"%s\"\n",
|
|
FilePath));
|
|
assert(FALSE);
|
|
goto Fail;
|
|
}
|
|
|
|
if (FileName.Buffer == NULL) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpGetFileDirectoryAttributes",
|
|
"RtlDosPathNameToNtPathName_U returned no filename for \"%s\"",
|
|
FilePath));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Fail;
|
|
}
|
|
|
|
nPathLength = (USHORT)((ULONG_PTR)FileName.Buffer - (ULONG_PTR)Path.Buffer);
|
|
|
|
FileName.Length = Path.Length - nPathLength;
|
|
FileName.MaximumLength = FileName.Length;
|
|
|
|
//
|
|
// path length, adjust please -- chopping off trailing backslash
|
|
//
|
|
// BUGBUG: what am I suppose to understand from the above comment ?
|
|
//
|
|
Path.Length = nPathLength;
|
|
|
|
if (nPathLength > 2 * sizeof(WCHAR)) {
|
|
if (*(Path.Buffer + (nPathLength/sizeof(WCHAR)) - 2) != L':' &&
|
|
*(Path.Buffer + (nPathLength/sizeof(WCHAR)) - 1) == L'\\') {
|
|
Path.Length -= sizeof(WCHAR);
|
|
}
|
|
}
|
|
|
|
Path.MaximumLength = Path.Length;
|
|
|
|
InitializeObjectAttributes(&DirObjectAttributes,
|
|
&Path,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenFile(&FileHandle,
|
|
FILE_LIST_DIRECTORY | SYNCHRONIZE,
|
|
&DirObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
|
FILE_DIRECTORY_FILE|
|
|
FILE_SYNCHRONOUS_IO_NONALERT |
|
|
FILE_OPEN_FOR_BACKUP_INTENT);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpGetFileDirectoryAttributes",
|
|
"NtOpenFile Failed to open \"%s\", status 0x%x\n",
|
|
(int)Path.Length,
|
|
Path.Buffer,
|
|
Status));
|
|
goto Fail;
|
|
}
|
|
|
|
Status = NtQueryDirectoryFile(FileHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
pDirInfo,
|
|
sizeof(DirectoryInformationBuf),
|
|
FileDirectoryInformation,
|
|
TRUE,
|
|
&FileName,
|
|
FALSE);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpGetFileDirectoryAttributes",
|
|
"NtQueryDirectoryFile Failed to query \"%s\" for \"%s\" status 0x%x\n",
|
|
(int)Path.Length,
|
|
Path.Buffer,
|
|
(int)FileName.Length,
|
|
FileName.Buffer,
|
|
Status));
|
|
goto Fail;
|
|
}
|
|
|
|
pFileDirectoryAttributes->dwFlags |= FDA_FILESIZE;
|
|
pFileDirectoryAttributes->dwFileSizeHigh = pDirInfo->EndOfFile.HighPart;
|
|
pFileDirectoryAttributes->dwFileSizeLow = pDirInfo->EndOfFile.LowPart;
|
|
|
|
Fail:
|
|
|
|
if (Path.Buffer) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Path.Buffer);
|
|
}
|
|
|
|
if (FileHandle != INVALID_HANDLE_VALUE) {
|
|
NtClose(FileHandle);
|
|
}
|
|
|
|
return NT_SUCCESS(Status);
|
|
}
|
|
|
|
LPWSTR
|
|
StringToUnicodeString(
|
|
IN LPCSTR pszSrc // pointer to the ANSI string to be converted
|
|
)
|
|
/*++
|
|
Return: A pointer to an allocated UNICODE string.
|
|
|
|
Desc: Converts an ANSI string to UNICODE.
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ANSI_STRING AnsiSrc;
|
|
UNICODE_STRING UnicodeDest = { 0 };
|
|
LPWSTR pwszUnicodeDest = NULL;
|
|
ULONG Length;
|
|
|
|
RtlInitString(&AnsiSrc, pszSrc);
|
|
|
|
Length = RtlAnsiStringToUnicodeSize(&AnsiSrc);
|
|
|
|
if (Length > MAXUSHORT) {
|
|
DBGPRINT((sdlWarning, "StringToUnicodeString", "String too long.\n"));
|
|
return pwszUnicodeDest;
|
|
}
|
|
|
|
pwszUnicodeDest = (LPWSTR)SdbAlloc(Length); // already contains the NULL
|
|
|
|
if (pwszUnicodeDest == NULL) {
|
|
DBGPRINT((sdlWarning, "StringToUnicodeString", "Failed to allocate %d bytes.\n", Length));
|
|
return pwszUnicodeDest;
|
|
}
|
|
|
|
UnicodeDest.MaximumLength = (USHORT)Length;
|
|
UnicodeDest.Length = UnicodeDest.MaximumLength - sizeof(UNICODE_NULL);
|
|
UnicodeDest.Buffer = pwszUnicodeDest;
|
|
|
|
Status = RtlAnsiStringToUnicodeString(&UnicodeDest, &AnsiSrc, FALSE);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
SdbFree(pwszUnicodeDest);
|
|
pwszUnicodeDest = NULL;
|
|
|
|
DBGPRINT((sdlWarning,
|
|
"StringToUnicodeString",
|
|
"Failed to convert \"%s\" to UNICODE. Status 0x%x\n",
|
|
pszSrc,
|
|
Status));
|
|
}
|
|
|
|
return pwszUnicodeDest;
|
|
}
|
|
|
|
BOOL
|
|
SdbpGet16BitDescription(
|
|
OUT LPWSTR* ppszDescription,
|
|
IN PIMAGEFILEDATA pImageData
|
|
)
|
|
/*++
|
|
Return: TRUE on success, FALSE otherwise
|
|
|
|
Desc: This function retrieves 16-bit description from the
|
|
module identified by pImageData
|
|
--*/
|
|
{
|
|
BOOL bSuccess;
|
|
char szBuffer[256];
|
|
LPWSTR pwszDescription = NULL;
|
|
|
|
bSuccess = SdbpQuery16BitDescription(szBuffer, pImageData);
|
|
|
|
if (bSuccess) {
|
|
pwszDescription = StringToUnicodeString(szBuffer);
|
|
|
|
*ppszDescription = pwszDescription;
|
|
}
|
|
|
|
return (pwszDescription != NULL);
|
|
}
|
|
|
|
|
|
BOOL
|
|
SdbpGet16BitModuleName(
|
|
OUT LPWSTR* ppszModuleName,
|
|
IN PIMAGEFILEDATA pImageData
|
|
)
|
|
/*++
|
|
Return: TRUE on success, FALSE otherwise
|
|
|
|
Desc: Retrieves 16-bit module name from the image
|
|
identified by pImageData, then converts it to UNICODE
|
|
--*/
|
|
{
|
|
BOOL bSuccess;
|
|
char szBuffer[256]; // max possible length of a string is 256 (1-byte worth of length)
|
|
LPWSTR pwszModuleName = NULL;
|
|
|
|
bSuccess = SdbpQuery16BitModuleName(szBuffer, pImageData);
|
|
|
|
if (bSuccess) {
|
|
pwszModuleName = StringToUnicodeString(szBuffer);
|
|
|
|
*ppszModuleName = pwszModuleName;
|
|
}
|
|
|
|
return (pwszModuleName != NULL);
|
|
}
|
|
|
|
PVOID
|
|
SdbGetFileInfo(
|
|
IN HSDB hSDB,
|
|
IN LPCWSTR pwszFilePath,
|
|
IN HANDLE hFile, // OPTIONAL HANDLE to the file, not used here
|
|
IN LPVOID pImageBase,
|
|
IN DWORD dwImageSize, // ignored
|
|
IN BOOL bNoCache
|
|
)
|
|
/*++
|
|
Return: BUGBUG: ?
|
|
|
|
Desc: Create and link a new entry in a file attribute cache.
|
|
--*/
|
|
{
|
|
PSDBCONTEXT pContext = (PSDBCONTEXT)hSDB;
|
|
WCHAR* FullPath = NULL;
|
|
PFILEINFO pFileInfo = NULL;
|
|
ULONG nBufferLength;
|
|
ULONG cch;
|
|
UNICODE_STRING FullPathU;
|
|
BOOL bFreeFullPath;
|
|
|
|
//
|
|
// Check hFile and/or pImageBase -- if these are supplied --
|
|
// we presume that the file exists and we do not touch it
|
|
//
|
|
if (hFile != INVALID_HANDLE_VALUE || pImageBase != NULL) {
|
|
|
|
FullPath = (LPWSTR)pwszFilePath; // we do not have to free it
|
|
cch = (ULONG)wcslen(FullPath) * sizeof(WCHAR);
|
|
nBufferLength = cch + sizeof(UNICODE_NULL);
|
|
bFreeFullPath = FALSE;
|
|
goto CreateFileInfo;
|
|
}
|
|
|
|
//
|
|
// See if we have info on this file. First we get the full path.
|
|
//
|
|
cch = RtlGetFullPathName_U(pwszFilePath,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
if (cch == 0) {
|
|
DBGPRINT((sdlError,
|
|
"SdbGetFileInfo",
|
|
"RtlGetFullPathName_U failed for \"%s\"\n",
|
|
pwszFilePath));
|
|
return pFileInfo;
|
|
}
|
|
|
|
//
|
|
// Now allocate that much.
|
|
//
|
|
nBufferLength = cch + sizeof(UNICODE_NULL);
|
|
|
|
STACK_ALLOC(FullPath, nBufferLength);
|
|
|
|
if (FullPath == NULL) {
|
|
DBGPRINT((sdlError,
|
|
"SdbGetFileInfo",
|
|
"Failed to allocate %d bytes for full path\n",
|
|
nBufferLength));
|
|
return pFileInfo;
|
|
}
|
|
bFreeFullPath = TRUE;
|
|
|
|
cch = RtlGetFullPathName_U(pwszFilePath,
|
|
nBufferLength,
|
|
FullPath,
|
|
NULL);
|
|
|
|
assert(cch <= nBufferLength);
|
|
|
|
if (cch > nBufferLength || cch == 0) {
|
|
DBGPRINT((sdlError,
|
|
"SdbGetFileInfo",
|
|
"RtlGetFullPathName_U failed for \"%s\"\n",
|
|
pwszFilePath));
|
|
goto Done;
|
|
}
|
|
|
|
CreateFileInfo:
|
|
|
|
if (!bNoCache) {
|
|
pFileInfo = FindFileInfo(pContext, FullPath);
|
|
}
|
|
|
|
if (pFileInfo == NULL) {
|
|
if (hFile != INVALID_HANDLE_VALUE || pImageBase != NULL ||
|
|
RtlDoesFileExists_U(FullPath)) {
|
|
//
|
|
// The path does exist. Create a record of this file path.
|
|
//
|
|
FullPathU.Buffer = FullPath;
|
|
FullPathU.Length = (USHORT)cch;
|
|
FullPathU.MaximumLength = (USHORT)nBufferLength;
|
|
|
|
pFileInfo = CreateFileInfo(pContext,
|
|
FullPathU.Buffer,
|
|
FullPathU.Length / sizeof(WCHAR),
|
|
hFile,
|
|
pImageBase,
|
|
dwImageSize,
|
|
bNoCache);
|
|
}
|
|
}
|
|
|
|
Done:
|
|
if (FullPath != NULL && bFreeFullPath) {
|
|
STACK_FREE(FullPath);
|
|
}
|
|
|
|
return (PVOID)pFileInfo;
|
|
}
|
|
|
|
int
|
|
GetShimDbgLevel(
|
|
void
|
|
)
|
|
/*++
|
|
Return: The current debug level.
|
|
|
|
Desc: Checks the environment variable that controls the amount of
|
|
debug output.
|
|
--*/
|
|
{
|
|
UNICODE_STRING ShimDbgLevel;
|
|
INT iShimDebugLevel = 0;
|
|
NTSTATUS Status;
|
|
WCHAR Buffer[MAX_PATH];
|
|
ULONG ulValue;
|
|
|
|
ShimDbgLevel.Buffer = (PWCHAR)Buffer;
|
|
ShimDbgLevel.Length = 0;
|
|
ShimDbgLevel.MaximumLength = sizeof(Buffer);
|
|
|
|
Status = RtlQueryEnvironmentVariable_U(NULL,
|
|
&g_ustrShimDbgLevelVar,
|
|
&ShimDbgLevel);
|
|
if (NT_SUCCESS(Status)) {
|
|
//
|
|
// We have the debug level variable present. Parse it to see what
|
|
// is the value of this variable.
|
|
//
|
|
Status = RtlUnicodeStringToInteger(&ShimDbgLevel, 0, (PULONG)&ulValue);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
iShimDebugLevel = (int)ulValue;
|
|
}
|
|
}
|
|
|
|
return iShimDebugLevel;
|
|
}
|
|
|
|
//
|
|
// Functions that deal with APPCOMPAT_EXE_DATA
|
|
//
|
|
|
|
//
|
|
// These two functions should be used when passing APPCOMPAT_EXE_DATA structure
|
|
// across the boundaries of CreateProcess. Before passing the struct it should
|
|
// be "denormalized"; after -- normalized.
|
|
//
|
|
|
|
#define APPCOMPAT_EXE_DATA_MAGIC 0xAC0DEDAB
|
|
|
|
|
|
BOOL
|
|
SdbPackAppCompatData(
|
|
IN HSDB hSDB,
|
|
IN PSDBQUERYRESULT pSdbQuery,
|
|
OUT PVOID* ppData, // app compat data package
|
|
OUT LPDWORD pdwSize // data size
|
|
)
|
|
/*++
|
|
Return: TRUE on success, FALSE otherwise.
|
|
|
|
Desc: Packs the APPCOMPAT_EXE_DATA to be sent to the kernel in the context
|
|
of the process about to be started (and shimmed).
|
|
--*/
|
|
{
|
|
DWORD cbData;
|
|
PAPPCOMPAT_EXE_DATA pData = NULL;
|
|
|
|
assert(ppData != NULL);
|
|
assert(pdwSize != NULL);
|
|
|
|
if (pSdbQuery->atrExes[0] == TAGREF_NULL && pSdbQuery->atrLayers[0] == TAGREF_NULL) {
|
|
DBGPRINT((sdlError, "SdbPackAppCompatData", "Invalid arguments.\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
cbData = sizeof(APPCOMPAT_EXE_DATA);
|
|
|
|
//
|
|
// Allocate memory. Use this form of allocation so we know that
|
|
// we can free it from CreateProcess.
|
|
//
|
|
pData = (PAPPCOMPAT_EXE_DATA)RtlAllocateHeap(RtlProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
cbData);
|
|
if (pData == NULL) {
|
|
DBGPRINT((sdlError,
|
|
"SdbPackAppCompatData",
|
|
"Failed to allocate %d bytes.\n",
|
|
cbData));
|
|
return FALSE;
|
|
}
|
|
|
|
pData->cbSize = cbData;
|
|
pData->dwMagic = APPCOMPAT_EXE_DATA_MAGIC;
|
|
pData->dwFlags = pSdbQuery->dwFlags;
|
|
pData->trAppHelp = pSdbQuery->trAppHelp;
|
|
pData->dwLayerFlags = pSdbQuery->dwLayerFlags;
|
|
|
|
RtlCopyMemory(&pData->atrExes[0],
|
|
&pSdbQuery->atrExes[0],
|
|
sizeof(pSdbQuery->atrExes));
|
|
|
|
RtlCopyMemory(&pData->atrLayers[0],
|
|
&pSdbQuery->atrLayers[0],
|
|
sizeof(pSdbQuery->atrLayers));
|
|
|
|
StringCchCopy(pData->szShimEngine, CHARCOUNT(pData->szShimEngine), L"ShimEng.dll");
|
|
|
|
//
|
|
// Store custom sdbs
|
|
//
|
|
pData->dwDatabaseMap = pSdbQuery->dwCustomSDBMap;
|
|
|
|
if (pData->dwDatabaseMap) {
|
|
RtlCopyMemory(&pData->rgGuidDB[0],
|
|
&pSdbQuery->rgGuidDB[0],
|
|
sizeof(pData->rgGuidDB));
|
|
}
|
|
|
|
DBGPRINT((sdlInfo|sdlLogShimViewer,
|
|
"SdbPackAppCompatData",
|
|
"\n\tdwFlags 0x%X\n"
|
|
"\tdwMagic 0x%X\n"
|
|
"\ttrExe 0x%X\n"
|
|
"\ttrLayer 0x%X\n",
|
|
hSDB,
|
|
pData->dwFlags,
|
|
pData->dwMagic,
|
|
pData->atrExes[0],
|
|
pData->atrLayers[0]));
|
|
|
|
//
|
|
// Dump all the relevant entries
|
|
//
|
|
if (SDBCONTEXT_IS_INSTRUMENTED(hSDB)) {
|
|
DWORD dwMask;
|
|
DWORD dwIndex;
|
|
WCHAR szGuid[64];
|
|
PDB pdb;
|
|
LPCWSTR pszDescription;
|
|
SDBDATABASEINFO DbInfo;
|
|
|
|
if (pSdbQuery->dwCustomSDBMap) {
|
|
|
|
DBGPRINT((sdlInfo|sdlLogShimViewer, "SdbPackAppcompatData", "Database List\n", hSDB));
|
|
|
|
for (dwIndex = 0; dwIndex < ARRAYSIZE(pSdbQuery->rgGuidDB); ++dwIndex) {
|
|
|
|
dwMask = (1UL << dwIndex);
|
|
|
|
if (!(pSdbQuery->dwCustomSDBMap & dwMask)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Dump the guid
|
|
//
|
|
pszDescription = NULL;
|
|
|
|
pdb = SdbGetPDBFromGUID(hSDB, &pSdbQuery->rgGuidDB[dwIndex]);
|
|
if (pdb != NULL && SdbGetDatabaseInformation(pdb, &DbInfo)) {
|
|
pszDescription = DbInfo.pszDescription;
|
|
}
|
|
|
|
if (!SdbGUIDToString(&pSdbQuery->rgGuidDB[dwIndex],
|
|
szGuid,
|
|
CHARCOUNT(szGuid))) {
|
|
DBGPRINT((sdlError,
|
|
"SdbPackAppcompatData",
|
|
"Failed to convert GUID to string\n"));
|
|
}
|
|
|
|
DBGPRINT((sdlInfo|sdlLogShimViewer,
|
|
"SdbPackAppcompatData",
|
|
"0x%lx %s %s\n",
|
|
hSDB,
|
|
SDB_INDEX_TO_MASK(dwIndex),
|
|
szGuid,
|
|
(pszDescription ? pszDescription : L"")));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now dump all the matches
|
|
//
|
|
for (dwIndex = 0; dwIndex < ARRAYSIZE(pSdbQuery->atrExes); ++dwIndex) {
|
|
|
|
if (pSdbQuery->atrExes[dwIndex] == TAGREF_NULL) {
|
|
continue;
|
|
}
|
|
|
|
DBGPRINT((sdlInfo|sdlLogShimViewer,
|
|
"SdbPackAppcompatData",
|
|
"Exe 0x%.8lx\n",
|
|
hSDB,
|
|
pSdbQuery->atrExes[dwIndex]));
|
|
}
|
|
|
|
for (dwIndex = 0; dwIndex < ARRAYSIZE(pSdbQuery->atrLayers); ++dwIndex) {
|
|
|
|
if (pSdbQuery->atrLayers[dwIndex] == TAGREF_NULL) {
|
|
continue;
|
|
}
|
|
|
|
DBGPRINT((sdlInfo|sdlLogShimViewer,
|
|
"SdbPackAppcompatData",
|
|
"Layer 0x%.8lx\n",
|
|
hSDB,
|
|
pSdbQuery->atrLayers[dwIndex]));
|
|
}
|
|
}
|
|
|
|
*ppData = (PVOID)pData;
|
|
*pdwSize = cbData;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SdbUnpackAppCompatData(
|
|
IN HSDB hSDB,
|
|
IN LPCWSTR pwszExeName,
|
|
IN PVOID pAppCompatData,
|
|
OUT PSDBQUERYRESULT pSdbQuery
|
|
)
|
|
/*++
|
|
Return: TRUE on success, FALSE otherwise.
|
|
|
|
Desc: Unpacks the APPCOMPAT_EXE_DATA to extract the TAGREFs to be used
|
|
for the EXE that just started. This function is called from
|
|
LdrpInitializeProcess (in ntdll).
|
|
--*/
|
|
{
|
|
PAPPCOMPAT_EXE_DATA pData = (PAPPCOMPAT_EXE_DATA)pAppCompatData;
|
|
PSDBCONTEXT pSdbContext = (PSDBCONTEXT)hSDB;
|
|
|
|
if (pData == NULL) {
|
|
DBGPRINT((sdlError, "SdbUnpackAppCompatData", "Invalid parameter\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
if (pData->dwMagic != APPCOMPAT_EXE_DATA_MAGIC) {
|
|
DbgPrint("[SdbUnpackAppCompatData] Invalid magic 0x%x. Expected 0x%x\n",
|
|
pData->dwMagic,
|
|
APPCOMPAT_EXE_DATA_MAGIC);
|
|
return FALSE;
|
|
}
|
|
|
|
DBGPRINT((sdlInfo,
|
|
"SdbUnpackAppCompatData",
|
|
"Appcompat Data for \"%s\":\n"
|
|
"\tdwFlags 0x%X\n"
|
|
"\tdwMagic 0x%X\n"
|
|
"\ttrExe 0x%X\n"
|
|
"\ttrLayer 0x%X\n",
|
|
pwszExeName,
|
|
pData->dwFlags,
|
|
pData->dwMagic,
|
|
pData->atrExes[0],
|
|
pData->atrLayers[0]));
|
|
|
|
//
|
|
// We have no use for dwFlags so far.
|
|
//
|
|
|
|
//
|
|
// We should get rid of all the local sdbs in this context
|
|
//
|
|
if (pSdbContext->pdbLocal != NULL) {
|
|
SdbCloseLocalDatabase(hSDB);
|
|
}
|
|
|
|
RtlCopyMemory(&pSdbQuery->atrExes[0],
|
|
&pData->atrExes[0],
|
|
sizeof(pData->atrExes));
|
|
|
|
RtlCopyMemory(&pSdbQuery->atrLayers[0],
|
|
&pData->atrLayers[0],
|
|
sizeof(pData->atrLayers));
|
|
|
|
pSdbQuery->dwFlags = pData->dwFlags;
|
|
pSdbQuery->trAppHelp = pData->trAppHelp;
|
|
pSdbQuery->dwLayerFlags = pData->dwLayerFlags;
|
|
|
|
//
|
|
// See if we need to open local db
|
|
//
|
|
// We might need to be able to use local dbs --
|
|
// actual open on a local db will be performed by a tagref translation layer
|
|
//
|
|
if (pData->dwDatabaseMap) {
|
|
|
|
DWORD dwMask;
|
|
DWORD dwIndex;
|
|
PSDBENTRY pEntry;
|
|
DWORD dwCount = 2;
|
|
|
|
SdbpCleanupLocalDatabaseSupport(hSDB);
|
|
|
|
//
|
|
// Now introduce entries, one by one
|
|
//
|
|
for (dwIndex = 2; dwIndex < ARRAYSIZE(pSdbContext->rgSDB); ++dwIndex) {
|
|
|
|
dwMask = (1UL << dwIndex);
|
|
|
|
if (pData->dwDatabaseMap & dwMask) {
|
|
pEntry = SDBGETENTRY(hSDB, dwIndex);
|
|
pEntry->dwFlags = SDBENTRY_VALID_GUID;
|
|
pEntry->guidDB = pData->rgGuidDB[dwIndex];
|
|
dwCount = dwIndex;
|
|
}
|
|
}
|
|
|
|
pSdbContext->dwDatabaseMask |= (pData->dwDatabaseMap & SDB_CUSTOM_MASK);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
DWORD
|
|
SdbGetAppCompatDataSize(
|
|
IN PVOID pAppCompatData
|
|
)
|
|
/*++
|
|
Return: The size of the appcompat data.
|
|
|
|
Desc: Self explanatory.
|
|
--*/
|
|
{
|
|
PAPPCOMPAT_EXE_DATA pData = (PAPPCOMPAT_EXE_DATA)pAppCompatData;
|
|
|
|
if (pData == NULL) {
|
|
DBGPRINT((sdlError, "SdbGetAppCompatDataSize", "Invalid parameter\n"));
|
|
return 0;
|
|
}
|
|
|
|
if (pData->dwMagic != APPCOMPAT_EXE_DATA_MAGIC) {
|
|
DBGPRINT((sdlError,
|
|
"SdbGetAppCompatDataSize",
|
|
"Invalid magic 0x%x. Expected 0x%x\n",
|
|
pData->dwMagic,
|
|
APPCOMPAT_EXE_DATA_MAGIC));
|
|
return 0;
|
|
}
|
|
|
|
return pData->cbSize;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SdbpWriteBitsToFile(
|
|
IN LPCTSTR pszFile,
|
|
IN PBYTE pBuffer,
|
|
IN DWORD dwSize
|
|
)
|
|
/*++
|
|
Return: TRUE on success, FALSE otherwise.
|
|
|
|
Desc: Self explanatory.
|
|
--*/
|
|
{
|
|
LPCWSTR pszFileU = (LPCWSTR)pszFile;
|
|
UNICODE_STRING strFilePath;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
HANDLE hFile = NULL;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
LARGE_INTEGER liOffset;
|
|
|
|
//
|
|
// Create the file.
|
|
//
|
|
RtlInitUnicodeString(&strFilePath, pszFileU);
|
|
|
|
if (!RtlDosPathNameToNtPathName_U(strFilePath.Buffer,
|
|
&strFilePath,
|
|
NULL,
|
|
NULL)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpWriteBitsToFile",
|
|
"Failed to convert file name \"%s\" to NT path.\n",
|
|
pszFileU));
|
|
goto cleanup;
|
|
}
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&strFilePath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
status = NtCreateFile(&hFile,
|
|
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
0,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
0,
|
|
FILE_OVERWRITE_IF,
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0);
|
|
|
|
//
|
|
// RtlDosPathNameToNtPathName_U allocates memory for the
|
|
// unicode string. This is where we dump it.
|
|
//
|
|
RtlFreeUnicodeString(&strFilePath);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpWriteBitsToFile",
|
|
"Failed to create file \"%s\".\n",
|
|
pszFileU));
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// ...and write the bits to disk
|
|
//
|
|
liOffset.LowPart = 0;
|
|
liOffset.HighPart = 0;
|
|
|
|
status = NtWriteFile(hFile,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
(PVOID)pBuffer,
|
|
dwSize,
|
|
&liOffset,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpWriteBitsToFile",
|
|
"Failed 0x%x to write file \"%s\" to disk.\n",
|
|
status,
|
|
pszFileU));
|
|
goto cleanup;
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
cleanup:
|
|
|
|
if (hFile != NULL) {
|
|
NtClose(hFile);
|
|
}
|
|
|
|
return (status == STATUS_SUCCESS);
|
|
}
|
|
|
|
DWORD
|
|
SdbExpandEnvironmentStrings(
|
|
IN LPCWSTR lpSrc,
|
|
OUT LPWSTR lpDst,
|
|
IN DWORD nSize
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING Source;
|
|
UNICODE_STRING Destination;
|
|
ULONG Length;
|
|
DWORD iSize;
|
|
|
|
if (nSize > (MAXUSHORT >> 1) - 2) {
|
|
iSize = (MAXUSHORT >> 1) - 2;
|
|
} else {
|
|
iSize = nSize;
|
|
}
|
|
|
|
RtlInitUnicodeString(&Source, lpSrc);
|
|
|
|
Destination.Buffer = lpDst;
|
|
Destination.Length = 0;
|
|
Destination.MaximumLength = (USHORT)(iSize * sizeof( WCHAR ));
|
|
|
|
Length = 0;
|
|
|
|
Status = RtlExpandEnvironmentStrings_U(NULL,
|
|
&Source,
|
|
&Destination,
|
|
&Length);
|
|
|
|
if (NT_SUCCESS( Status ) || Status == STATUS_BUFFER_TOO_SMALL) {
|
|
return (Length / sizeof(WCHAR));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
BOOL
|
|
SDBAPI
|
|
SdbRegisterDatabaseEx(
|
|
IN LPCWSTR pszDatabasePath,
|
|
IN DWORD dwDatabaseType,
|
|
IN PULONGLONG pTimeStamp OPTIONAL
|
|
)
|
|
/*++
|
|
Registers any given database so that it is "known" to our database lookup apis
|
|
|
|
--*/
|
|
{
|
|
PSDBDATABASEINFO pDbInfo = NULL;
|
|
BOOL bReturn = FALSE;
|
|
UNICODE_STRING ustrPath;
|
|
UNICODE_STRING ustrFullPath = { 0 };
|
|
BOOL bFreeFullPath = FALSE;
|
|
BOOL bExpandSZ = FALSE;
|
|
UNICODE_STRING ustrKey;
|
|
UNICODE_STRING ustrFullKey;
|
|
UNICODE_STRING ustrDatabaseID = { 0 };
|
|
WCHAR wszFullKey[MAX_PATH];
|
|
NTSTATUS Status;
|
|
HANDLE KeyHandleAppcompat = NULL;
|
|
HANDLE KeyHandle = NULL;
|
|
ULONG CreateDisposition;
|
|
ULONG PathLength = 0;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
FILETIME TimeStamp;
|
|
LARGE_INTEGER liTimeStamp;
|
|
KEY_WRITE_TIME_INFORMATION KeyWriteTimeInfo;
|
|
|
|
//
|
|
// See if we need to expand some strings...
|
|
//
|
|
if (_tcschr(pszDatabasePath, TEXT('%')) != NULL) {
|
|
|
|
bExpandSZ = TRUE;
|
|
|
|
RtlInitUnicodeString(&ustrPath, pszDatabasePath);
|
|
|
|
Status = RtlExpandEnvironmentStrings_U(NULL,
|
|
&ustrPath,
|
|
&ustrFullPath,
|
|
&PathLength);
|
|
|
|
if (Status != STATUS_BUFFER_TOO_SMALL) {
|
|
DBGPRINT((sdlError,
|
|
"SdbRegisterDatabase",
|
|
"Failed to expand environment strings for \"%s\" Status 0x%lx\n",
|
|
pszDatabasePath,
|
|
Status));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Allocate the length we need
|
|
//
|
|
ustrFullPath.Buffer = (WCHAR*)SdbAlloc(PathLength);
|
|
|
|
if (ustrFullPath.Buffer == NULL) {
|
|
DBGPRINT((sdlError,
|
|
"SdbRegisterDatabase",
|
|
"Failed to allocate 0x%lx bytes for the path buffer \"%s\"\n",
|
|
PathLength,
|
|
pszDatabasePath));
|
|
return FALSE;
|
|
}
|
|
|
|
ustrFullPath.MaximumLength = (USHORT)PathLength;
|
|
ustrFullPath.Length = 0;
|
|
bFreeFullPath = TRUE;
|
|
|
|
Status = RtlExpandEnvironmentStrings_U(NULL,
|
|
&ustrPath,
|
|
&ustrFullPath,
|
|
&PathLength);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbRegisterDatabase",
|
|
"Failed to expand environment strings for \"%s\" Status 0x%lx\n",
|
|
pszDatabasePath,
|
|
Status));
|
|
goto HandleError;
|
|
}
|
|
|
|
} else {
|
|
RtlInitUnicodeString(&ustrFullPath, pszDatabasePath);
|
|
}
|
|
|
|
if (!SdbGetDatabaseInformationByName(ustrFullPath.Buffer, &pDbInfo)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbRegisterDatabase",
|
|
"Cannot obtain database information for \"%s\"\n",
|
|
pszDatabasePath));
|
|
goto HandleError;
|
|
}
|
|
|
|
if (!(pDbInfo->dwFlags & DBINFO_GUID_VALID)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbRegisterDatabase",
|
|
"Cannot register database with no id \"%s\"\n",
|
|
pszDatabasePath));
|
|
goto HandleError;
|
|
}
|
|
|
|
Status = GUID_TO_UNICODE_STRING(&pDbInfo->guidDB, &ustrDatabaseID);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbRegisterDatabase",
|
|
"Cannot convert guid to unicode string status 0x%lx\n",
|
|
Status));
|
|
goto HandleError;
|
|
}
|
|
|
|
//
|
|
// Now that we have database information - create entry
|
|
//
|
|
ustrFullKey.Length = 0;
|
|
ustrFullKey.MaximumLength = sizeof(wszFullKey);
|
|
ustrFullKey.Buffer = wszFullKey;
|
|
|
|
RtlAppendUnicodeToString(&ustrFullKey, APPCOMPAT_KEY_PATH_MACHINE_INSTALLEDSDB);
|
|
RtlAppendUnicodeToString(&ustrFullKey, L"\\");
|
|
RtlAppendUnicodeStringToString(&ustrFullKey, &ustrDatabaseID);
|
|
|
|
FREE_GUID_STRING(&ustrDatabaseID);
|
|
|
|
//
|
|
// All done, now create key - first make sure that appcompat key path machine exists...
|
|
//
|
|
RtlInitUnicodeString(&ustrKey, APPCOMPAT_KEY_PATH_MACHINE_INSTALLEDSDB);
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&ustrKey,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtCreateKey(&KeyHandleAppcompat,
|
|
STANDARD_RIGHTS_WRITE |
|
|
KEY_QUERY_VALUE |
|
|
KEY_ENUMERATE_SUB_KEYS |
|
|
KEY_SET_VALUE |
|
|
KEY_CREATE_SUB_KEY|
|
|
SdbpGetWow64Flag(),
|
|
&ObjectAttributes,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
&CreateDisposition);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbRegisterDatabase",
|
|
"Failed to create key \"%s\" status = 0x%lx\n",
|
|
ustrKey.Buffer,
|
|
Status));
|
|
goto HandleError;
|
|
}
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&ustrFullKey,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtCreateKey(&KeyHandle,
|
|
STANDARD_RIGHTS_WRITE |
|
|
KEY_QUERY_VALUE |
|
|
KEY_ENUMERATE_SUB_KEYS |
|
|
KEY_SET_VALUE |
|
|
KEY_CREATE_SUB_KEY|
|
|
SdbpGetWow64Flag(),
|
|
&ObjectAttributes,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
&CreateDisposition);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbRegisterDatabase",
|
|
"Failed to create key \"%s\" Status 0x%x\n",
|
|
ustrFullKey.Buffer,
|
|
Status));
|
|
goto HandleError;
|
|
}
|
|
|
|
//
|
|
// We have an open key, we shall set these values:
|
|
//
|
|
Status = NtSetValueKey(KeyHandle,
|
|
(PUNICODE_STRING)&g_ustrDatabasePath,
|
|
0,
|
|
bExpandSZ ? REG_EXPAND_SZ : REG_SZ,
|
|
(PVOID)pszDatabasePath,
|
|
(ULONG)(_tcslen(pszDatabasePath) + 1) * sizeof(*pszDatabasePath));
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbRegisterDatabase",
|
|
"Failed to set value \"%s\" to \"%s\" Status 0x%lx\n",
|
|
g_ustrDatabasePath.Buffer,
|
|
pszDatabasePath,
|
|
Status));
|
|
goto HandleError;
|
|
}
|
|
|
|
Status = NtSetValueKey(KeyHandle,
|
|
(PUNICODE_STRING)&g_ustrDatabaseType,
|
|
0,
|
|
REG_DWORD,
|
|
(PVOID)&dwDatabaseType,
|
|
sizeof(dwDatabaseType));
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbRegisterDatabase",
|
|
"Failed to set value \"%s\" to 0x%lx Status 0x%lx\n",
|
|
g_ustrDatabaseType.Buffer,
|
|
dwDatabaseType, Status));
|
|
goto HandleError;
|
|
}
|
|
|
|
if (pDbInfo->pszDescription != NULL) {
|
|
Status = NtSetValueKey(KeyHandle,
|
|
(PUNICODE_STRING)&g_ustrDatabaseDescription,
|
|
0,
|
|
REG_SZ,
|
|
(PVOID)pDbInfo->pszDescription,
|
|
(ULONG)(_tcslen(pDbInfo->pszDescription) + 1) * sizeof(WCHAR));
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbRegisterDatabase",
|
|
"Failed to set value \"%s\" to 0x%lx Status 0x%lx\n",
|
|
g_ustrDatabaseDescription.Buffer,
|
|
pDbInfo->pszDescription,
|
|
Status));
|
|
goto HandleError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Finally, set the date/time stamp explicitly as a value
|
|
//
|
|
if (pTimeStamp == NULL) {
|
|
GetSystemTimeAsFileTime(&TimeStamp);
|
|
liTimeStamp.LowPart = TimeStamp.dwLowDateTime;
|
|
liTimeStamp.HighPart = TimeStamp.dwHighDateTime;
|
|
} else {
|
|
liTimeStamp.QuadPart = *pTimeStamp;
|
|
}
|
|
|
|
Status = NtSetValueKey(KeyHandle,
|
|
(PUNICODE_STRING)&g_ustrInstallTimeStamp,
|
|
0,
|
|
REG_QWORD,
|
|
(PVOID)&liTimeStamp.QuadPart,
|
|
sizeof(liTimeStamp.QuadPart));
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbRegisterDatabase",
|
|
"Failed to set value \"%s\" Status 0x%lx\n",
|
|
g_ustrInstallTimeStamp.Buffer,
|
|
Status));
|
|
goto HandleError;
|
|
}
|
|
|
|
//
|
|
// Make the value we set into the timestamp MATCH the last write date/time stamp
|
|
// on the key itself (just to be consistent)
|
|
//
|
|
KeyWriteTimeInfo.LastWriteTime.QuadPart = liTimeStamp.QuadPart;
|
|
|
|
Status = NtSetInformationKey(KeyHandle,
|
|
KeyWriteTimeInformation,
|
|
&KeyWriteTimeInfo,
|
|
sizeof(KeyWriteTimeInfo));
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbRegisterDatabase",
|
|
"Failed to set last write time on key Status 0x%lx\n",
|
|
Status));
|
|
goto HandleError;
|
|
}
|
|
|
|
bReturn = TRUE;
|
|
|
|
HandleError:
|
|
|
|
if (pDbInfo != NULL) {
|
|
SdbFreeDatabaseInformation(pDbInfo);
|
|
}
|
|
|
|
if (bFreeFullPath && ustrFullPath.Buffer != NULL) {
|
|
SdbFree(ustrFullPath.Buffer);
|
|
}
|
|
|
|
if (KeyHandle != NULL) {
|
|
NtClose(KeyHandle);
|
|
}
|
|
|
|
if (KeyHandleAppcompat != NULL) {
|
|
NtClose(KeyHandleAppcompat);
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
BOOL
|
|
SDBAPI
|
|
SdbRegisterDatabase(
|
|
IN LPCWSTR pszDatabasePath,
|
|
IN DWORD dwDatabaseType
|
|
)
|
|
{
|
|
return SdbRegisterDatabaseEx(pszDatabasePath, dwDatabaseType, NULL);
|
|
}
|
|
|
|
|
|
BOOL
|
|
SDBAPI
|
|
SdbUnregisterDatabase(
|
|
IN GUID* pguidDB
|
|
)
|
|
/*++
|
|
Unregisters a database so it's no longer available.
|
|
|
|
|
|
--*/
|
|
{
|
|
BOOL bReturn = FALSE;
|
|
UNICODE_STRING ustrFullKey;
|
|
UNICODE_STRING ustrDatabaseID = { 0 };
|
|
WCHAR wszFullKey[MAX_PATH];
|
|
NTSTATUS Status;
|
|
HANDLE KeyHandle = NULL;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
|
|
Status = GUID_TO_UNICODE_STRING(pguidDB, &ustrDatabaseID);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbUnregisterDatabase",
|
|
"Cannot convert guid to unicode string status 0x%lx\n",
|
|
Status));
|
|
goto HandleError;
|
|
}
|
|
|
|
//
|
|
// Now that we have database information - remove entry
|
|
//
|
|
ustrFullKey.Length = 0;
|
|
ustrFullKey.MaximumLength = sizeof(wszFullKey);
|
|
ustrFullKey.Buffer = wszFullKey;
|
|
|
|
RtlAppendUnicodeToString(&ustrFullKey, APPCOMPAT_KEY_PATH_MACHINE_INSTALLEDSDB);
|
|
RtlAppendUnicodeToString(&ustrFullKey, L"\\");
|
|
RtlAppendUnicodeStringToString(&ustrFullKey, &ustrDatabaseID);
|
|
|
|
FREE_GUID_STRING(&ustrDatabaseID);
|
|
|
|
//
|
|
// All done, now delete key
|
|
//
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&ustrFullKey,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenKey(&KeyHandle, DELETE|SdbpGetWow64Flag(), &ObjectAttributes);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbUnregisterDatabase",
|
|
"Failed to open key \"%s\" Status 0x%x\n",
|
|
ustrFullKey.Buffer,
|
|
Status));
|
|
goto HandleError;
|
|
}
|
|
|
|
//
|
|
// We have an open key, now delete it
|
|
//
|
|
Status = NtDeleteKey(KeyHandle);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbUnregisterDatabase",
|
|
"Failed to delete key \"%s\" Status 0x%x\n",
|
|
ustrFullKey.Buffer,
|
|
Status));
|
|
goto HandleError;
|
|
}
|
|
|
|
bReturn = TRUE;
|
|
|
|
HandleError:
|
|
|
|
if (KeyHandle != NULL) {
|
|
NtClose(KeyHandle);
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
BOOL
|
|
SdbpDoesFileExistNTPath(
|
|
LPCWSTR lpwszFileName
|
|
)
|
|
{
|
|
OBJECT_ATTRIBUTES Obja;
|
|
UNICODE_STRING NtFileName;
|
|
NTSTATUS Status;
|
|
BOOL ReturnValue = FALSE;
|
|
FILE_BASIC_INFORMATION BasicInfo;
|
|
|
|
RtlInitUnicodeString(&NtFileName, lpwszFileName);
|
|
|
|
InitializeObjectAttributes(&Obja,
|
|
&NtFileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
//
|
|
// Query the file's attributes. Note that the file cannot simply be opened
|
|
// to determine whether or not it exists, as the NT LanMan redirector lies
|
|
// on NtOpenFile to a Lan Manager server because it does not actually open
|
|
// the file until an operation is performed on it. We don't care since net is not
|
|
// even involved -- we always check for LOCAL files
|
|
//
|
|
Status = NtQueryAttributesFile(&Obja, &BasicInfo);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
if (Status == STATUS_SHARING_VIOLATION || Status == STATUS_ACCESS_DENIED) {
|
|
ReturnValue = TRUE;
|
|
} else {
|
|
ReturnValue = FALSE;
|
|
}
|
|
} else {
|
|
ReturnValue = TRUE;
|
|
}
|
|
|
|
return ReturnValue;
|
|
}
|
|
|
|
BOOL
|
|
SDBAPI
|
|
SdbGetStandardDatabaseGUID(
|
|
IN DWORD dwDatabaseType,
|
|
OUT GUID* pGuidDB
|
|
)
|
|
{
|
|
GUID const * pguid = NULL;
|
|
|
|
if (!(dwDatabaseType & SDB_DATABASE_MAIN)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbGetStandardDatabaseGUID",
|
|
"Cannot obtain database guid for databases other than main\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
switch (dwDatabaseType & SDB_DATABASE_TYPE_MASK) {
|
|
|
|
case SDB_DATABASE_MAIN_DRIVERS:
|
|
pguid = &GUID_DRVMAIN_SDB;
|
|
break;
|
|
|
|
case SDB_DATABASE_MAIN_DETAILS:
|
|
pguid = &GUID_APPHELP_SDB;
|
|
break;
|
|
|
|
case SDB_DATABASE_MAIN_MSI:
|
|
pguid = &GUID_MSIMAIN_SDB;
|
|
break;
|
|
|
|
case SDB_DATABASE_MAIN_SHIM:
|
|
pguid = &GUID_SYSMAIN_SDB;
|
|
break;
|
|
}
|
|
|
|
if (pguid != NULL) {
|
|
if (pGuidDB != NULL) {
|
|
RtlCopyMemory(pGuidDB, pguid, sizeof(*pGuidDB));
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
DWORD
|
|
SdbpGetStandardDatabasePath(
|
|
IN HSDB hSDB,
|
|
IN DWORD dwDatabaseType,
|
|
IN DWORD dwFlags, // specify HID_DOS_PATHS for dos paths
|
|
OUT LPTSTR pszDatabasePath,
|
|
IN DWORD dwBufferSize // in tchars
|
|
)
|
|
{
|
|
|
|
PSDBCONTEXT pContext = (PSDBCONTEXT)hSDB;
|
|
TCHAR szAppPatch[MAX_PATH];
|
|
LPCTSTR pszDatabase = NULL;
|
|
LPCTSTR pszDir = NULL;
|
|
LCID lcid;
|
|
int nLen;
|
|
size_t cchRemaining;
|
|
|
|
if (dwFlags & HID_DOS_PATHS) {
|
|
SdbpGetAppPatchDir(hSDB, szAppPatch, CHARCOUNT(szAppPatch));
|
|
} else {
|
|
if (pContext && pContext->uExeType == IMAGE_FILE_MACHINE_IA64) {
|
|
StringCchCopy(szAppPatch,
|
|
CHARCOUNT(szAppPatch),
|
|
TEXT("\\SystemRoot\\AppPatch\\IA64"));
|
|
} else {
|
|
StringCchCopy(szAppPatch,
|
|
CHARCOUNT(szAppPatch),
|
|
TEXT("\\SystemRoot\\AppPatch"));
|
|
}
|
|
}
|
|
|
|
if (!(dwDatabaseType & SDB_DATABASE_MAIN)) { // cannot get it for non-main d
|
|
return 0;
|
|
}
|
|
|
|
pszDir = szAppPatch;
|
|
|
|
switch (dwDatabaseType & SDB_DATABASE_TYPE_MASK) {
|
|
|
|
case SDB_DATABASE_MAIN_DRIVERS:
|
|
pszDatabase = TEXT("drvmain.sdb");
|
|
break;
|
|
|
|
case SDB_DATABASE_MAIN_MSI:
|
|
pszDatabase = TEXT("msimain.sdb");
|
|
break;
|
|
|
|
case SDB_DATABASE_MAIN_SHIM:
|
|
pszDatabase = TEXT("sysmain.sdb");
|
|
break;
|
|
|
|
case SDB_DATABASE_MAIN_TEST:
|
|
pszDatabase = TEXT("systest.sdb");
|
|
break;
|
|
|
|
case SDB_DATABASE_MAIN_DETAILS:
|
|
//
|
|
// The code below is not operational on nt4 yet. It would prevent sdbapiu from
|
|
// working properly as GetUserDefaultUILanguage does not exist.
|
|
//
|
|
#ifndef WIN32U_MODE
|
|
|
|
lcid = GetUserDefaultUILanguage();
|
|
|
|
if (lcid != MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)) {
|
|
|
|
BOOL bFoundDB = FALSE;
|
|
TCHAR szTemp[MAX_PATH];
|
|
|
|
|
|
//
|
|
// When doing this, always remember which apppatch we are putting up here
|
|
// we need to form our own private apppatch
|
|
//
|
|
StringCchPrintfEx(szTemp,
|
|
CHARCOUNT(szTemp),
|
|
NULL,
|
|
&cchRemaining,
|
|
0,
|
|
TEXT("%s\\MUI\\%04x\\apphelp.sdb"),
|
|
szAppPatch,
|
|
lcid);
|
|
|
|
nLen = CHARCOUNT(szTemp) - (int)cchRemaining;
|
|
|
|
if (nLen > 0) {
|
|
if (dwFlags & HID_DOS_PATHS) {
|
|
bFoundDB = RtlDoesFileExists_U(szTemp);
|
|
} else {
|
|
bFoundDB = SdbpDoesFileExistNTPath(szTemp);
|
|
}
|
|
}
|
|
|
|
if (bFoundDB) {
|
|
StringCchPrintf(szTemp, CHARCOUNT(szTemp), TEXT("MUI\\%04x\\apphelp.sdb"), lcid);
|
|
|
|
pszDatabase = szTemp;
|
|
}
|
|
}
|
|
#else
|
|
UNREFERENCED_PARAMETER(lcid);
|
|
#endif
|
|
if (pszDatabase == NULL) {
|
|
|
|
//
|
|
// Standard case
|
|
//
|
|
pszDatabase = TEXT("apphelp.sdb");
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (pszDatabase == NULL) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpGetStandardDatabasePath",
|
|
"Cannot get the path for database type 0x%lx\n",
|
|
dwDatabaseType));
|
|
return 0;
|
|
}
|
|
|
|
if (pszDatabasePath != NULL) {
|
|
StringCchPrintfEx(pszDatabasePath,
|
|
(int)dwBufferSize,
|
|
NULL,
|
|
&cchRemaining,
|
|
0,
|
|
TEXT("%s\\%s"),
|
|
pszDir,
|
|
pszDatabase);
|
|
|
|
nLen = (int)dwBufferSize - (int)cchRemaining;
|
|
} else {
|
|
nLen = -1;
|
|
}
|
|
|
|
if (nLen < 0) {
|
|
if (pszDatabasePath != NULL) {
|
|
DBGPRINT((sdlError, "SdbpGetStandardDatabasePath", "Path is too long\n"));
|
|
}
|
|
|
|
//
|
|
// Calc expected length. One for term null char and one for "\\" ...
|
|
//
|
|
nLen = (pszDir == NULL ? 0 : (int)_tclen(pszDir)) + (int)_tcslen(pszDatabase) + 1 + 1;
|
|
}
|
|
|
|
return (DWORD)nLen;
|
|
}
|
|
|
|
|
|
DWORD
|
|
SDBAPI
|
|
SdbResolveDatabase(
|
|
IN HSDB hSDB,
|
|
IN GUID* pguidDB, // pointer to the database guid to resolve
|
|
OUT LPDWORD lpdwDatabaseType, // optional pointer to the database type
|
|
OUT LPTSTR pszDatabasePath, // optional pointer to the database path
|
|
IN DWORD dwBufferSize // size of the buffer pszDatabasePath
|
|
)
|
|
{
|
|
WCHAR wszFullKey[MAX_PATH];
|
|
UNICODE_STRING ustrFullKey;
|
|
UNICODE_STRING ustrKey;
|
|
UNICODE_STRING ustrPath;
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE KeyHandle = NULL;
|
|
ULONG KeyValueBuffer[MAX_PATH];
|
|
ULONG KeyValueLength = 0;
|
|
ULONG PathValueLength = 0; // this is return result
|
|
DWORD dwDatabaseType = 0;
|
|
int i;
|
|
|
|
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
|
|
|
|
static struct tagSTDGUIDSDB {
|
|
const GUID* pGuid;
|
|
DWORD dwDatabaseType;
|
|
} guidStd[] = {
|
|
{ &GUID_SYSMAIN_SDB, SDB_DATABASE_MAIN_SHIM },
|
|
{ &GUID_MSIMAIN_SDB, SDB_DATABASE_MAIN_MSI },
|
|
{ &GUID_DRVMAIN_SDB, SDB_DATABASE_MAIN_DRIVERS },
|
|
{ &GUID_APPHELP_SDB, SDB_DATABASE_MAIN_DETAILS },
|
|
{ &GUID_SYSTEST_SDB, SDB_DATABASE_MAIN_TEST }
|
|
};
|
|
|
|
for (i = 0; i < ARRAYSIZE(guidStd); ++i) {
|
|
if (!memcmp(guidStd[i].pGuid, pguidDB, sizeof(GUID))) {
|
|
dwDatabaseType = guidStd[i].dwDatabaseType;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i < ARRAYSIZE(guidStd)) {
|
|
DWORD dwLen;
|
|
|
|
dwLen = SdbpGetStandardDatabasePath(hSDB,
|
|
dwDatabaseType,
|
|
HID_DOS_PATHS,
|
|
pszDatabasePath,
|
|
dwBufferSize);
|
|
if (lpdwDatabaseType != NULL) {
|
|
*lpdwDatabaseType = dwDatabaseType;
|
|
}
|
|
return dwLen;
|
|
}
|
|
|
|
ustrFullKey.Buffer = wszFullKey;
|
|
ustrFullKey.MaximumLength = sizeof(wszFullKey);
|
|
ustrFullKey.Length = 0;
|
|
|
|
RtlAppendUnicodeToString(&ustrFullKey, APPCOMPAT_KEY_PATH_MACHINE_INSTALLEDSDB);
|
|
RtlAppendUnicodeToString(&ustrFullKey, L"\\");
|
|
|
|
Status = GUID_TO_UNICODE_STRING(pguidDB, &ustrKey);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbResolveDatabase",
|
|
"Failed to convert guid to string status 0x%lx\n",
|
|
Status));
|
|
goto HandleError; // 0 means error
|
|
}
|
|
|
|
RtlAppendUnicodeStringToString(&ustrFullKey, &ustrKey);
|
|
|
|
FREE_GUID_STRING(&ustrKey);
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&ustrFullKey,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenKey(&KeyHandle, GENERIC_READ|SdbpGetWow64Flag(), &ObjectAttributes);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbResolveDatabase",
|
|
"Failed to open Key \"%s\" Status 0x%lx\n",
|
|
ustrFullKey.Buffer,
|
|
Status));
|
|
goto HandleError;
|
|
}
|
|
|
|
//
|
|
// Now since we were able to open the key -- database was found, recover path and type
|
|
//
|
|
KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)KeyValueBuffer;
|
|
|
|
Status = NtQueryValueKey(KeyHandle,
|
|
(PUNICODE_STRING)&g_ustrDatabasePath,
|
|
KeyValuePartialInformation,
|
|
KeyValueInformation,
|
|
sizeof(KeyValueBuffer),
|
|
&KeyValueLength);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbResolveDatabase",
|
|
"Failed trying to query value \"%s\" Status 0x%lx\n",
|
|
g_ustrDatabasePath.Buffer,
|
|
Status));
|
|
goto HandleError;
|
|
}
|
|
|
|
|
|
switch (KeyValueInformation->Type) {
|
|
|
|
case REG_SZ:
|
|
|
|
PathValueLength = KeyValueInformation->DataLength;
|
|
|
|
if (pszDatabasePath == NULL || dwBufferSize * sizeof(WCHAR) < PathValueLength) {
|
|
DBGPRINT((sdlWarning,
|
|
"SdbResolveDatabase",
|
|
"Insufficient buffer for the database path\n"));
|
|
goto HandleError;
|
|
}
|
|
|
|
RtlMoveMemory(pszDatabasePath, KeyValueInformation->Data, PathValueLength);
|
|
break;
|
|
|
|
case REG_EXPAND_SZ:
|
|
|
|
ustrKey.Buffer = (PWSTR)&KeyValueInformation->Data;
|
|
ustrKey.Length = (USHORT)(KeyValueInformation->DataLength - sizeof(UNICODE_NULL));
|
|
ustrKey.MaximumLength = (USHORT)KeyValueInformation->DataLength;
|
|
|
|
ustrPath.Buffer = pszDatabasePath;
|
|
ustrPath.Length = 0;
|
|
ustrPath.MaximumLength = (USHORT)(dwBufferSize * sizeof(WCHAR));
|
|
|
|
Status = RtlExpandEnvironmentStrings_U(NULL, &ustrKey, &ustrPath, &PathValueLength);
|
|
|
|
if (Status == STATUS_BUFFER_TOO_SMALL) {
|
|
DBGPRINT((sdlWarning, "SdbResolveDatabase", "Insufficient buffer to expand path\n"));
|
|
goto HandleError;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
PathValueLength = 0;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
DBGPRINT((sdlError,
|
|
"SdbResolveDatabase",
|
|
"Wrong key type 0x%lx\n",
|
|
KeyValueInformation->Type));
|
|
|
|
PathValueLength = 0;
|
|
goto HandleError;
|
|
break;
|
|
}
|
|
|
|
if (lpdwDatabaseType != NULL) {
|
|
|
|
//
|
|
// Query for the database type
|
|
//
|
|
Status = NtQueryValueKey(KeyHandle,
|
|
(PUNICODE_STRING)&g_ustrDatabaseType,
|
|
KeyValuePartialInformation,
|
|
KeyValueInformation,
|
|
sizeof(KeyValueBuffer),
|
|
&KeyValueLength);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
if (KeyValueInformation->Type != REG_DWORD) {
|
|
//
|
|
// bummer, get out -- wrong type
|
|
//
|
|
DBGPRINT((sdlError,
|
|
"SdbResolveDatabase",
|
|
"Wrong database type - value type 0x%lx\n",
|
|
KeyValueInformation->Type));
|
|
PathValueLength = 0;
|
|
goto HandleError;
|
|
}
|
|
|
|
//
|
|
// Else, we get the value
|
|
//
|
|
RtlMoveMemory(lpdwDatabaseType, &KeyValueInformation->Data, sizeof(*lpdwDatabaseType));
|
|
|
|
} else {
|
|
*lpdwDatabaseType = 0; // we do not have any value then
|
|
}
|
|
}
|
|
|
|
HandleError:
|
|
|
|
if (KeyHandle != NULL) {
|
|
NtClose(KeyHandle);
|
|
}
|
|
|
|
return PathValueLength / sizeof(WCHAR);
|
|
}
|
|
|
|
LPTSTR
|
|
SDBAPI
|
|
SdbGetLayerName(
|
|
IN HSDB hSDB,
|
|
IN TAGREF trLayer
|
|
)
|
|
/*++
|
|
Return: BUGBUG
|
|
|
|
Desc: BUGBUG
|
|
--*/
|
|
{
|
|
PDB pdb;
|
|
TAGID tiLayer, tiName;
|
|
LPTSTR pwszName;
|
|
|
|
if (!SdbTagRefToTagID(hSDB, trLayer, &pdb, &tiLayer)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbGetLayerName",
|
|
"Failed to get tag id from tag ref 0x%lx\n",
|
|
trLayer));
|
|
return NULL;
|
|
}
|
|
|
|
tiName = SdbFindFirstTag(pdb, tiLayer, TAG_NAME);
|
|
|
|
if (tiName == TAGID_NULL) {
|
|
DBGPRINT((sdlError,
|
|
"SdbGetLayerName",
|
|
"Failed to get the name tag id for 0x%lx\n",
|
|
tiName));
|
|
return NULL;
|
|
}
|
|
|
|
pwszName = SdbGetStringTagPtr(pdb, tiName);
|
|
|
|
if (pwszName == NULL) {
|
|
DBGPRINT((sdlError,
|
|
"SdbGetLayerName",
|
|
"Cannot read the name of the layer tag id 0x%lx\n",
|
|
tiName));
|
|
}
|
|
|
|
return pwszName;
|
|
}
|
|
|
|
|
|
/*++
|
|
SdbBuildComapatEnvVar
|
|
|
|
This function builds the environment variable necessary for
|
|
Compat Layer propagation
|
|
|
|
--*/
|
|
|
|
DWORD
|
|
SDBAPI
|
|
SdbBuildCompatEnvVariables(
|
|
IN HSDB hSDB,
|
|
IN SDBQUERYRESULT* psdbQuery,
|
|
IN DWORD dwFlags,
|
|
IN LPCWSTR pwszParentEnv OPTIONAL, // environment which contains vars
|
|
// that we shall inherit from
|
|
OUT LPWSTR pBuffer,
|
|
IN DWORD cchSize, // size of the buffer in tchars
|
|
OUT LPDWORD lpdwShimsCount OPTIONAL
|
|
)
|
|
{
|
|
int i;
|
|
TCHAR szFullEnvVar[MAX_PATH];
|
|
TAGREF trShimRef;
|
|
INT nCountLayers = 0;
|
|
DWORD dwSizeRequired;
|
|
size_t cchRemaining;
|
|
|
|
//
|
|
// Count the DLLs that trLayer uses, and put together the environment variable
|
|
//
|
|
szFullEnvVar[0] = TEXT('\0');
|
|
|
|
//
|
|
// Make sure to propagate the flags.
|
|
//
|
|
if (!(dwFlags & SBCE_ADDITIVE)) {
|
|
StringCchCat(szFullEnvVar, CHARCOUNT(szFullEnvVar), TEXT("!"));
|
|
}
|
|
|
|
if (dwFlags & SBCE_INCLUDESYSTEMEXES) {
|
|
StringCchCat(szFullEnvVar, CHARCOUNT(szFullEnvVar), TEXT("#"));
|
|
}
|
|
|
|
for (i = 0; i < SDB_MAX_LAYERS && psdbQuery->atrLayers[i] != TAGREF_NULL; ++i) {
|
|
WCHAR* pszEnvVar;
|
|
|
|
//
|
|
// Get the environment var and tack it onto the full string
|
|
//
|
|
pszEnvVar = SdbGetLayerName(hSDB, psdbQuery->atrLayers[i]);
|
|
|
|
if (pszEnvVar) {
|
|
if (nCountLayers) {
|
|
StringCchCat(szFullEnvVar, CHARCOUNT(szFullEnvVar), TEXT(" "));
|
|
}
|
|
++nCountLayers;
|
|
|
|
StringCchCat(szFullEnvVar, CHARCOUNT(szFullEnvVar), pszEnvVar);
|
|
}
|
|
|
|
if (lpdwShimsCount != NULL) {
|
|
|
|
//
|
|
// Keep counting the dlls.
|
|
//
|
|
trShimRef = SdbFindFirstTagRef(hSDB, psdbQuery->atrLayers[i], TAG_SHIM_REF);
|
|
|
|
while (trShimRef != TAGREF_NULL) {
|
|
(*lpdwShimsCount)++;
|
|
trShimRef = SdbFindNextTagRef(hSDB, psdbQuery->atrLayers[i], trShimRef);
|
|
}
|
|
}
|
|
}
|
|
|
|
dwSizeRequired = (DWORD)(_tcslen(szFullEnvVar) + _tcslen(g_szCompatLayer) + 3);
|
|
|
|
if (cchSize < dwSizeRequired || pBuffer == NULL) {
|
|
//
|
|
// need g_szCompatLayer + '=' + \0 + szFullEnvVar space in the buffer
|
|
//
|
|
return dwSizeRequired;
|
|
}
|
|
|
|
StringCchPrintfEx(pBuffer,
|
|
cchSize,
|
|
NULL,
|
|
&cchRemaining,
|
|
0,
|
|
TEXT("%s=%s%c"),
|
|
g_szCompatLayer,
|
|
szFullEnvVar,
|
|
TEXT('\0'));
|
|
|
|
return (DWORD)cchSize - (int)cchRemaining;
|
|
|
|
UNREFERENCED_PARAMETER(pwszParentEnv);
|
|
}
|
|
|
|
DWORD
|
|
SdbpGetProcessorArchitecture(
|
|
IN USHORT uExeType // executable's image type
|
|
)
|
|
{
|
|
static DWORD dwProcessorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN;
|
|
|
|
SYSTEM_PROCESSOR_INFORMATION ProcessorInfo;
|
|
NTSTATUS Status;
|
|
|
|
if (dwProcessorArchitecture == PROCESSOR_ARCHITECTURE_UNKNOWN) {
|
|
|
|
Status = NtQuerySystemInformation(SystemProcessorInformation,
|
|
&ProcessorInfo,
|
|
sizeof(ProcessorInfo),
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbpGetProcessorArchitecture",
|
|
"Failed to obtain system processor information 0x%lx\n",
|
|
Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwProcessorArchitecture = ProcessorInfo.ProcessorArchitecture;
|
|
}
|
|
|
|
//
|
|
// if we are running under WOW64 we would have right now
|
|
// PROCESSOR_ARCHITECTURE_INTEL
|
|
// if we are running 64-bit code we'd have now PROCESSOR_ARCHITECTURE_*
|
|
// as a result to determine further course of actions, we need to consider
|
|
// dynamically finding out whether we are running native code or not
|
|
// if we have an image type that we're checking -- there is no problem since
|
|
// we just check the image type and we're done, MSI case however is different
|
|
//
|
|
|
|
if (dwProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64 ||
|
|
dwProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ||
|
|
dwProcessorArchitecture == PROCESSOR_ARCHITECTURE_ALPHA64) {
|
|
|
|
if (uExeType == IMAGE_FILE_MACHINE_I386) {
|
|
return PROCESSOR_ARCHITECTURE_IA32_ON_WIN64;
|
|
}
|
|
|
|
} else if (dwProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL && uExeType == IMAGE_FILE_MSI) {
|
|
//
|
|
// determine dynamically what kind of code we're running
|
|
//
|
|
PVOID Wow64Info = NULL;
|
|
|
|
Status = NtQueryInformationProcess(NtCurrentProcess(),
|
|
ProcessWow64Information,
|
|
&Wow64Info,
|
|
sizeof(Wow64Info),
|
|
NULL);
|
|
|
|
if (NT_SUCCESS(Status) && Wow64Info != NULL) {
|
|
return PROCESSOR_ARCHITECTURE_IA32_ON_WIN64;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
return dwProcessorArchitecture;
|
|
}
|
|
|
|
#define FULL_TABLETPC_KEY_PATH KEY_MACHINE TEXT("\\") TABLETPC_KEY_PATH
|
|
#define FULL_EHOME_KEY_PATH KEY_MACHINE TEXT("\\") EHOME_KEY_PATH
|
|
|
|
BOOL
|
|
SdbpIsOs(
|
|
DWORD dwOSSKU
|
|
)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
UNICODE_STRING ustrKeyPath = {0};
|
|
UNICODE_STRING ustrValue;
|
|
NTSTATUS status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE KeyHandle;
|
|
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
|
|
ULONG KeyValueBuffer[64];
|
|
ULONG KeyValueLength;
|
|
|
|
if (dwOSSKU == OS_SKU_TAB) {
|
|
RtlInitUnicodeString(&ustrKeyPath, FULL_TABLETPC_KEY_PATH);
|
|
} else if (dwOSSKU == OS_SKU_MED) {
|
|
RtlInitUnicodeString(&ustrKeyPath, FULL_EHOME_KEY_PATH);
|
|
} else {
|
|
DBGPRINT((sdlWarning,
|
|
"SdbpIsOs",
|
|
"Specified unknown OS type 0x%lx",
|
|
dwOSSKU));
|
|
return FALSE;
|
|
}
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&ustrKeyPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
status = NtOpenKey(
|
|
&KeyHandle,
|
|
KEY_QUERY_VALUE | SdbpGetWow64Flag(),
|
|
&ObjectAttributes);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DBGPRINT((sdlWarning,
|
|
"SdbpIsOs",
|
|
"Failed to open Key %s Status 0x%lx",
|
|
ustrKeyPath.Buffer,
|
|
status));
|
|
goto out;
|
|
}
|
|
|
|
KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)&KeyValueBuffer;
|
|
RtlInitUnicodeString(&ustrValue, IS_OS_INSTALL_VALUE);
|
|
|
|
status = NtQueryValueKey(
|
|
KeyHandle,
|
|
&ustrValue,
|
|
KeyValuePartialInformation,
|
|
KeyValueInformation,
|
|
sizeof(KeyValueBuffer),
|
|
&KeyValueLength);
|
|
|
|
NtClose(KeyHandle);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DBGPRINT((sdlWarning,
|
|
"SdbpIsOs",
|
|
"Failed to read value info for value %s for key %s Status 0x%lx",
|
|
IS_OS_INSTALL_VALUE,
|
|
ustrKeyPath.Buffer,
|
|
status));
|
|
goto out;
|
|
}
|
|
|
|
if (KeyValueInformation->Type != REG_DWORD) {
|
|
DBGPRINT((sdlWarning,
|
|
"SdbpIsOs",
|
|
"Unexpected value type 0x%x for value %s under key %s",
|
|
KeyValueInformation->Type,
|
|
IS_OS_INSTALL_VALUE,
|
|
ustrKeyPath.Buffer));
|
|
goto out;
|
|
}
|
|
|
|
if (*(DWORD*)(&KeyValueInformation->Data[0]) != 0) {
|
|
bRet = TRUE;
|
|
}
|
|
|
|
DBGPRINT((sdlInfo|sdlLogShimViewer,
|
|
"SdbpIsOs",
|
|
"%s %s installed",
|
|
0,
|
|
(dwOSSKU == OS_SKU_TAB ? TEXT("TabletPC") : TEXT("eHome")),
|
|
(bRet ? TEXT("is") : TEXT("is not"))));
|
|
|
|
out:
|
|
|
|
return bRet;
|
|
}
|
|
|
|
void
|
|
SdbpGetOSSKU(
|
|
LPDWORD lpdwSKU,
|
|
LPDWORD lpdwSP
|
|
)
|
|
{
|
|
WORD wServicePackMajor;
|
|
WORD wSuiteMask;
|
|
WORD wProductType;
|
|
PPEB Peb = NtCurrentPeb();
|
|
|
|
wServicePackMajor = (Peb->OSCSDVersion >> 8) & 0xFF;
|
|
|
|
wSuiteMask = (USHORT)(USER_SHARED_DATA->SuiteMask & 0xffff);
|
|
|
|
*lpdwSP = 1 << wServicePackMajor;
|
|
|
|
wSuiteMask = wSuiteMask;
|
|
|
|
wProductType = USER_SHARED_DATA->NtProductType;
|
|
|
|
if (wProductType == VER_NT_WORKSTATION) {
|
|
if (wSuiteMask & VER_SUITE_PERSONAL) {
|
|
*lpdwSKU = OS_SKU_PER;
|
|
} else {
|
|
|
|
#if (_WIN32_WINNT >= 0x0501)
|
|
|
|
if (SdbpIsOs(OS_SKU_TAB)) {
|
|
*lpdwSKU = OS_SKU_TAB;
|
|
} else if (SdbpIsOs(OS_SKU_MED)) {
|
|
*lpdwSKU = OS_SKU_MED;
|
|
} else {
|
|
*lpdwSKU = OS_SKU_PRO;
|
|
}
|
|
#else
|
|
*lpdwSKU = OS_SKU_PRO;
|
|
#endif
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (wSuiteMask & VER_SUITE_DATACENTER) {
|
|
*lpdwSKU = OS_SKU_DTC;
|
|
return;
|
|
}
|
|
|
|
if (wSuiteMask & VER_SUITE_ENTERPRISE) {
|
|
*lpdwSKU = OS_SKU_ADS;
|
|
return;
|
|
}
|
|
|
|
if (wSuiteMask & VER_SUITE_BLADE) {
|
|
*lpdwSKU = OS_SKU_BLA;
|
|
return;
|
|
}
|
|
|
|
if (wSuiteMask & VER_SUITE_SMALLBUSINESS) {
|
|
*lpdwSKU = OS_SKU_SBS;
|
|
return;
|
|
}
|
|
|
|
*lpdwSKU = OS_SKU_SRV;
|
|
}
|
|
|
|
DWORD
|
|
SdbpGetWow64Flag(
|
|
VOID
|
|
)
|
|
{
|
|
if (g_dwWow64Key == (DWORD)-1) {
|
|
|
|
OSVERSIONINFOEXW osvi;
|
|
BOOL bSuccess;
|
|
|
|
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
|
|
|
|
#ifdef WIN32U_MODE
|
|
bSuccess = GetVersionExW((POSVERSIONINFOW)&osvi);
|
|
#else
|
|
bSuccess = NT_SUCCESS(RtlGetVersion((PRTL_OSVERSIONINFOW)&osvi));
|
|
#endif
|
|
if (bSuccess) {
|
|
|
|
//
|
|
// Straight win2k
|
|
//
|
|
if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
|
|
|
|
g_dwWow64Key = 0; // no flag since there is no wow64 on win2k
|
|
|
|
} else {
|
|
|
|
g_dwWow64Key = KEY_WOW64_64KEY;
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
DBGPRINT((sdlError, "SdbGetWow64Flag", "RtlGetVersion failed\n"));
|
|
|
|
//
|
|
// XP or higher.
|
|
//
|
|
g_dwWow64Key = KEY_WOW64_64KEY;
|
|
}
|
|
}
|
|
|
|
return g_dwWow64Key;
|
|
}
|
|
|
|
NTSTATUS
|
|
SDBAPI
|
|
SdbEnsureBufferSizeFunction(
|
|
IN ULONG Flags,
|
|
IN OUT PRTL_BUFFER Buffer,
|
|
IN SIZE_T Size
|
|
)
|
|
{
|
|
HANDLE ModuleHandle;
|
|
UNICODE_STRING ModuleName = RTL_CONSTANT_STRING(L"ntdll.dll");
|
|
STRING ProcedureName = RTL_CONSTANT_STRING("RtlpEnsureBufferSize");
|
|
NTSTATUS Status;
|
|
ULONG DllCharacteristics = IMAGE_FILE_SYSTEM;
|
|
|
|
if (g_pfnEnsureBufferSize == NULL) {
|
|
|
|
Status = LdrGetDllHandle(NULL,
|
|
&DllCharacteristics,
|
|
&ModuleName,
|
|
(PVOID)&ModuleHandle);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbEnsureBufferSizeFunction",
|
|
"Failed to retrieve ntdll.dll module handle, Error 0x%lx\n",
|
|
Status));
|
|
return Status;
|
|
}
|
|
|
|
|
|
Status = LdrGetProcedureAddress(ModuleHandle,
|
|
&ProcedureName,
|
|
0,
|
|
(PVOID*)&g_pfnEnsureBufferSize);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlError,
|
|
"SdbEnsureBufferSizeFunction",
|
|
"RtlpEnsureBufferSize is not available, reverting to sdbapi\n"));
|
|
|
|
g_pfnEnsureBufferSize = SdbpEnsureBufferSize;
|
|
}
|
|
}
|
|
|
|
return g_pfnEnsureBufferSize(Flags, Buffer, Size);
|
|
}
|
|
|
|
BOOL
|
|
SdbpWriteToShimViewer(
|
|
HSDB hSDB,
|
|
LPCSTR pszBuffer
|
|
)
|
|
{
|
|
ANSI_STRING strMessage;
|
|
UNICODE_STRING ustrMessage;
|
|
WCHAR pwszFullMessageBuffer[SHIMVIEWER_DATA_SIZE];
|
|
NTSTATUS Status = STATUS_UNSUCCESSFUL;
|
|
PCHAR pchStart;
|
|
PCHAR pch;
|
|
PWCHAR pwch;
|
|
|
|
UNREFERENCED_PARAMETER(hSDB);
|
|
|
|
if (g_eShimViewerOption != SHIMVIEWER_OPTION_YES || pszBuffer == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
StringCchCopy(pwszFullMessageBuffer, SHIMVIEWER_DATA_SIZE, SHIMVIEWER_DATA_PREFIX);
|
|
|
|
//
|
|
// Preprocess the string to eliminate any \t \r \n
|
|
//
|
|
pchStart = (PCHAR)pszBuffer;
|
|
|
|
while (pchStart != NULL && *pchStart) {
|
|
|
|
//
|
|
// Skip over white space and any empty lines
|
|
//
|
|
pchStart += strspn(pchStart, " \t\r\n");
|
|
|
|
pch = strpbrk(pchStart, "\r\n");
|
|
|
|
if (pch == NULL) {
|
|
RtlInitAnsiString(&strMessage, pchStart);
|
|
pchStart = NULL;
|
|
} else {
|
|
|
|
//
|
|
// if it's a tab, nuke it by replacing it with ' '
|
|
//
|
|
|
|
strMessage.Buffer = pchStart;
|
|
strMessage.MaximumLength =
|
|
strMessage.Length = (USHORT)((DWORD_PTR)pch - (DWORD_PTR)pchStart); // size in bytes
|
|
|
|
//
|
|
// now adjust the pointer past \r\n
|
|
//
|
|
pchStart = pch + strspn(pch, "\r\n");
|
|
}
|
|
|
|
if (strMessage.Length == 0) {
|
|
continue;
|
|
}
|
|
|
|
Status = RtlAnsiStringToUnicodeString(&ustrMessage, &strMessage, TRUE);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DBGPRINT((sdlWarning,
|
|
"SdbpWriteToShimViewer",
|
|
"Failed to convert string to unicode status 0x%lx\n",
|
|
Status));
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Second line of defense against rogue chars in shimviewer.
|
|
// Search and replace all instances of '\t' with ' '
|
|
//
|
|
pwch = ustrMessage.Buffer;
|
|
|
|
while (pwch != NULL && *pwch) {
|
|
|
|
pwch = wcschr(pwch, L'\t');
|
|
|
|
if (pwch != NULL) {
|
|
*pwch++ = L' ';
|
|
}
|
|
}
|
|
|
|
//
|
|
// Send the spew to shimviewer.
|
|
//
|
|
pwszFullMessageBuffer[SHIMVIEWER_DATA_PREFIX_LEN] = 0;
|
|
StringCchCat(pwszFullMessageBuffer, SHIMVIEWER_DATA_SIZE, ustrMessage.Buffer);
|
|
RtlFreeUnicodeString(&ustrMessage);
|
|
|
|
OutputDebugStringW(pwszFullMessageBuffer);
|
|
}
|
|
|
|
cleanup:
|
|
|
|
return NT_SUCCESS(Status);
|
|
}
|
|
|
|
#pragma warning(disable:4101) // we do this to cover up for a bug in RtlInitUnicodeStringBuffer
|
|
|
|
void
|
|
SdbpRtlInitUnicodeStringBuffer(
|
|
RTL_UNICODE_STRING_BUFFER* pBuff,
|
|
UCHAR* StatBuff,
|
|
SIZE_T StatSize
|
|
)
|
|
{
|
|
RtlInitUnicodeStringBuffer(pBuff, StatBuff, StatSize);
|
|
}
|
|
|
|
#pragma warning(default:4101)
|
|
|
|
|
|
|