NtUser stubs
From ReactOS
This pseudo code demonstrates how to securely capture buffers in NtUser stubs.
This page will be updated soon!
BOOL
UserSomeFunc(IN PSOME_STRUCT SomeStruct,
IN ULONG InputUlong,
IN ULONG SomeOtherVar,
OUT PUNICODE_STRING ReturnedInfo,
OUT PULONG ReturnUlong)
{
ULONG SomeData1, SomeData2;
PWSTR SomeStr = NULL;
BOOL Ret = FALSE;
/* NOTE: all parameters are probed here, but writing to the structures
may still raise an exception! So make sure not to touch them
or protect the access to them (again) while holding locks to
prevent dead-locks, or while having allocated anonymous memory
to prevent memory leaks! */
/* UserSomeFunc will demonstrate how to deal with anonymous buffers that
contain information that is supposed to be returned to the caller. In
this case you need to protect it with SEH so that in case of failure
we don't leak the memory! */
AcquireSomeLock();
/* SomeData1 will later be written into SomeStruct, don't write it here
because that could fail and we're holding a lock */
SomeData1 = GetSomeData1();
/* in this case if SomeData1 is not 0, we allocate some anonymous memory
to get a (internal) string */
if (SomeData1 != 0)
{
SomeStr = AllocateSomeMemory(SomeData1);
if (SomeStr == NULL)
{
goto Cleanup;
}
/* do something with the allocated memory, in this case */
GetSomeInternalString(InputUlong,
SomeStr);
}
SomeData2 = SomeData1 + InputUlong;
/* nothing failed, return success */
Ret = TRUE;
Cleanup:
/* release the lock! */
ReleaseSomeLock();
if (Ret)
{
_SEH_TRY
{
UNICODE_STRING LocalReturnedInfo;
/* in case of success now write to the structures. However, as
we still have temporary memory allocated we need to protect
this with SEH so we get a chance to free all memory even in
case of failure! */
SomeStruct->SomeData = SomeData1;
*ReturnUlong = SomeData2;
/* initialize a local UNICODE_STRING structure with the
String in SomeStr (which we assume is NULL-terminated).
Once that's done copy the string back to the caller. */
RtlInitUnicodeString(&LocalReturnedInfo,
SomeStr);
RtlCopyUnicodeString(ReturnedInfo,
&LocalReturnedInfo);
}
_SEH_HANDLE
{
/* ok, something bad happened, make sure we return FALSE. Another
possibility is to use _SEH_EXCEPT and forward the exception to
the NtUser stub and free the allocated memory in here */
SetLastWin32Error(RtlNtStatusToDosError(_SEH_GetExceptionCode()));
Ret = FALSE;
}
_SEH_END;
}
/* finally free the temporary buffer if necessary */
if (SomeStr != NULL)
{
FreeSomeMemory(SomeStr);
}
return Ret;
}
BOOL
UserSomeOtherFunc(IN PSOME_STRUCT SomeStruct,
IN ULONG SomeOtherVar,
OUT PUNICODE_STRING ReturnedInfo,
OUT PULONG ReturnUlong)
{
UNICODE_STRING LocalReturnedInfo;
ULONG SomeData1, SomeData2;
/* NOTE: all parameters are probed here, but writing to the structures
may still raise an exception! So make sure not to touch them
or protect the access to them (again) while holding locks to
prevent dead-locks, or while having allocated anonymous memory
to prevent memory leaks! */
/* UserSomeOtherFunc will demonstrate how to implement a simple function
that doesn't need temporary buffers. The code is mostly the same as
UserSomeFunc. */
AcquireSomeLock();
/* SomeData1 will later be written into SomeStruct, don't write it here
because that could fail and we're holding a lock */
SomeData1 = GetSomeData1();
SomeData2 = SomeData1 + InputUlong;
/* release the lock! */
ReleaseSomeLock();
/* in case of success now write to the structures. As we don't have any
temporary buffers allocated and we released all locks we don't care
if an exception is raised here. Just copy back the information gathered.*/
SomeStruct->SomeData = SomeData1;
*ReturnUlong = SomeData2;
/* initialize a local UNICODE_STRING structure with a static string we want to return.
Once that's done copy the string back to the caller. */
RtlInitUnicodeString(&LocalReturnedInfo,
L"SomeStaticString");
RtlCopyUnicodeString(ReturnedInfo,
&LocalReturnedInfo);
return Ret;
}
BOOL STDCALL
NtUserSomeFunc(IN PSOME_STRUCT SomeStruct,
IN PULONG InputUlong OPTIONAL,
IN ULONG SomeOtherVar,
OUT PUNICODE_STRING ReturnedInfo,
OUT PULONG ReturnUlong)
{
BOOL Ret = FALSE;
_SEH_TRY
{
UNICODE_STRING cptReturnedInfo;
SOME_STRUCT cptSomeStruct;
ULONG cptInputUlong = 0;
/* probe and capture the input structure */
ProbeForRead(SomeStruct,
sizeof(SOME_STRUCT),
sizeof(ULONG));
RtlCopyMemory(&cptSomeStruct,
SomeStruct,
sizeof(SOME_STRUCT);
/* probe and capture the input ULONG variable if necessary */
if (InputUlong != NULL)
{
cptInputUlong = ProbeForReadUlong(InputUlong);
}
/* probe the buffer the string will be written to */
cptReturnedInfo = ProbeForReadUnicodeString(ReturnedInfo);
ProbeForWrite(cptReturnedInfo.Buffer,
cptReturnedInfo.MaximumLength,
1);
/* make sure to probe the return ULONG memory */
ProbeForWriteUlong(ReturnUlong);
/* we're all set, time to call the real implementation.
In this example, if InputUlong was NULL we want to call
a different function, but that's just an example here */
if (InputUlong != NULL)
{
/* in this case we use the value InputUlong points to */
Ret = UserSomeFunc(&cptSomeStruct,
cptInputUlong,
SomeOtherVar,
ReturnedInfo,
ReturnUlong);
}
else
{
/* in this case we don't want to use InputUlong */
Ret = UserSomeOtherFunc(&cptSomeStruct,
SomeOtherVar,
ReturnedInfo,
ReturnUlong);
}
}
_SEH_HANDLE
{
SetLastWin32Error(RtlNtStatusToDosError(_SEH_GetExceptionCode()));
}
_SEH_END;
return Ret;
}
BOOL
UserSomeFunc2(IN PUNICODE_STRING InputUnicodeString)
{
/* NOTE: InputUnicodeString will not be NULL here! Although it could
contain a NULL-Unicode-String. */
DbgPrint("InputUnicodeString is %wZ\n", InputUnicodeString);
}
BOOL STDCALL
NtUserSomeFunc2(IN PUNICODE_STRING InputUnicodeString OPTIONAL)
{
UNICODE_STRING CapturedInput = {0};
NTSTATUS Status;
BOOL Ret = FALSE;
/* this function demonstrates how to properly capture
unicode strings. in this we want the caller to be
able to pass NULL as well. */
_SEH_TRY
{
if (InputUnicodeString != NULL)
{
Status = ProbeAndCaptureUnicodeString(&CapturedInput,
UserMode,
InputUnicodeString);
if (!NT_SUCCESS(Status))
{
RtlRaiseStatus(Status);
/* NOTE: execution will be continued in the _SEH_HANDLE
block, so make sure you don't leak temporary
memory! */
}
}
/* call UserSomeFunc2, if InputUnicodeString was NULL then
CapturedInput will be a NULL-String here! */
Ret = UserSomeFunc2(&CapturedInput);
}
_SEH_HANDLE
{
SetLastWin32Error(RtlNtStatusToDosError(_SEH_GetExceptionCode()));
}
_SEH_END;
/* Free the captured unicode string */
RtlFreeUnicodeString(&CapturedInput);
return Ret;
}