Geoff Chappell, Software Analyst
The RtlAppendUnicodeToString function modifes a UNICODE_STRING structure so that the string it describes on input has another string appended to it on output.
NTSTATUS RtlAppendUnicodeToString ( UNICODE_STRING *Destination, PCWSTR Source);
The required Destination argument is the address of a structure that already has Length bytes of Unicode characters at a Buffer whose size in bytes is MaximumLength. The function may change the Length.
The optional Source argument is the address of a null-terminated Unicode string to append to whatever characters are already described by Destination. This argument can be NULL to append nothing.
The function returns STATUS_SUCCESS if successful, else a negative error code.
The RtlAppendUnicodeToString function is exported by name from the kernel and NTDLL is all known versions, i.e., 3.10 and higher.
The RtlAppendUnicodeToString function is documented in all known editions of the Device Driver Kit (DDK) or Windows Driver Kit (WDK) since at least the DDK for Windows NT 3.51. Though this documentation is of the kernel-mode function as an export from the kernel, it is mostly applicable to the user-mode implementation too, both being plausibly compiled from the same source file.
The intention of the UNICODE_STRING structure is to keep together both the address and size of a Unicode string, presumably to save on passing them as separate arguments for subsequent work with the string and to save on repeated re-reading of the whole string to rediscover its size. Indeed, the structure keeps two sizes. The Length member is the size in bytes of the array of Unicode characters at Buffer. If this array is null-terminated, which it explicitly need not be, then Length does not count the null terminator. The MaximumLength member is the size in bytes of the memory from Buffer onwards.
The RtlAppendUnicodeToString function appends the string from Source to whatever string is already described by Destination. The null terminator is appended too, but not counted in the new Length, if it fits within the MaximumLength.
The function succeeds trivially if Source is NULL.
If the string at Source has more than 0x7FFE characters, not counting its null terminator, it is too long to be represented faithfully by the 16-bit Length and MaximumLength, and so the function fails, returning STATUS_BUFFER_TOO_SMALL. If appending the characters from Source, not including its null terminator, to the Length bytes that are already in the Buffer for the Destination is not possible within this buffer’s MaximumLength bytes, then the function fails, again returning STATUS_BUFFER_TOO_SMALL.
The function appends the characters from Source, not including the null terminator, to the existing whole characters in the Length bytes at the Destination and adjusts Length to account for the added characters. If the new Length is at least a character short of the MaximumLength, the function appends a null terminator.
The defence against the string at Source being too long for representation by a UNICODE_STRING dates from version 5.2 and its introduction of RtlInitUnicodeStringEx. Earlier versions do not reject too long a string. They may instead append only a portion of the string.
All versions, if not all uses of the UNICODE_STRING, are better treated as having undefined behaviour if either Length or MaximumLength is not even. Versions before 5.2 are specially prone: for deciding whether a null terminator will fit after appending the string from Source, they ask only that the Length be less MaximumLength. If the latter is odd, the function may write one byte beyond the buffer’s end.
The kernel-mode implementation is in a non-paged section in all versions. Provided that both the UNICODE_STRING and the Unicode string that are addressed through the given arguments are in non-paged memory, the RtlAppendUnicodeToString function can safely be called at DISPATCH_LEVEL and higher. That it can has been documented by Microsoft since at least the DDK for Windows NT 3.51, but with only the one condition that “the Destination buffer must be resident.”