[WIN32K:NTUSER] Fixes for NtUserCreateWindowStation(), IntCreateWindowStation() and NtUserOpenWindowStation().

CORE-11933 and PR #621.

We are potentially going to modify the window station name to be
created or opened, by one with the format "Service-0x<luidhigh>-<luidlow>$",
in case the user provides an empty name.
Since we want to use the user-mode ObjectAttributes structure pointer so
that the Object manager can correctly perform the access checks and the
capture, we actually need to construct the new window station name in the
user memory space! This allows us then to fetch the new name in the
ObjectAttributes structure so that Ob can use it.
All of this is performed under SEH.

For NtUserOpenWindowStation(), we also need to be Windows-compatible and
detect whether user-mode gave instead the special "Service-0x00000000-00000000$"
name (observed via API monitoring) that is used when one tries to open a
window station with empty name.
This commit is contained in:
Hermès Bélusca-Maïto 2018-07-22 20:22:49 +02:00
parent 26933a0965
commit 7bc2ddd669
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0
2 changed files with 375 additions and 108 deletions

View file

@ -14,7 +14,10 @@ DBG_DEFAULT_CHANNEL(UserWinsta);
/* GLOBALS *******************************************************************/
/* Currently active window station */
/*
* The currently active window station. This is the
* only one interactive window station on the system.
*/
PWINSTATION_OBJECT InputWindowStation = NULL;
/* Winlogon SAS window */
@ -96,7 +99,7 @@ UserCreateWinstaDirectory(VOID)
return Status;
}
/* OBJECT CALLBACKS **********************************************************/
/* OBJECT CALLBACKS ***********************************************************/
NTSTATUS
NTAPI
@ -106,7 +109,18 @@ IntWinStaObjectDelete(
PWIN32_DELETEMETHOD_PARAMETERS DeleteParameters = Parameters;
PWINSTATION_OBJECT WinSta = (PWINSTATION_OBJECT)DeleteParameters->Object;
TRACE("Deleting window station (0x%p)\n", WinSta);
TRACE("Deleting window station 0x%p\n", WinSta);
if (WinSta == InputWindowStation)
{
ERR("WARNING: Deleting the interactive window station '%wZ'!\n",
&(OBJECT_HEADER_TO_NAME_INFO(OBJECT_TO_OBJECT_HEADER(InputWindowStation))->Name));
/* Only Winlogon can close and delete the interactive window station */
ASSERT(gpidLogon == PsGetCurrentProcessId());
InputWindowStation = NULL;
}
WinSta->Flags |= WSS_DYING;
@ -192,7 +206,7 @@ IntWinStaOkToClose(
ppi = PsGetCurrentProcessWin32Process();
if(ppi && (OkToCloseParameters->Handle == ppi->hwinsta))
if (ppi && (OkToCloseParameters->Handle == ppi->hwinsta))
{
return STATUS_ACCESS_DENIED;
}
@ -390,6 +404,7 @@ IntCreateWindowStation(
OUT HWINSTA* phWinSta,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN KPROCESSOR_MODE AccessMode,
IN KPROCESSOR_MODE OwnerMode,
IN ACCESS_MASK dwDesiredAccess,
DWORD Unknown2,
DWORD Unknown3,
@ -398,8 +413,8 @@ IntCreateWindowStation(
DWORD Unknown6)
{
NTSTATUS Status;
HWINSTA WindowStation;
PWINSTATION_OBJECT WindowStationObject;
HWINSTA hWinSta;
PWINSTATION_OBJECT WindowStation;
TRACE("IntCreateWindowStation called\n");
@ -412,60 +427,61 @@ IntCreateWindowStation(
NULL,
dwDesiredAccess,
NULL,
(PVOID*)&WindowStation);
(PVOID*)&hWinSta);
if (NT_SUCCESS(Status))
{
TRACE("IntCreateWindowStation opened window station %wZ\n",
TRACE("IntCreateWindowStation opened window station '%wZ'\n",
ObjectAttributes->ObjectName);
*phWinSta = WindowStation;
*phWinSta = hWinSta;
return Status;
}
/*
* No existing window station found, try to create new one.
* No existing window station found, try to create a new one.
*/
/* Create the window station object */
Status = ObCreateObject(KernelMode,
Status = ObCreateObject(AccessMode,
ExWindowStationObjectType,
ObjectAttributes,
AccessMode,
OwnerMode,
NULL,
sizeof(WINSTATION_OBJECT),
0,
0,
(PVOID*)&WindowStationObject);
(PVOID*)&WindowStation);
if (!NT_SUCCESS(Status))
{
ERR("ObCreateObject failed with %lx for window station %wZ\n",
Status, ObjectAttributes->ObjectName);
ERR("ObCreateObject failed for window station '%wZ', Status 0x%08lx\n",
ObjectAttributes->ObjectName, Status);
SetLastNtError(Status);
return Status;
}
/* Initialize the window station */
RtlZeroMemory(WindowStationObject, sizeof(WINSTATION_OBJECT));
RtlZeroMemory(WindowStation, sizeof(WINSTATION_OBJECT));
InitializeListHead(&WindowStationObject->DesktopListHead);
WindowStationObject->dwSessionId = NtCurrentPeb()->SessionId;
Status = RtlCreateAtomTable(37, &WindowStationObject->AtomTable);
InitializeListHead(&WindowStation->DesktopListHead);
WindowStation->dwSessionId = NtCurrentPeb()->SessionId;
Status = RtlCreateAtomTable(37, &WindowStation->AtomTable);
if (!NT_SUCCESS(Status))
{
ERR("RtlCreateAtomTable failed with %lx for window station %wZ\n", Status, ObjectAttributes->ObjectName);
ObDereferenceObject(WindowStationObject);
ERR("RtlCreateAtomTable failed for window station '%wZ', Status 0x%08lx\n",
ObjectAttributes->ObjectName, Status);
ObDereferenceObject(WindowStation);
SetLastNtError(Status);
return Status;
}
Status = ObInsertObject(WindowStationObject,
Status = ObInsertObject(WindowStation,
NULL,
dwDesiredAccess,
0,
NULL,
(PVOID*)&WindowStation);
(PVOID*)&hWinSta);
if (!NT_SUCCESS(Status))
{
ERR("ObInsertObject failed with %lx for window station\n", Status);
ERR("ObInsertObject failed for window station, Status 0x%08lx\n", Status);
SetLastNtError(Status);
return Status;
}
@ -475,24 +491,182 @@ IntCreateWindowStation(
if (InputWindowStation == NULL)
{
ERR("Initializing input window station\n");
InputWindowStation = WindowStationObject;
WindowStationObject->Flags &= ~WSS_NOIO;
/* Only Winlogon can create the interactive window station */
ASSERT(gpidLogon == PsGetCurrentProcessId());
InputWindowStation = WindowStation;
WindowStation->Flags &= ~WSS_NOIO;
InitCursorImpl();
}
else
{
WindowStationObject->Flags |= WSS_NOIO;
WindowStation->Flags |= WSS_NOIO;
}
TRACE("IntCreateWindowStation created object 0x%p with name %wZ handle 0x%p\n",
WindowStationObject, ObjectAttributes->ObjectName, WindowStation);
TRACE("IntCreateWindowStation created window station '%wZ' object 0x%p handle 0x%p\n",
ObjectAttributes->ObjectName, WindowStation, hWinSta);
*phWinSta = WindowStation;
*phWinSta = hWinSta;
return STATUS_SUCCESS;
}
static VOID
FreeUserModeWindowStationName(
IN OUT PUNICODE_STRING WindowStationName,
IN PUNICODE_STRING TebStaticUnicodeString,
IN OUT POBJECT_ATTRIBUTES UserModeObjectAttributes OPTIONAL,
IN POBJECT_ATTRIBUTES LocalObjectAttributes OPTIONAL)
{
SIZE_T MemSize = 0;
/* Try to restore the user's UserModeObjectAttributes */
if (UserModeObjectAttributes && LocalObjectAttributes)
{
_SEH2_TRY
{
ProbeForWrite(UserModeObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG));
*UserModeObjectAttributes = *LocalObjectAttributes;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
NOTHING;
}
_SEH2_END;
}
/* Free the user-mode memory */
if (WindowStationName && (WindowStationName != TebStaticUnicodeString))
{
ZwFreeVirtualMemory(ZwCurrentProcess(),
(PVOID*)&WindowStationName,
&MemSize,
MEM_RELEASE);
}
}
static NTSTATUS
BuildUserModeWindowStationName(
IN OUT POBJECT_ATTRIBUTES UserModeObjectAttributes,
IN OUT POBJECT_ATTRIBUTES LocalObjectAttributes,
OUT PUNICODE_STRING* WindowStationName,
OUT PUNICODE_STRING* TebStaticUnicodeString)
{
NTSTATUS Status;
SIZE_T MemSize;
LUID CallerLuid;
PTEB Teb;
USHORT StrSize;
*WindowStationName = NULL;
*TebStaticUnicodeString = NULL;
/* Retrieve the current process LUID */
Status = GetProcessLuid(NULL, NULL, &CallerLuid);
if (!NT_SUCCESS(Status))
{
ERR("Failed to retrieve the caller LUID, Status 0x%08lx\n", Status);
return Status;
}
/* Compute the needed string size */
MemSize = _scwprintf(L"%wZ\\Service-0x%x-%x$",
&gustrWindowStationsDir,
CallerLuid.HighPart,
CallerLuid.LowPart);
MemSize = MemSize * sizeof(WCHAR) + sizeof(UNICODE_NULL);
if (MemSize > MAXUSHORT)
{
ERR("Window station name length is too long.\n");
return STATUS_NAME_TOO_LONG;
}
StrSize = (USHORT)MemSize;
/*
* Check whether it's short enough so that we can use the static buffer
* in the TEB. Otherwise continue with virtual memory allocation.
*/
Teb = NtCurrentTeb();
if (Teb && (StrSize <= sizeof(Teb->StaticUnicodeBuffer)))
{
/* We can use the TEB's static unicode string */
ASSERT(Teb->StaticUnicodeString.Buffer == Teb->StaticUnicodeBuffer);
ASSERT(Teb->StaticUnicodeString.MaximumLength == sizeof(Teb->StaticUnicodeBuffer));
/* Remember the TEB's static unicode string address for later */
*TebStaticUnicodeString = &Teb->StaticUnicodeString;
*WindowStationName = *TebStaticUnicodeString;
(*WindowStationName)->Length = 0;
}
else
{
/* The TEB's static unicode string is too small, allocate some user-mode virtual memory */
MemSize += ALIGN_UP(sizeof(UNICODE_STRING), sizeof(PVOID));
/* Allocate the memory in user-mode */
Status = ZwAllocateVirtualMemory(ZwCurrentProcess(),
(PVOID*)WindowStationName,
0,
&MemSize,
MEM_COMMIT,
PAGE_READWRITE);
if (!NT_SUCCESS(Status))
{
ERR("ZwAllocateVirtualMemory() failed, Status 0x%08lx\n", Status);
return Status;
}
RtlInitEmptyUnicodeString(*WindowStationName,
(PWCHAR)((ULONG_PTR)*WindowStationName +
ALIGN_UP(sizeof(UNICODE_STRING), sizeof(PVOID))),
StrSize);
}
/* Build a valid window station name from the LUID */
Status = RtlStringCbPrintfW((*WindowStationName)->Buffer,
(*WindowStationName)->MaximumLength,
L"%wZ\\Service-0x%x-%x$",
&gustrWindowStationsDir,
CallerLuid.HighPart,
CallerLuid.LowPart);
if (!NT_SUCCESS(Status))
{
ERR("Impossible to build a valid window station name, Status 0x%08lx\n", Status);
goto Quit;
}
(*WindowStationName)->Length = (USHORT)(wcslen((*WindowStationName)->Buffer) * sizeof(WCHAR));
/* Try to update the user's UserModeObjectAttributes */
_SEH2_TRY
{
ProbeForWrite(UserModeObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG));
*LocalObjectAttributes = *UserModeObjectAttributes;
UserModeObjectAttributes->ObjectName = *WindowStationName;
UserModeObjectAttributes->RootDirectory = NULL;
Status = STATUS_SUCCESS;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
Quit:
if (!NT_SUCCESS(Status))
{
/* Release the window station name */
FreeUserModeWindowStationName(*WindowStationName,
*TebStaticUnicodeString,
NULL, NULL);
}
return Status;
}
HWINSTA
APIENTRY
NtUserCreateWindowStation(
@ -504,108 +678,103 @@ NtUserCreateWindowStation(
DWORD Unknown5,
DWORD Unknown6)
{
NTSTATUS Status;
HWINSTA hWinSta;
NTSTATUS Status = STATUS_SUCCESS;
HWINSTA hWinSta = NULL;
OBJECT_ATTRIBUTES LocalObjectAttributes;
UNICODE_STRING WindowStationName;
KPROCESSOR_MODE AccessMode = UserMode;
PUNICODE_STRING WindowStationName = NULL;
PUNICODE_STRING TebStaticUnicodeString = NULL;
KPROCESSOR_MODE OwnerMode = UserMode;
TRACE("NtUserCreateWindowStation called\n");
/* Capture window station name */
/* Capture the object attributes and the window station name */
_SEH2_TRY
{
ProbeForRead(ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG));
LocalObjectAttributes = *ObjectAttributes;
if (LocalObjectAttributes.ObjectName ||
LocalObjectAttributes.RootDirectory
/* &&
LocalObjectAttributes.ObjectName->Buffer &&
LocalObjectAttributes.ObjectName->Length > 0 */)
if (LocalObjectAttributes.Length != sizeof(OBJECT_ATTRIBUTES))
{
Status = IntSafeCopyUnicodeStringTerminateNULL(&WindowStationName,
LocalObjectAttributes.ObjectName);
LocalObjectAttributes.ObjectName = &WindowStationName;
ERR("Invalid ObjectAttributes length!\n");
Status = STATUS_INVALID_PARAMETER;
_SEH2_LEAVE;
}
else
/*
* Check whether the caller provided a window station name together
* with a RootDirectory handle.
*
* If the caller did not provide a window station name, build a new one
* based on the logon session identifier for the calling process.
* The new name is allocated in user-mode, as the rest of ObjectAttributes
* already is, so that the validation performed by the Object Manager
* can be done adequately.
*/
if ((LocalObjectAttributes.ObjectName == NULL ||
LocalObjectAttributes.ObjectName->Buffer == NULL ||
LocalObjectAttributes.ObjectName->Length == 0 ||
LocalObjectAttributes.ObjectName->Buffer[0] == UNICODE_NULL)
/* &&
LocalObjectAttributes.RootDirectory == NULL */)
{
LocalObjectAttributes.ObjectName = NULL;
Status = STATUS_SUCCESS;
/* No, build the new window station name */
Status = BuildUserModeWindowStationName(ObjectAttributes,
&LocalObjectAttributes,
&WindowStationName,
&TebStaticUnicodeString);
if (!NT_SUCCESS(Status))
{
ERR("BuildUserModeWindowStationName() failed, Status 0x%08lx\n", Status);
_SEH2_LEAVE;
}
OwnerMode = KernelMode;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status =_SEH2_GetExceptionCode();
ERR("ObjectAttributes capture failed, Status 0x%08lx\n", Status);
}
_SEH2_END
_SEH2_END;
if (!NT_SUCCESS(Status))
{
ERR("Failed reading or capturing window station name, Status 0x%08lx\n", Status);
SetLastNtError(Status);
return NULL;
}
/*
* If the caller did not provide a window station name, build a new one
* based on the logon session identifier for the calling process.
*/
if (!LocalObjectAttributes.ObjectName)
{
LUID CallerLuid;
WCHAR ServiceWinStaName[MAX_PATH];
/* Retrieve the LUID of the current process */
Status = GetProcessLuid(NULL, NULL, &CallerLuid);
if (!NT_SUCCESS(Status))
{
ERR("Failed to retrieve the caller LUID, Status 0x%08lx\n", Status);
SetLastNtError(Status);
return NULL;
}
/* Build a valid window station name from the LUID */
Status = RtlStringCbPrintfW(ServiceWinStaName,
sizeof(ServiceWinStaName),
L"%wZ\\Service-0x%x-%x$",
&gustrWindowStationsDir,
CallerLuid.HighPart,
CallerLuid.LowPart);
if (!NT_SUCCESS(Status))
{
ERR("Impossible to build a valid window station name, Status 0x%08lx\n", Status);
SetLastNtError(Status);
return NULL;
}
RtlInitUnicodeString(&WindowStationName, ServiceWinStaName);
LocalObjectAttributes.ObjectName = &WindowStationName;
AccessMode = KernelMode;
}
// TODO: Capture and use the SecurityQualityOfService!
/* Create the window station */
Status = IntCreateWindowStation(&hWinSta,
&LocalObjectAttributes,
AccessMode,
ObjectAttributes,
UserMode,
OwnerMode,
dwDesiredAccess,
Unknown2,
Unknown3,
Unknown4,
Unknown5,
Unknown6);
if ((AccessMode == UserMode) && LocalObjectAttributes.ObjectName)
ExFreePoolWithTag(LocalObjectAttributes.ObjectName->Buffer, TAG_STRING);
if (NT_SUCCESS(Status))
{
TRACE("NtUserCreateWindowStation created a window station with handle 0x%p\n", hWinSta);
TRACE("NtUserCreateWindowStation created window station '%wZ' with handle 0x%p\n",
ObjectAttributes->ObjectName, hWinSta);
}
else
{
ASSERT(hWinSta == NULL);
TRACE("NtUserCreateWindowStation failed to create a window station!\n");
ERR("NtUserCreateWindowStation failed to create window station '%wZ', Status 0x%08lx\n",
ObjectAttributes->ObjectName, Status);
}
/* Try to restore the user's ObjectAttributes and release the window station name */
FreeUserModeWindowStationName(WindowStationName,
TebStaticUnicodeString,
(OwnerMode == KernelMode ? ObjectAttributes : NULL),
&LocalObjectAttributes);
if (!NT_SUCCESS(Status))
{
ASSERT(hWinSta == NULL);
SetLastNtError(Status);
}
return hWinSta;
@ -635,32 +804,130 @@ NtUserCreateWindowStation(
* @implemented
*/
HWINSTA APIENTRY
HWINSTA
APIENTRY
NtUserOpenWindowStation(
POBJECT_ATTRIBUTES ObjectAttributes,
ACCESS_MASK dwDesiredAccess)
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN ACCESS_MASK dwDesiredAccess)
{
HWINSTA hwinsta;
NTSTATUS Status;
NTSTATUS Status = STATUS_SUCCESS;
HWINSTA hWinSta = NULL;
OBJECT_ATTRIBUTES LocalObjectAttributes;
PUNICODE_STRING WindowStationName = NULL;
PUNICODE_STRING TebStaticUnicodeString = NULL;
KPROCESSOR_MODE OwnerMode = UserMode;
TRACE("NtUserOpenWindowStation called\n");
/* Capture the object attributes and the window station name */
_SEH2_TRY
{
ProbeForRead(ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG));
LocalObjectAttributes = *ObjectAttributes;
if (LocalObjectAttributes.Length != sizeof(OBJECT_ATTRIBUTES))
{
ERR("Invalid ObjectAttributes length!\n");
Status = STATUS_INVALID_PARAMETER;
_SEH2_LEAVE;
}
/*
* Check whether the caller did not provide a window station name,
* or provided the special "Service-0x00000000-00000000$" name.
*
* NOTE: On Windows, the special "Service-0x00000000-00000000$" string
* is used instead of an empty name (observed when API-monitoring
* OpenWindowStation() called with an empty window station name).
*/
if ((LocalObjectAttributes.ObjectName == NULL ||
LocalObjectAttributes.ObjectName->Buffer == NULL ||
LocalObjectAttributes.ObjectName->Length == 0 ||
LocalObjectAttributes.ObjectName->Buffer[0] == UNICODE_NULL)
/* &&
LocalObjectAttributes.RootDirectory == NULL */)
{
/* No, remember that for later */
LocalObjectAttributes.ObjectName = NULL;
}
if (LocalObjectAttributes.ObjectName &&
LocalObjectAttributes.ObjectName->Length ==
sizeof(L"Service-0x00000000-00000000$") - sizeof(UNICODE_NULL) &&
_wcsnicmp(LocalObjectAttributes.ObjectName->Buffer,
L"Service-0x00000000-00000000$",
LocalObjectAttributes.ObjectName->Length / sizeof(WCHAR)) == 0)
{
/* No, remember that for later */
LocalObjectAttributes.ObjectName = NULL;
}
/*
* If the caller did not provide a window station name, build a new one
* based on the logon session identifier for the calling process.
* The new name is allocated in user-mode, as the rest of ObjectAttributes
* already is, so that the validation performed by the Object Manager
* can be done adequately.
*/
if (!LocalObjectAttributes.ObjectName)
{
/* No, build the new window station name */
Status = BuildUserModeWindowStationName(ObjectAttributes,
&LocalObjectAttributes,
&WindowStationName,
&TebStaticUnicodeString);
if (!NT_SUCCESS(Status))
{
ERR("BuildUserModeWindowStationName() failed, Status 0x%08lx\n", Status);
_SEH2_LEAVE;
}
OwnerMode = KernelMode;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status =_SEH2_GetExceptionCode();
ERR("ObjectAttributes capture failed, Status 0x%08lx\n", Status);
}
_SEH2_END;
if (!NT_SUCCESS(Status))
{
SetLastNtError(Status);
return NULL;
}
/* Open the window station */
Status = ObOpenObjectByName(ObjectAttributes,
ExWindowStationObjectType,
UserMode,
NULL,
dwDesiredAccess,
NULL,
(PVOID*)&hwinsta);
(PVOID*)&hWinSta);
if (NT_SUCCESS(Status))
{
TRACE("NtUserOpenWindowStation opened window station '%wZ' with handle 0x%p\n",
ObjectAttributes->ObjectName, hWinSta);
}
else
{
ASSERT(hWinSta == NULL);
ERR("NtUserOpenWindowStation failed to open window station '%wZ', Status 0x%08lx\n",
ObjectAttributes->ObjectName, Status);
}
/* Try to restore the user's ObjectAttributes and release the window station name */
FreeUserModeWindowStationName(WindowStationName,
TebStaticUnicodeString,
(OwnerMode == KernelMode ? ObjectAttributes : NULL),
&LocalObjectAttributes);
if (!NT_SUCCESS(Status))
{
ERR("NtUserOpenWindowStation failed\n");
ASSERT(hWinSta == NULL);
SetLastNtError(Status);
return 0;
}
TRACE("Opened window station %wZ with handle %p\n", ObjectAttributes->ObjectName, hwinsta);
return hwinsta;
return hWinSta;
}
/*
@ -677,7 +944,7 @@ NtUserOpenWindowStation(
*
* Remarks
* The window station handle can be created with NtUserCreateWindowStation
* or NtUserOpenWindowStation. Attemps to close a handle to the window
* or NtUserOpenWindowStation. Attempts to close a handle to the window
* station assigned to the calling process will fail.
*
* Status
@ -1713,5 +1980,4 @@ Leave:
return Ret;
}
/* EOF */

View file

@ -112,6 +112,7 @@ IntCreateWindowStation(
OUT HWINSTA* phWinSta,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN KPROCESSOR_MODE AccessMode,
IN KPROCESSOR_MODE OwnerMode,
IN ACCESS_MASK dwDesiredAccess,
DWORD Unknown2,
DWORD Unknown3,