Module unicode_string

Source
Expand description

Interface around the Windows kernel string type known as UNICODE_STRING.

The UNICODE_STRING type was designed for the C programming language, which only knows about NUL-terminated buffers of characters. To determine the length of such a buffer, you need to iterate over all characters until finding the NUL. Bad enough? It gets worse: A classic buffer overflow occurs if the buffer contains no NUL, but an algorithm attempts to find it anyway.

To overcome these performance and security hazards, UNICODE_STRINGs consist of a buffer, a buffer capacity (“maximum length”), and a field to indicate the actually used length. Determining length and capacity is now as simple as querying the corresponding fields.

Length and capacity are 16-bit values and expressed in bytes. This allows for up to 32767 UTF-16 characters per string. However, note that additional space may be occupied by a NUL terminator or by UTF-16 characters outside the Basic Multilingual Plane (which require 4 bytes per character instead of 2).

32767 characters are way below the expected limit of most data structures. Therefore, most functions of this crate are fallible and may return NtStringError::BufferSizeExceedsU16.

While the string length is fully expressed by the length field of a UNICODE_STRING and a NUL termination is not required by the specification, this crate tries to NUL-terminate the internal buffer whenever possible. This defense-in-depth approach guards against external applications that mistakenly treat the internal buffer of a UNICODE_STRING as a NUL-terminated string. Only a few functions of this crate don’t NUL-terminate the internal buffer; they are marked as such.

UNICODE_STRING is implemented in 3 Rust structs:

  • NtUnicodeStr is an immutable reference to an existing UNICODE_STRING in memory (analogous to &str).
    You can also create a constant NtUnicodeStr using the nt_unicode_str macro (analogous to const STR: &'static str = "...").

  • NtUnicodeStrMut is a mutable reference to an existing UNICODE_STRING in memory (analogous to &mut str).
    As it doesn’t know how its internal buffer has been allocated, you can only do limited alterations (like removing a character), but not grow it beyond the buffer size.

  • NtUnicodeString is an owned and growable UNICODE_STRING (analogous to String).
    It can reallocate the internal buffer on demand and therefore implements all kinds of methods to alter its contents.

Deref and DerefMut traits have been implemented to make every NtUnicodeString act as NtUnicodeStrMut and every NtUnicodeStrMut act as NtUnicodeStr if required.

§Examples

You can work with these string types just like you work with other Rust string types:

let mut string = NtUnicodeString::try_from("Hello! ").unwrap();
string.try_push_str("Moin!").unwrap();
println!("{string}");

Conversions are also supported from raw u16 string buffers as well as the U16CStr and U16Str types of the widestring crate:

let abc = NtUnicodeString::try_from_u16(&[b'A' as u16, b'B' as u16, b'C' as u16]).unwrap();
let de = NtUnicodeString::try_from_u16_until_nul(&[b'D' as u16, b'E' as u16, 0]).unwrap();
let fgh = NtUnicodeString::try_from(u16cstr!("FGH")).unwrap();
let ijk = NtUnicodeString::try_from(u16str!("IJK")).unwrap();

Just like a String automatically dereferences to a &str when you pass it to an appropriate function, you can do the same with an NtUnicodeString and it will dereference to an &NtUnicodeStr:

let string = NtUnicodeString::try_from("My String").unwrap();
subfunction(&string);

fn subfunction(str_ref: &NtUnicodeStr) {
    println!("Hello from subfunction with \"{str_ref}\".");
}

Constant UNICODE_STRINGs can be created at compile-time. This provides strings with a 'static lifetime and saves a UTF-16 conversion at runtime:

const MY_CONSTANT_STRING: NtUnicodeStr<'static> = nt_unicode_str!("My Constant String");

Finally, you most likely want to pass your NtUnicodeStr, NtUnicodeStrMut or NtUnicodeString to an FFI function that expects a pointer to a UNICODE_STRING. Use the as_ptr or as_mut_ptr methods to get an immutable or mutable pointer.

Modules§

iter
Iterator implementations for NtUnicodeStr.

Structs§

NtUnicodeStr
An immutable reference to a UNICODE_STRING (equivalent of &str).
NtUnicodeStrMut
A mutable reference to a UNICODE_STRING (equivalent of &mut str).
NtUnicodeStringalloc
An allocated, owned, and growable variant of UNICODE_STRING (equivalent of String).