diff --git a/PInvoke/NtDll/Winternl.UnicodeString.cs b/PInvoke/NtDll/Winternl.UnicodeString.cs new file mode 100644 index 00000000..61360113 --- /dev/null +++ b/PInvoke/NtDll/Winternl.UnicodeString.cs @@ -0,0 +1,254 @@ +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 + { + /// 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(); + + if (string.IsNullOrEmpty(value)) + SetHandle(Marshal.AllocCoTaskMem(structLen)); + else + SetHandle(InitMemForString(value, structLen)); + } + + internal SafeUNICODE_STRING(IntPtr ptr, bool own) : base(ptr, own) + { + } + + private SafeUNICODE_STRING() : base() + { + } + + /// Performs an implicit conversion from to . + /// The string value. + /// The resulting instance from the conversion. + public static implicit operator SafeUNICODE_STRING(string value) => new SafeUNICODE_STRING(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 len = unchecked((ushort)Marshal.ReadInt16(handle, 2)); + hProc = hProc == default ? GetCurrentProcess() : hProc; + using var mem = new SafeCoTaskMemString(len); + if (IsWow64Process(hProc)) + return NtWow64ReadVirtualMemory64(hProc, handle.Offset(Marshal.OffsetOf(typeof(UNICODE_STRING_WOW64), "Buffer").ToInt64()).ToInt64(), ((IntPtr)mem).ToInt32(), mem.Size, out _).Succeeded ? mem : string.Empty; + return ReadProcessMemory(hProc, handle.Offset(Marshal.OffsetOf(typeof(UNICODE_STRING), "Buffer").ToInt64()), 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() => Marshal.SizeOf(IsWow64Process() ? typeof(UNICODE_STRING_WOW64) : typeof(UNICODE_STRING)); + + internal static IntPtr InitMemForString(string value, int structLen) + { + // Collect lengths + var strLen = (ushort)StringHelper.GetByteCount(value, true, CharSet.Unicode); + + // Create mem and append string after struct + IntPtr mem = Marshal.AllocCoTaskMem(structLen + strLen); + IntPtr strOffset = mem.Offset(structLen); + StringHelper.Write(value, strOffset, out _, true, CharSet.Unicode, strLen); + RtlInitUnicodeString(mem, strOffset); + return mem; + } + + /// + protected override bool InternalReleaseHandle() { Marshal.FreeCoTaskMem(handle); return true; } + + private static bool IsWow64Process(HPROCESS hProc = default) => Kernel32.IsWow64Process(hProc == default ? GetCurrentProcess() : hProc, out var w) && w; + } + + /// 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(); + + IntPtr ICustomMarshaler.MarshalManagedToNative(object ManagedObj) => ManagedObj is string s ? SafeUNICODE_STRING.InitMemForString(s, SafeUNICODE_STRING.GetStructSize()) : 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 + */ + } +} \ No newline at end of file