using System; using System.Runtime.InteropServices; using Vanara.Extensions; using Vanara.InteropServices; using static Vanara.PInvoke.Kernel32; namespace Vanara.PInvoke { public static partial class NtDll { /// Initializes a counted Unicode string. /// /// The buffer for a counted Unicode string to be initialized. The length is initialized to zero if the SourceString is not specified. /// /// Optional pointer to a null-terminated Unicode string with which to initialize the counted string. /// None // https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-rtlinitunicodestring void RtlInitUnicodeString( // PUNICODE_STRING DestinationString, PCWSTR SourceString ); [DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)] [PInvokeData("winternl.h", MSDNShortId = "NF:winternl.RtlInitUnicodeString")] public static extern void RtlInitUnicodeString(ref UNICODE_STRING DestinationString, [MarshalAs(UnmanagedType.LPWStr), Optional] string SourceString); /// Initializes a counted Unicode string. /// /// The buffer for a counted Unicode string to be initialized. The length is initialized to zero if the SourceString is not specified. /// /// Optional pointer to a null-terminated Unicode string with which to initialize the counted string. /// None // https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-rtlinitunicodestring void RtlInitUnicodeString( // PUNICODE_STRING DestinationString, PCWSTR SourceString ); [DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)] [PInvokeData("winternl.h", MSDNShortId = "NF:winternl.RtlInitUnicodeString")] public static extern void RtlInitUnicodeString(ref UNICODE_STRING_WOW64 DestinationString, [MarshalAs(UnmanagedType.LPWStr), Optional] string SourceString); /// Initializes a counted Unicode string. /// /// The buffer for a counted Unicode string to be initialized. The length is initialized to zero if the SourceString is not specified. /// /// Optional pointer to a null-terminated Unicode string with which to initialize the counted string. /// None // https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-rtlinitunicodestring void RtlInitUnicodeString( // PUNICODE_STRING DestinationString, PCWSTR SourceString ); [DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)] [PInvokeData("winternl.h", MSDNShortId = "NF:winternl.RtlInitUnicodeString")] public static extern void RtlInitUnicodeString([In, Out] IntPtr DestinationString, [MarshalAs(UnmanagedType.LPWStr), Optional] string SourceString); /// Initializes a counted Unicode string. /// /// The buffer for a counted Unicode string to be initialized. The length is initialized to zero if the SourceString is not specified. /// /// Optional pointer to a null-terminated Unicode string with which to initialize the counted string. /// None // https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-rtlinitunicodestring void RtlInitUnicodeString( // PUNICODE_STRING DestinationString, PCWSTR SourceString ); [DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)] [PInvokeData("winternl.h", MSDNShortId = "NF:winternl.RtlInitUnicodeString")] public static extern void RtlInitUnicodeString([In, Out] IntPtr DestinationString, [In, Optional] IntPtr SourceString); /// The UNICODE_STRING structure is used to define Unicode strings. /// /// /// The UNICODE_STRING structure is used to pass Unicode strings. Use RtlUnicodeStringInit or RtlUnicodeStringInitEx to /// initialize a UNICODE_STRING structure. /// /// If the string is null-terminated, Length does not include the trailing null character. /// /// The MaximumLength is used to indicate the length of Buffer so that if the string is passed to a conversion routine /// such as RtlAnsiStringToUnicodeString the returned string does not exceed the buffer size. /// /// // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wudfwdm/ns-wudfwdm-_unicode_string typedef struct // _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWCH Buffer; } UNICODE_STRING; [PInvokeData("wudfwdm.h", MSDNShortId = "b02f29a9-1049-4e29-aac3-72bf0c70a21e")] [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct UNICODE_STRING { /// The length, in bytes, of the string stored in Buffer. public ushort Length; /// The length, in bytes, of Buffer. public ushort MaximumLength; /// Pointer to a wide-character string. public IntPtr Buffer; /// public override string ToString() => StringHelper.GetString(Buffer, CharSet.Unicode, MaximumLength) ?? string.Empty; /// Extracts the string value from this structure by reading process specific memory. /// The process handle of the process from which to read the memory. /// A that has the value. public string ToString(HPROCESS hProc) { using var mem = new SafeCoTaskMemString(MaximumLength); return ReadProcessMemory(hProc, Buffer, mem, mem.Size, out _) ? mem : string.Empty; } } /// The UNICODE_STRING structure is used to define Unicode strings. /// /// /// The UNICODE_STRING structure is used to pass Unicode strings. Use RtlUnicodeStringInit or RtlUnicodeStringInitEx to /// initialize a UNICODE_STRING structure. /// /// If the string is null-terminated, Length does not include the trailing null character. /// /// The MaximumLength is used to indicate the length of Buffer so that if the string is passed to a conversion routine /// such as RtlAnsiStringToUnicodeString the returned string does not exceed the buffer size. /// /// // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wudfwdm/ns-wudfwdm-_unicode_string typedef struct // _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWCH Buffer; } UNICODE_STRING; [PInvokeData("wudfwdm.h", MSDNShortId = "b02f29a9-1049-4e29-aac3-72bf0c70a21e")] [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct UNICODE_STRING_WOW64 { /// The length, in bytes, of the string stored in Buffer. public ushort Length; /// The length, in bytes, of Buffer. public ushort MaximumLength; /// Pointer to a wide-character string. public long Buffer; /// Extracts the string value from this structure by reading process specific memory. /// The process handle of the process from which to read the memory. /// A that has the value. public string ToString(HPROCESS hProc) { using var mem = new SafeCoTaskMemString(MaximumLength); return NtWow64ReadVirtualMemory64(hProc, Buffer, ((IntPtr)mem).ToInt32(), mem.Size, out _).Succeeded ? mem : string.Empty; } } /// /// Provides an abstraction for both and that converts easily with /// . /// /// public class SafeUNICODE_STRING : SafeHANDLE { private readonly int size; /// Initializes a new instance of the class with a string value. /// The string value. public SafeUNICODE_STRING(string value) : base(IntPtr.Zero, true) { var structLen = GetStructSize(GetCurrentProcess()); if (string.IsNullOrEmpty(value)) SetHandle(Marshal.AllocCoTaskMem(size = structLen)); else SetHandle(InitMemForString(value, structLen, out size)); } internal SafeUNICODE_STRING(IntPtr ptr, bool own) : base(ptr, own) { } private SafeUNICODE_STRING() : base() { } /// The length, in bytes, of the string stored in Buffer. public ushort Length => IsInvalid ? (ushort)0 : unchecked((ushort)Marshal.ReadInt16(handle, 0)); /// The length, in bytes, of Buffer. public ushort MaximumLength => IsInvalid ? (ushort)0 : unchecked((ushort)Marshal.ReadInt16(handle, 2)); /// The size of the allocated memory holding the structure and the string. public int Size => size; /// Performs an implicit conversion from to . /// The string value. /// The resulting instance from the conversion. public static implicit operator SafeUNICODE_STRING(string value) => new(value); /// Performs an implicit conversion from to . /// The value. /// The resulting instance from the conversion. public static implicit operator string(SafeUNICODE_STRING value) => value?.ToString(default); /// Extracts the string value from this structure by reading process specific memory. /// The process handle of the process from which to read the memory. /// A that has the value. public string ToString(HPROCESS hProc) { if (IsInvalid) return null; var maxlen = unchecked((ushort)Marshal.ReadInt16(handle, 2)); hProc = hProc == default ? GetCurrentProcess() : hProc; var bufOffset = GetBufferOffset(hProc); var bufPtr = Marshal.ReadIntPtr(handle, bufOffset); if (hProc == GetCurrentProcess()) return StringHelper.GetString(bufPtr, CharSet.Unicode, MaximumLength) ?? string.Empty; using var mem = new SafeCoTaskMemString(maxlen); if (hProc.IsWow64()) return NtWow64ReadVirtualMemory64(hProc, bufPtr.ToInt64(), ((IntPtr)mem).ToInt32(), mem.Size, out _).Succeeded ? mem : string.Empty; return ReadProcessMemory(hProc, bufPtr, mem, mem.Size, out _) ? mem : string.Empty; } /// Extracts the string value from this structure by reading process specific memory. /// A that has the value. public override string ToString() => ToString(default); internal static int GetStructSize(HPROCESS hProc) => Marshal.SizeOf(hProc.IsWow64() ? typeof(UNICODE_STRING_WOW64) : typeof(UNICODE_STRING)); internal static int GetBufferOffset(HPROCESS hProc) => Marshal.OffsetOf(hProc.IsWow64() ? typeof(UNICODE_STRING_WOW64) : typeof(UNICODE_STRING), "Buffer").ToInt32(); internal static IntPtr InitMemForString(string value, int structLen, out int allocatedSize) { // Collect lengths var strLen = StringHelper.GetByteCount(value, true, CharSet.Unicode); // Create mem and append string after struct IntPtr mem = Marshal.AllocCoTaskMem(allocatedSize = structLen + strLen); IntPtr strOffset = mem.Offset(structLen); Marshal.WriteInt16(mem, 0, (short)(strLen - 2)); Marshal.WriteInt16(mem, 2, (short)strLen); StringHelper.Write(value, strOffset, out _, true, CharSet.Unicode, strLen); Marshal.WriteIntPtr(mem, GetBufferOffset(GetCurrentProcess()), strOffset); return mem; } /// protected override bool InternalReleaseHandle() { Marshal.FreeCoTaskMem(handle); return true; } } /// A custom marshaler for functions using UNICODE_STRING so that managed strings can be used. /// internal class UnicodeStringMarshaler : ICustomMarshaler { public static ICustomMarshaler GetInstance(string _) => new UnicodeStringMarshaler(); void ICustomMarshaler.CleanUpManagedData(object ManagedObj) { } void ICustomMarshaler.CleanUpNativeData(IntPtr pNativeData) => Marshal.FreeCoTaskMem(pNativeData); int ICustomMarshaler.GetNativeDataSize() => SafeUNICODE_STRING.GetStructSize(GetCurrentProcess()); IntPtr ICustomMarshaler.MarshalManagedToNative(object ManagedObj) => ManagedObj is string s ? SafeUNICODE_STRING.InitMemForString(s, SafeUNICODE_STRING.GetStructSize(GetCurrentProcess()), out _) : IntPtr.Zero; object ICustomMarshaler.MarshalNativeToManaged(IntPtr pNativeData) => new SafeUNICODE_STRING(pNativeData, false).ToString(); } /* RtlAppendUnicodeStringToString RtlCompareUnicodeString RtlConvertSidToUnicodeString RtlCopyUnicodeString RtlCreateUnicodeString RtlDowncaseUnicodeString RtlEqualUnicodeString RtlFreeUnicodeString RtlHashUnicodeString RtlInt64ToUnicodeString RtlIntegerToUnicodeString RtlOemStringToUnicodeString RtlPrefixUnicodeString RtlUnicodeStringToAnsiString RtlUnicodeStringToCountedOemString RtlUnicodeStringToInteger RtlUnicodeStringToOemString RtlUpcaseUnicodeString RtlUpcaseUnicodeStringToCountedOemString RtlUpcaseUnicodeStringToOemString */ } }