NtUser stubs

From ReactOS
Jump to: navigation, search

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;
   }
Personal tools
Namespaces

Variants
Actions
Navigation
Toolbox