AppModelFunctions.ps1
# Copyright 2021 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. <# .SYNOPSIS Get an appcontainer profile for a specified package name. .DESCRIPTION This cmdlet gets an appcontainer profile for a specified package name. .PARAMETER Name Specify appcontainer name to use for the profile. .PARAMETER OpenAlways Specify to open the profile even if it doesn't exist. .INPUTS None .OUTPUTS NtApiDotNet.Win32.AppContainerProfile .EXAMPLE Get-AppContainerProfile Get appcontainer profiles for all installed packages. .EXAMPLE Get-AppContainerProfile -Name Package_aslkjdskjds Get an appcontainer profile from a package name. #> function Get-AppContainerProfile { [CmdletBinding(DefaultParameterSetName = "All")] Param( [parameter(ParameterSetName = "All")] [switch]$AllUsers, [parameter(Mandatory, Position = 0, ParameterSetName = "FromName")] [string]$Name, [parameter(ParameterSetName = "FromName")] [switch]$OpenAlways ) switch ($PSCmdlet.ParameterSetName) { "All" { [NtApiDotNet.Win32.AppContainerProfile]::GetAppContainerProfiles() | Write-Output } "FromName" { if ($OpenAlways) { $prof = [NtApiDotNet.Win32.AppContainerProfile]::OpenExisting($Name, $false) if (!$prof.IsSuccess) { $prof = [NtApiDotNet.Win32.AppContainerProfile]::Open($Name) } $prof | Write-Output } else { [NtApiDotNet.Win32.AppContainerProfile]::OpenExisting($Name) | Write-Output } } } } <# .SYNOPSIS Create a new appcontainer profile for a specified package name. .DESCRIPTION This cmdlet create a new appcontainer profile for a specified package name. If the profile already exists it'll open it. .PARAMETER Name Specify appcontainer name to use for the profile. .PARAMETER DisplayName Specify the profile display name. .PARAMETER Description Specify the profile description. .PARAMETER DeleteOnClose Specify the profile should be deleted when closed. .PARAMETER TemporaryProfile Specify to create a temporary profile. Close the profile after use to delete it. .INPUTS None .OUTPUTS NtApiDotNet.Win32.AppContainerProfile .EXAMPLE New-AppContainerProfile -Name Package_aslkjdskjds Create a new AppContainer profile with a specified name. .EXAMPLE Get-AppContainerProfile -TemporaryProfile Create a new temporary profile. #> function New-AppContainerProfile { [CmdletBinding(DefaultParameterSetName = "FromName")] Param( [parameter(Mandatory, Position = 0, ParameterSetName = "FromName")] [string]$Name, [parameter(Position = 1, ParameterSetName = "FromName")] [string]$DisplayName = "DisplayName", [parameter(Position = 2, ParameterSetName = "FromName")] [string]$Description = "Description", [parameter(ParameterSetName = "FromName")] [parameter(ParameterSetName = "FromTemp")] [NtApiDotNet.Sid[]]$Capabilities, [parameter(ParameterSetName = "FromName")] [switch]$DeleteOnClose, [parameter(Mandatory, ParameterSetName = "FromTemp")] [switch]$TemporaryProfile ) switch ($PSCmdlet.ParameterSetName) { "FromName" { $prof = [NtApiDotNet.Win32.AppContainerProfile]::Create($Name, $DisplayName, $Description, $Capabilities) if ($null -ne $prof) { $prof.DeleteOnClose = $DeleteOnClose Write-Output $prof } } "FromTemp" { [NtApiDotNet.Win32.AppContainerProfile]::CreateTemporary($Capabilities) | Write-Output } } } <# .SYNOPSIS Delete an appcontainer profile. .DESCRIPTION This cmdlet deletes an appcontainer profile for a specified package name or from its profile. .PARAMETER Name Specify appcontainer name to delete. .PARAMETER Profile Specify appcontainer profile to delete. .INPUTS None .OUTPUTS None .EXAMPLE Remove-AppContainerProfile -Name "profile_to_remove" Delete an appcontainer profiles by name. .EXAMPLE Remove-AppContainerProfile -Profile $prof Delete an appcontainer profiles from an existing profile. #> function Remove-AppContainerProfile { [CmdletBinding(DefaultParameterSetName = "FromName")] param( [parameter(Mandatory, Position = 0, ParameterSetName = "FromProfile")] [NtApiDotNet.Win32.AppContainerProfile]$Profile, [parameter(Mandatory, Position = 0, ParameterSetName = "FromName")] [string]$Name ) switch ($PSCmdlet.ParameterSetName) { "FromProfile" { $Profile.Delete() } "FromName" { [NtApiDotNet.Win32.AppContainerProfile]::Delete($Name) } } } <# .SYNOPSIS Start an application model application. .DESCRIPTION This cmdlet starts an application model application from it's application model ID. .PARAMETER AppModelId Specify the application model ID. .PARAMETER Argument Specify the argument for the application. .PARAMETER PassThru Specify to pass through a process object for the application. .INPUTS None .OUTPUTS NtApiDotNet.NtProcess .EXAMPLE Start-AppModelApplication -AppModelId "Microsoft.WindowsCalculator_8wekyb3d8bbwe!App" Start the Windows calculator. #> function Start-AppModelApplication { param( [parameter(Mandatory, Position = 0)] [string]$AppModelId, [parameter(Position = 1)] [string]$Argument = "", [switch]$PassThru ) try { $app_id = [NtApiDotNet.Win32.AppModel.AppModelUtils]::ActivateApplication($AppModelId, $Argument) if ($PassThru) { Get-NtProcess -ProcessId $app_id } } catch { Write-Error $_ } } <# .SYNOPSIS Query an app model policy for the a process. .DESCRIPTION This cmdlet queries the app model policy for a process. .PARAMETER Process Specify the process to get the app model policy for. .PARAMETER Policy Specify a specific policy to query. .INPUTS None .OUTPUTS NtApiDotNet.AppModelPolicy_PolicyValue .EXAMPLE Get-AppModelApplicationPolicy -Process $proc Query all app model policies. #> function Get-AppModelApplicationPolicy { [CmdletBinding(DefaultParameterSetName="All")] param( [parameter(Mandatory, Position = 0)] [NtApiDotNet.NtProcess]$Process, [parameter(Mandatory, Position = 1, ParameterSetName="FromPolicy")] [NtApiDotNet.AppModelPolicy_Type[]]$Policy ) try { Use-NtObject($token = Get-NtToken -Process $proc) { switch($PSCmdlet.ParameterSetName) { "All" { $token.AppModelPolicyDictionary | Write-Output } "FromPolicy" { foreach($pol in $Policy) { $token.GetAppModelPolicy($pol) | Write-Output } } } } } catch { Write-Error $_ } } function Check-FullTrust { param([xml]$Manifest) if ($Manifest -eq $null) { return $false } $nsmgr = [System.Xml.XmlNamespaceManager]::new($Manifest.NameTable) $nsmgr.AddNamespace("rescap", "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities") $Manifest.SelectSingleNode("//rescap:Capability[@Name='runFullTrust']", $nsmgr) -ne $null } function Get-AppExtensions { [CmdletBinding()] param( [parameter(Mandatory, ValueFromPipelineByPropertyName)] [xml]$Manifest ) PROCESS { if ($Manifest -eq $null) { return } $nsmgr = [System.Xml.XmlNamespaceManager]::new($Manifest.NameTable) $nsmgr.AddNamespace("desktop", "http://schemas.microsoft.com/appx/manifest/desktop/windows10") $nodes = $Manifest.SelectNodes("//desktop:Extension[@Category='windows.fullTrustProcess']", $nsmgr) foreach($node in $nodes) { Write-Output $node.GetAttribute("Executable") } } } function Get-FullTrustApplications { [CmdletBinding()] param( [parameter(Mandatory, ValueFromPipelineByPropertyName)] [xml]$Manifest, [parameter(Mandatory)] [string]$PackageFamilyName ) PROCESS { if ($Manifest -eq $null) { return } $nsmgr = [System.Xml.XmlNamespaceManager]::new($Manifest.NameTable) $nsmgr.AddNamespace("app", "http://schemas.microsoft.com/appx/manifest/foundation/windows10") $nodes = $Manifest.SelectNodes("//app:Application[@EntryPoint='Windows.FullTrustApplication']", $nsmgr) foreach($node in $nodes) { $id = $node.GetAttribute("Id") $props = @{ ApplicationUserModelId="$PackageFamilyName!$id"; Executable=$node.GetAttribute("Executable"); } Write-Output $(New-Object psobject -Property $props) } } } function Read-DesktopAppxManifest { [CmdletBinding()] param( [Parameter(Mandatory, ValueFromPipeline)] $Package, [switch]$AllUsers ) PROCESS { $Manifest = Get-AppxPackageManifest $Package if (-not $(Check-FullTrust $Manifest)) { return } $install_location = $Package.InstallLocation $profile_dir = "" if (-not $AllUsers) { $profile_dir = "$env:LOCALAPPDATA\Packages\$($Package.PackageFamilyName)" } $has_registry = (Test-Path "$install_location\registry.dat") -or ` (Test-Path "$install_location\user.dat") -or ` (Test-Path "$install_location\userclasses.dat") $vfs_files = @{} $vfs_root = "$install_location\VFS" if (Test-Path $vfs_root) { foreach($f in (Get-ChildItem $vfs_root)) { $name = $f.Name $vfs_files[$name] = Get-ChildItem -Recurse "$vfs_root\$name" } } $props = @{ Name=$Package.Name; Architecture=$Package.Architecture; Version=$Package.Version; Publisher=$Package.Publisher; PackageFamilyName=$Package.PackageFamilyName; InstallLocation=$install_location; Manifest=Get-AppxPackageManifest $Package; Applications=Get-FullTrustApplications $Manifest $Package.PackageFamilyName; Extensions=Get-AppExtensions $Manifest; VFSFiles=$vfs_files; HasRegistry=$has_registry; ProfileDir=$profile_dir; } New-Object psobject -Property $props } } <# .SYNOPSIS Get a list AppX packages with Desktop Bridge components. .DESCRIPTION This cmdlet gets a list of installed AppX packages which are either directly full trust applications or have an extension which can be used to run full trust applications. .PARAMETER AllUsers Specify getting information for all users, needs admin privileges. .INPUTS None .OUTPUTS Package results. .EXAMPLE Get-AppxDesktopBridge Get all desktop bridge AppX packages for current user. .EXAMPLE Get-AppxDesktopBridge -AllUsers Get all desktop bridge AppX packages for all users. #> function Get-AppxDesktopBridge { param([switch]$AllUsers) Get-AppxPackage -AllUsers:$AllUsers -PackageTypeFilter Main | Read-DesktopAppxManifest -AllUsers:$AllUsers } <# .SYNOPSIS Get list of package SIDs granted loopback exceptions. .DESCRIPTION This cmdlet gets the list of package SIDs which have been granted loopback exceptions. .INPUTS None .OUTPUTS NtApiDotNet.Sid[] .EXAMPLE Get-AppModelLoopbackException Get the list of loopback exception package SIDs. #> function Get-AppModelLoopbackException { [NtApiDotNet.Win32.AppModel.AppModelUtils]::GetLoopbackException() } <# .SYNOPSIS Add a package SID to the list of granted loopback exceptions. .DESCRIPTION This cmdlet adds a package SID to the list of granted loopback exceptions. .PARAMETER PackageSid The package SID to add. Can be an SDDL SID or a name. .INPUTS string[] .OUTPUTS None .EXAMPLE Add-AppModelLoopbackException -PackageSid $package_sid Add $package_sid to the list of loopback exceptions. .EXAMPLE Add-AppModelLoopbackException -PackageSid "ABC" Add package "ABC" to the list of loopback exceptions. #> function Add-AppModelLoopbackException { [CmdletBinding()] param( [Parameter(Mandatory, Position = 0, ValueFromPipeline)] [string]$PackageSid ) PROCESS { try { $sid = [NtApiDotNet.Win32.TokenUtils]::GetPackageSidFromName($PackageSid) [NtApiDotNet.Win32.AppModel.AppModelUtils]::AddLoopbackException($sid) } catch { Write-Error $_ } } } <# .SYNOPSIS Remove a package SID from the list of granted loopback exceptions. .DESCRIPTION This cmdlet removes a package SID from the list of granted loopback exceptions. .PARAMETER PackageSid The package SID to remove. .INPUTS string[] .OUTPUTS None .EXAMPLE Remove-AppModelLoopbackException -PackageSid $package_sid Remove $package_sid from the list of loopback exceptions. .EXAMPLE Remove-AppModelLoopbackException -PackageSid "ABC" Remove package "ABC" from the list of loopback exceptions. #> function Remove-AppModelLoopbackException { [CmdletBinding()] param( [Parameter(Mandatory, Position = 0, ValueFromPipeline)] [string]$PackageSid ) PROCESS { try { $sid = [NtApiDotNet.Win32.TokenUtils]::GetPackageSidFromName($PackageSid) [NtApiDotNet.Win32.AppModel.AppModelUtils]::RemoveLoopbackException($sid) } catch { Write-Error $_ } } } <# .SYNOPSIS Gets the execution alias information from a name. .DESCRIPTION This cmdlet looks up an execution alias and tries to parse its reparse point to extract internal information. .PARAMETER AliasName The alias name to lookup. Can be either a full path to the alias or a name which will be found in the WindowsApps folder. .EXAMPLE Get-ExecutionAlias ubuntu.exe Get the ubuntu.exe execution alias from local appdata. .EXAMPLE Get-ExecutionAlias c:\path\to\alias.exe Get the alias.exe execution alias from an absolute path. #> function Get-ExecutionAlias { Param( [Parameter(Mandatory = $true, Position = 0)] [string]$AliasName ) if (Test-Path $AliasName) { $path = Resolve-Path $AliasName } else { $path = $env:LOCALAPPDATA + "\Microsoft\WindowsApps\$AliasName" } Use-NtObject($file = Get-NtFile -Path $path -Win32Path -Options OpenReparsePoint, SynchronousIoNonAlert ` -Access GenericRead, Synchronize) { $file.GetReparsePoint() } } <# .SYNOPSIS Creates a new execution alias information or updates and existing one. .DESCRIPTION This cmdlet creates a new execution alias for a packaged application. .PARAMETER PackageName The name of the UWP package. .PARAMETER EntryPoint The entry point of the application .PARAMETER Target The target executable path .PARAMETER AppType The application type. .PARAMETER Version Version number .EXAMPLE Set-ExecutionAlias c:\path\to\alias.exe -PackageName test -EntryPoint test!test -Target c:\test.exe -Flags 48 -Version 3 Set the alias.exe execution alias. #> function Set-ExecutionAlias { Param( [Parameter(Mandatory = $true, Position = 0)] [string]$Path, [Parameter(Mandatory = $true, Position = 1)] [string]$PackageName, [Parameter(Mandatory = $true, Position = 2)] [string]$EntryPoint, [Parameter(Mandatory = $true, Position = 3)] [string]$Target, [NtApiDotNet.ExecutionAliasAppType]$AppType = "Desktop", [Int32]$Version = 3 ) $rp = [NtApiDotNet.ExecutionAliasReparseBuffer]::new($Version, $PackageName, $EntryPoint, $Target, $AppType) Use-NtObject($file = New-NtFile -Path $Path -Win32Path -Options OpenReparsePoint, SynchronousIoNonAlert ` -Access GenericWrite, Synchronize -Disposition OpenIf) { $file.SetReparsePoint($rp) } } |