Adds initial support for reading parameters passed to consent UI
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
namespace Lithnet.CredentialProvider
|
||||
{
|
||||
internal class ConsentUICommandLineArgs
|
||||
{
|
||||
public uint AppInfoProcessId { get; set; }
|
||||
|
||||
public int Size { get; set; }
|
||||
|
||||
public long Address { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Principal;
|
||||
using Lithnet.CredentialProvider.Interop;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.System.Threading;
|
||||
using NativeMethods = Windows.Win32.PInvoke;
|
||||
|
||||
namespace Lithnet.CredentialProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// ConsentUIData is an abstract base class that represents all the different types of data structures that can be passed to the ConsentUI process for a UAC elevation prompt.
|
||||
/// The static members of the class can be used to retrieve the data structure passed to the ConsentUI process, or to determine if the current process is the ConsentUI process.
|
||||
/// The caller will be provided with one of the concrete implementations of this class, depending on the type of data structure that was passed to the ConsentUI process.
|
||||
/// Use the <see cref="ConsentUIData.ConsentUIType"/> property to determine the type of data structure and cast it to one of the concrete implementations.
|
||||
/// </summary>
|
||||
public abstract class ConsentUIData
|
||||
{
|
||||
private static bool? isConsentUI;
|
||||
private static ConsentUICommandLineArgs commandLineArgs;
|
||||
private protected ConsentUIStructureHeader header;
|
||||
private readonly byte[] rawData;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating the type of ConsentUI data structure
|
||||
/// </summary>
|
||||
public ConsentUIType Type => this.header.Type;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating the consent prompt type
|
||||
/// </summary>
|
||||
public int PromptType => this.header.PromptType;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a handle to the Window that was responsible for invoking the ConsentUI prompt
|
||||
/// </summary>
|
||||
public IntPtr HWnd => this.header.hWnd;
|
||||
|
||||
public ElevationType ElevationType => this.header.elevationType;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ID of the session where the ConsentUI prompt was originally invoked
|
||||
/// </summary>
|
||||
public int SessionId => this.header.sessionId;
|
||||
|
||||
private protected ConsentUIData(IntPtr pData, int expectedSize)
|
||||
{
|
||||
this.rawData = GetRawBytes(pData, expectedSize);
|
||||
this.header = Marshal.PtrToStructure<ConsentUIStructureHeader>(pData);
|
||||
|
||||
if (this.header.Size != expectedSize)
|
||||
{
|
||||
throw new InvalidDataException($"The size of the data structure {this.header.Size} does not match the expected size {expectedSize}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Windows Identity from the original caller requesting elevation
|
||||
/// </summary>
|
||||
/// <returns>A WindowsIdentity object that represents the user requesting elevation</returns>
|
||||
/// <exception cref="Win32Exception">Thrown when the user's token could not be obtained from the session information</exception>
|
||||
public WindowsIdentity GetWindowsIdentity()
|
||||
{
|
||||
var duplicatedToken = DuplicateHandleInternal(this.header.hToken);
|
||||
return new WindowsIdentity(duplicatedToken.DangerousGetHandle());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a raw byte array representing the ConsentUI data structure
|
||||
/// </summary>
|
||||
/// <returns>A byte array</returns>
|
||||
public byte[] GetRawData()
|
||||
{
|
||||
return this.rawData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string value that is packed at the end of the data structure if the offset is valid
|
||||
/// </summary>
|
||||
/// <param name="pData">A pointer to the start of the data structure</param>
|
||||
/// <param name="offset">The position from the start of the data structure where the string starts</param>
|
||||
/// <returns>A string containing all characters from the given offset up to the first null character found</returns>
|
||||
private protected string GetStringValueIfValid(IntPtr pData, int offset)
|
||||
{
|
||||
if (offset > 0)
|
||||
{
|
||||
this.ThrowOnInvalidOffset(offset);
|
||||
return Marshal.PtrToStringUni(IntPtr.Add(pData, offset));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an exception if the given offset is greater than the size of the data structure
|
||||
/// </summary>
|
||||
/// <param name="value">The value of the offset</param>
|
||||
/// <exception cref="InvalidDataException">Thrown when the value of the pointer is greater than the expected data size</exception>
|
||||
private protected void ThrowOnInvalidOffset(int value)
|
||||
{
|
||||
if (value >= this.header.Size)
|
||||
{
|
||||
throw new InvalidDataException($"Offset value {value} is greater than the expected data size {this.header.Size}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a ConsentUIData object from a previously-obtained raw byte representation
|
||||
/// </summary>
|
||||
/// <param name="consentUIDataStructure">The raw bytes of a supported ConsentUI data structure</param>
|
||||
/// <returns>A ConsentUIData object</returns>
|
||||
public static ConsentUIData GetConsentUIData(byte[] consentUIDataStructure)
|
||||
{
|
||||
SafeHGlobalHandle pData = SafeHGlobalHandle.AllocHGlobal(consentUIDataStructure.Length);
|
||||
Marshal.Copy(consentUIDataStructure, 0, pData.ToIntPtr(), consentUIDataStructure.Length);
|
||||
return CreateInstance(pData.ToIntPtr(), consentUIDataStructure.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data structure passed to the Consent UI process
|
||||
/// </summary>
|
||||
/// <returns>A ConsentUIData object</returns>
|
||||
public static ConsentUIData GetConsentUIData()
|
||||
{
|
||||
var pData = GetConsentUIData(out int structSize);
|
||||
return ConsentUIData.CreateInstance(pData.ToIntPtr(), structSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the current process is consent.exe, indicating that the provider is running inside an elevated UAC prompt
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static bool IsConsentUIParent()
|
||||
{
|
||||
if (isConsentUI == null)
|
||||
{
|
||||
var consentPath = Environment.ExpandEnvironmentVariables("%systemroot%\\system32\\consent.exe");
|
||||
var process = Process.GetCurrentProcess();
|
||||
var callingProcess = process.MainModule?.FileName;
|
||||
isConsentUI = string.Equals(callingProcess, consentPath, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return isConsentUI.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the raw bytes of the ConsentUI data structure
|
||||
/// </summary>
|
||||
/// <returns>A byte array</returns>
|
||||
public static byte[] GetConsentUIDataRawBytes()
|
||||
{
|
||||
var pData = GetConsentUIData(out int structSize);
|
||||
return GetRawBytes(pData.ToIntPtr(), structSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the command line of the consent.exe process to retrieve the data structure passed to it
|
||||
/// </summary>
|
||||
/// <param name="size">Returns the size of the data structure as reported in the command line arguments</param>
|
||||
/// <returns>A pointer to the newly created copy of the data structure</returns>
|
||||
/// <exception cref="InvalidOperationException">Thrown when either consent.exe is not the parent process</exception>
|
||||
/// <exception cref="ArgumentException">Throw when the arguments passed to consent.exe are invalid</exception>
|
||||
private static SafeHGlobalHandle GetConsentUIData(out int size)
|
||||
{
|
||||
if (!IsConsentUIParent())
|
||||
{
|
||||
throw new InvalidOperationException("The consent UI data can only be retrieved when consent.exe is the parent process");
|
||||
}
|
||||
|
||||
commandLineArgs ??= GetConsentUICommandLineArgs();
|
||||
|
||||
size = commandLineArgs.Size;
|
||||
return ReadMemoryFromProcess(commandLineArgs.AppInfoProcessId, commandLineArgs.Address, commandLineArgs.Size);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the command line arguments passed to the consent.exe process
|
||||
/// </summary>
|
||||
/// <returns>A ConsentUICommandLineArgs object containing the arguments parsed from the command line</returns>
|
||||
/// <exception cref="ArgumentException">Thrown when the arguments passed to consent.exe cannot be parsed or are of the incorrect number</exception>
|
||||
private static ConsentUICommandLineArgs GetConsentUICommandLineArgs()
|
||||
{
|
||||
var args = Environment.GetCommandLineArgs();
|
||||
|
||||
if (args.Length != 4)
|
||||
{
|
||||
throw new ArgumentException($"Unable to parse command line of consent.exe. The number of elements was incorrect\r\n{string.Join("\r\n", args)}");
|
||||
}
|
||||
|
||||
if (!uint.TryParse(args[1], out var appInfoPid))
|
||||
{
|
||||
throw new ArgumentException($"Unable to parse command line of consent.exe. The expected first element was not an integer\r\n{string.Join("\r\n", args)}");
|
||||
}
|
||||
|
||||
if (!int.TryParse(args[2], out var size))
|
||||
{
|
||||
throw new ArgumentException($"Unable to parse command line of consent.exe. The expected second element was not an integer\r\n{string.Join("\r\n", args)}");
|
||||
}
|
||||
|
||||
if (!long.TryParse(args[3], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var address))
|
||||
{
|
||||
throw new ArgumentException($"Unable to parse command line of consent.exe. The expected third element was not an integer\r\n{string.Join("\r\n", args)}");
|
||||
}
|
||||
|
||||
return new ConsentUICommandLineArgs
|
||||
{
|
||||
Address = address,
|
||||
AppInfoProcessId = appInfoPid,
|
||||
Size = size,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the memory from a raw pointer into a managed byte array
|
||||
/// </summary>
|
||||
/// <param name="pData">The pointer where the data copy must start</param>
|
||||
/// <param name="size">The number of bytes to copy</param>
|
||||
/// <returns>A copy of the raw memory returned as a managed byte array</returns>
|
||||
private static byte[] GetRawBytes(IntPtr pData, int size)
|
||||
{
|
||||
byte[] dataForExport = new byte[size];
|
||||
Marshal.Copy(pData, dataForExport, 0, size);
|
||||
return dataForExport;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the memory from a specified process
|
||||
/// </summary>
|
||||
/// <param name="processId">The ID of the process</param>
|
||||
/// <param name="address">The memory address to read</param>
|
||||
/// <param name="size">The size of the data at the specified memory address</param>
|
||||
/// <returns>A handle to a copy of the process memory</returns>
|
||||
/// <exception cref="Win32Exception">Thrown when the process could not be opened or the memory address could not be read</exception>
|
||||
/// <exception cref="InvalidDataException">Thrown when the size of the copied structure did not equal the expected size as passed to the method</exception>
|
||||
private static SafeHGlobalHandle ReadMemoryFromProcess(uint processId, long address, int size)
|
||||
{
|
||||
SafeHGlobalHandle pData = SafeHGlobalHandle.AllocHGlobal(size);
|
||||
var pAddress = new IntPtr(address);
|
||||
|
||||
SafeFileHandle hProcess = OpenProcessHandle(processId, PROCESS_ACCESS_RIGHTS.PROCESS_VM_READ);
|
||||
|
||||
unsafe
|
||||
{
|
||||
nuint numberOfBytesRead = 0;
|
||||
|
||||
if (!NativeMethods.ReadProcessMemory(hProcess, pAddress.ToPointer(), pData.ToIntPtr().ToPointer(), (nuint)size, &numberOfBytesRead))
|
||||
{
|
||||
int error = Marshal.GetLastWin32Error();
|
||||
throw new Win32Exception(error, $"Unable to read memory from process {processId}");
|
||||
}
|
||||
|
||||
if (numberOfBytesRead != (nuint)size)
|
||||
{
|
||||
throw new InvalidDataException($"Bytes read from memory {numberOfBytesRead} was not the expected structure size {size}");
|
||||
}
|
||||
}
|
||||
|
||||
return pData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a native handle to a process
|
||||
/// </summary>
|
||||
/// <param name="processId">The ID of the process</param>
|
||||
/// <param name="rights">The requested access rights</param>
|
||||
/// <returns>A safe handle to the process</returns>
|
||||
/// <exception cref="Win32Exception">Thrown when the process handle could not be obtained</exception>
|
||||
private static SafeFileHandle OpenProcessHandle(uint processId, PROCESS_ACCESS_RIGHTS rights)
|
||||
{
|
||||
var hProcess = NativeMethods.OpenProcess_SafeHandle(rights, false, processId);
|
||||
if (hProcess.IsInvalid)
|
||||
{
|
||||
int error = Marshal.GetLastWin32Error();
|
||||
throw new Win32Exception(error, $"Unable to open process {processId}");
|
||||
}
|
||||
|
||||
return hProcess;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of the appropriate subclass of ConsentUIData by reading the type from the data structure
|
||||
/// </summary>
|
||||
/// <param name="pData">A pointer to the data structure</param>
|
||||
/// <param name="expectedSize">The expected size of the data structure</param>
|
||||
/// <returns>A ConsentUIData object</returns>
|
||||
/// <exception cref="InvalidDataException">Thrown when the size of the expected data structure does not match the size reported in the structure itself</exception>
|
||||
private static ConsentUIData CreateInstance(IntPtr pData, int expectedSize)
|
||||
{
|
||||
var sizeReportedInStructure = Marshal.ReadInt32(pData, 0);
|
||||
|
||||
if (sizeReportedInStructure != expectedSize)
|
||||
{
|
||||
throw new InvalidDataException($"The expected size {expectedSize} did not match the size reported by the structure {sizeReportedInStructure}");
|
||||
}
|
||||
|
||||
var type = (ConsentUIType)Marshal.ReadInt32(pData, 4);
|
||||
|
||||
return type switch
|
||||
{
|
||||
ConsentUIType.Exe => new ConsentUIDataExe(pData, sizeReportedInStructure),
|
||||
ConsentUIType.Msi => new ConsentUIDataMsi(pData, sizeReportedInStructure),
|
||||
ConsentUIType.Com => new ConsentUIDataCom(pData, sizeReportedInStructure),
|
||||
ConsentUIType.Msix => new ConsentUIDataMsix(pData, sizeReportedInStructure),
|
||||
ConsentUIType.ActiveX => new ConsentUIDataActiveX(pData, sizeReportedInStructure),
|
||||
ConsentUIType.CredCollect => new ConsentUIDataCredCollect(pData, sizeReportedInStructure),
|
||||
_ => throw new InvalidDataException("The ConsentUI data structure was for an unknown type"),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Duplicates a handle passed in from the AppInfo service
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle to duplicate</param>
|
||||
/// <returns>A duplicated reference to the handle</returns>
|
||||
/// <exception cref="Win32Exception">Thrown when the handle could not be duplicated</exception>
|
||||
protected private static SafeHandle DuplicateHandleInternal(IntPtr handle)
|
||||
{
|
||||
commandLineArgs ??= GetConsentUICommandLineArgs();
|
||||
|
||||
var processHandle = OpenProcessHandle(commandLineArgs.AppInfoProcessId, PROCESS_ACCESS_RIGHTS.PROCESS_DUP_HANDLE);
|
||||
SafeFileHandle t = new(handle, false);
|
||||
|
||||
if (!NativeMethods.DuplicateHandle(processHandle, t, Process.GetCurrentProcess().SafeHandle, out var duplicatedToken, 0, false, DUPLICATE_HANDLE_OPTIONS.DUPLICATE_SAME_ACCESS))
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to duplicate the handle");
|
||||
}
|
||||
|
||||
return duplicatedToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace Lithnet.CredentialProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the data structure passed to consent UI when a user is trying to install an ActiveX control
|
||||
/// </summary>
|
||||
/// <remarks>This data structure is currently unknown and only the common header values are present</remarks>
|
||||
public class ConsentUIDataActiveX : ConsentUIData
|
||||
{
|
||||
internal ConsentUIDataActiveX(IntPtr pData, int expectedSize) : base(pData, expectedSize)
|
||||
{
|
||||
if (this.header.Type != ConsentUIType.ActiveX)
|
||||
{
|
||||
throw new InvalidOperationException("The data structure is not of type ActiveX");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Lithnet.CredentialProvider.Interop;
|
||||
|
||||
namespace Lithnet.CredentialProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the data structure passed to consent UI when a user is trying to elevate a COM component. This is typically seen when a user presses a 'shield' icon in something like the file security dialog to elevate permissions.
|
||||
/// </summary>
|
||||
public class ConsentUIDataCom : ConsentUIData
|
||||
{
|
||||
/// <summary>
|
||||
/// The path to the COM component that is requesting elevation
|
||||
/// </summary>
|
||||
public string ComComponentPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The resource path to the image to display in consent UI
|
||||
/// </summary>
|
||||
public string ImageResourcePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The path to the process that is hosting the COM component
|
||||
/// </summary>
|
||||
public string ProcessPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A user friendly description of the type of operation that will be performed by the elevation
|
||||
/// </summary>
|
||||
public string OperationType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The CLSID of the COM component
|
||||
/// </summary>
|
||||
public Guid ClsId { get; }
|
||||
|
||||
internal ConsentUIDataCom(IntPtr pData, int expectedSize) : base(pData, expectedSize)
|
||||
{
|
||||
if (this.header.Type != ConsentUIType.Com)
|
||||
{
|
||||
throw new InvalidOperationException("The data structure is not of type COM");
|
||||
}
|
||||
|
||||
var s = Marshal.PtrToStructure<ConsentUIStructureCom>(pData);
|
||||
|
||||
this.ComComponentPath = this.GetStringValueIfValid(pData, (int)s.oComComponentPath);
|
||||
this.ImageResourcePath = this.GetStringValueIfValid(pData, (int)s.oImageResourcePath);
|
||||
this.ProcessPath = this.GetStringValueIfValid(pData, (int)s.oProcessPath);
|
||||
this.OperationType = this.GetStringValueIfValid(pData, (int)s.oOperationType);
|
||||
this.ClsId = s.Clsid;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace Lithnet.CredentialProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the data structure passed to consent UI in a yet unknown scenario
|
||||
/// </summary>
|
||||
/// <remarks>This data structure is currently unknown and only the common header values are present</remarks>
|
||||
public class ConsentUIDataCredCollect : ConsentUIData
|
||||
{
|
||||
internal ConsentUIDataCredCollect(IntPtr pData, int expectedSize) : base(pData, expectedSize)
|
||||
{
|
||||
if (this.header.Type != ConsentUIType.CredCollect)
|
||||
{
|
||||
throw new InvalidOperationException("The data structure is not of type CredCollect");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Lithnet.CredentialProvider.Interop;
|
||||
|
||||
namespace Lithnet.CredentialProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the data structure passed to consent UI when a user is trying to elevate an executable
|
||||
/// </summary>
|
||||
public class ConsentUIDataExe : ConsentUIData
|
||||
{
|
||||
private IntPtr hFile;
|
||||
|
||||
/// <summary>
|
||||
/// The path to the process that the user has requested to be launched as an administrator
|
||||
/// </summary>
|
||||
public string ExecutablePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A currently unknown value. In most cases it seems to be the same as <see cref="ExecutablePath"/>
|
||||
/// </summary>
|
||||
public string Unknown1 { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The full command line, including arguments that will be used to launch the executable
|
||||
/// </summary>
|
||||
public string CommandLine { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A currently unknown parameter
|
||||
/// </summary>
|
||||
public string Unknown2 { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a handle to the executable that the user has requested to be launched as an administrator
|
||||
/// </summary>
|
||||
/// <returns>A handle to the executable</returns>
|
||||
public SafeHandle GetExecutableHandle()
|
||||
{
|
||||
return DuplicateHandleInternal(this.hFile);
|
||||
}
|
||||
|
||||
internal ConsentUIDataExe(IntPtr pData, int expectedSize) : base(pData, expectedSize)
|
||||
{
|
||||
if (this.header.Type != ConsentUIType.Exe)
|
||||
{
|
||||
throw new InvalidOperationException("The data structure is not of type EXE");
|
||||
}
|
||||
|
||||
var s = Marshal.PtrToStructure<ConsentUIStructureExe>(pData);
|
||||
|
||||
this.hFile = s.hFile;
|
||||
this.ExecutablePath = this.GetStringValueIfValid(pData, (int)s.oExecutablePath1);
|
||||
this.Unknown1 = this.GetStringValueIfValid(pData, (int)s.oExecutablePath2);
|
||||
this.CommandLine = this.GetStringValueIfValid(pData, (int)s.oCommandLine);
|
||||
this.Unknown2 = this.GetStringValueIfValid(pData, (int)s.oUnknown0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Lithnet.CredentialProvider.Interop;
|
||||
|
||||
namespace Lithnet.CredentialProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the data structure passed to consent UI when a user is trying to elevate an MSI installer
|
||||
/// </summary>
|
||||
public class ConsentUIDataMsi : ConsentUIData
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the product being installed
|
||||
/// </summary>
|
||||
public string ProductName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The version of the product being installed
|
||||
/// </summary>
|
||||
public string Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The locale of the product being installed
|
||||
/// </summary>
|
||||
public string Locale { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The publisher of the product being installed
|
||||
/// </summary>
|
||||
public string Publisher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The path to the MSI installer
|
||||
/// </summary>
|
||||
public string ExecutionPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The path to the original MSI file launched by the user
|
||||
/// </summary>
|
||||
public string OriginalMsi { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A currently unknown parameter
|
||||
/// </summary>
|
||||
public string Unknown1 { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A currently unknown parameter
|
||||
/// </summary>
|
||||
public string Unknown2 { get; }
|
||||
|
||||
internal ConsentUIDataMsi(IntPtr pData, int expectedSize) : base(pData, expectedSize)
|
||||
{
|
||||
if (this.header.Type != ConsentUIType.Msi)
|
||||
{
|
||||
throw new InvalidOperationException("The data structure is not of type MSI");
|
||||
}
|
||||
|
||||
var s = Marshal.PtrToStructure<ConsentUIStructureMsi>(pData);
|
||||
|
||||
this.ProductName = this.GetStringValueIfValid(pData, (int)s.oProductName);
|
||||
this.Version = this.GetStringValueIfValid(pData, (int)s.oVersion);
|
||||
this.Locale = this.GetStringValueIfValid(pData, (int)s.oLocale);
|
||||
this.Publisher = this.GetStringValueIfValid(pData, (int)s.oPublisher);
|
||||
this.ExecutionPath = this.GetStringValueIfValid(pData, (int)s.oExecutionPath);
|
||||
this.OriginalMsi = this.GetStringValueIfValid(pData, (int)s.oOriginalMsi);
|
||||
this.Unknown1 = this.GetStringValueIfValid(pData, (int)s.oUnknown1);
|
||||
this.Unknown2 = this.GetStringValueIfValid(pData, (int)s.oUnknown2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Lithnet.CredentialProvider.Interop
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the data structure passed to consent UI when a user is trying to elevate an MSIX package
|
||||
/// </summary>
|
||||
public class ConsentUIDataMsix : ConsentUIData
|
||||
{
|
||||
/// <summary>
|
||||
/// The path to the package being installed
|
||||
/// </summary>
|
||||
public string ExecutablePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the package being installed
|
||||
/// </summary>
|
||||
public string PackageName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The full command line, including any arguments used to launch the installer
|
||||
/// </summary>
|
||||
public string CommandLine { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A currently unknown parameter
|
||||
/// </summary>
|
||||
public string OtherName { get; }
|
||||
|
||||
internal ConsentUIDataMsix(IntPtr pData, int expectedSize) : base(pData, expectedSize)
|
||||
{
|
||||
if (this.header.Type != ConsentUIType.Msix)
|
||||
{
|
||||
throw new InvalidOperationException("The data structure is not of type MSIX");
|
||||
}
|
||||
|
||||
var s = Marshal.PtrToStructure<ConsentUIStructureMsix>(pData);
|
||||
|
||||
this.ExecutablePath = this.GetStringValueIfValid(pData, (int)s.oExecutablePath);
|
||||
this.PackageName = this.GetStringValueIfValid(pData, (int)s.oPackageName);
|
||||
this.CommandLine = this.GetStringValueIfValid(pData, (int)s.oCommandLine);
|
||||
this.OtherName = this.GetStringValueIfValid(pData, (int)s.oOtherName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Lithnet.CredentialProvider
|
||||
{
|
||||
public enum ElevationType
|
||||
{
|
||||
Unknown1 = 0,
|
||||
Unknown2 = 1,
|
||||
Consent = 2,
|
||||
Credentials = 3
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user