Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
PowerShell Script to Remove and Block `CONSOLE_PROPS` in Windows Shortcuts
<#
.SYNOPSIS
Utility script to remove custom console customizations from shortcuts and additionally adjusts permissions to block them from being re–added.
.DESCRIPTION
This script can take a set of shortcuts from the pipeline or optionally crawl the user’s start menu for shortcuts having an embedded `CONSOLE_PROPS` data block. Shortcuts that have the datablock are stripped of its `CONSOLE_PROPS` then, if enabled, its permission inheritance is disabled, then changed to deny the user write access to the shortcut. In effect, this prevents re–adding the `CONSOLE_PROPS` to the shortcut and prevents the shortcut from diverging from the default terminal settings on the user.
If specified, this script will also remove any console customizations in the registry. Note: Denying subkey creation on `HKCU:\Console` will break some installers expecting to create subkeys here.
IMPORTANT:
The Windows Command Prompt and Windows PowerShell shortcuts in the user’s start menu must be recreated in order for the permissions adjustment to work properly. Otherwise, the combination of these shortcuts and denying the user write permission causes these shortcuts in particular to break and prevent them from being listed in the start menu.
.PARAMETER InputArray
Shortcuts to process. Optional.
.PARAMETER StartMenu
Include shortcuts in user’s start menu, in addition to shortcuts supplied by pipeline input.
.PARAMETER AdjustPermissions
Additionally remove write permission on processed shortcuts.
.PARAMETER Registry
Additionally remove any new subkeys from `HKCU:\Console`.
.EXAMPLE
remove-consoleprops -StartMenu -AdjustPermissions -Registry -ErrorAction Continue | select FullName
.INPUTS
System.IO.Path.FileInfo. Explicit set of shortcuts to process.
.OUTPUTS
System.IO.Path.FileInfo[]. Shortcuts processed.
.NOTES
Permissions are only applied if a shortcut had its `CONSOLE_PROPS` removed.
Manually creating shortcuts for Windows PowerShell and Command Prompt is preferred over changing the default shortcuts copied from the default user’s start menu.
Denying subkey creation on `HKCU:\Console` would be recommended, but it can cause some installers to error.
#>
[CmdLetBinding(
SupportsShouldProcess=$true
)]
param(
[Parameter(Mandatory=$false, ValueFromPipeline=$true)]
[IO.FileInfo[]] $InputArray,
[switch] $StartMenu,
[switch] $Registry,
[switch] $AdjustPermissions
)
# load dependencies
try {
[space.wtfbox.win32.ShellLink] | out-null;
}
catch {
write-verbose "Compiling helper assembly";
add-type -TypeDefinition (get-content -raw "$PSScriptRoot\space.wtfbox.win32.ShellLink.cs") | out-null;
}
# setup parameters
if ( $StartMenu -and -not $InputArray.Count ) {
$InputArray = (
"$env:appdata\microsoft\windows\start menu",
"$env:appdata\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar"
) |% {
get-childitem $_ -recurse -include *.lnk;
}
}
if ( $AdjustPermissions ) {
$user = [Security.Principal.WindowsIdentity]::GetCurrent();
$rights = [Security.AccessControl.FileSystemRights]"ReadAndExecute, Synchronize";
$rule = new-object Security.AccessControl.FileSystemAccessRule $user.Name, $rights, "Allow";
}
# process registry
if ( $Registry ) {
$console = (get-item HKCU:\).OpenSubKey("Console", $true)
$console.GetSubKeyNames() |% {
if ( $pscmdlet.ShouldProcess($_, "Remove Subkey") ) {
$console.DeleteSubKey($_);
}
}
}
if ( -not $InputArray ) {
# nothing to do
return;
}
# process shortcuts
$ShellLink = [space.wtfbox.win32.ShellLink];
$lnk = new-object $ShellLink (new-object -ComObject lnkfile);
try {
$InputArray |% {
$path = $_.FullName;
try {
# load and check for CONSOLE_PROPS
$lnk.Load($path, 2);
if ( -not $lnk.HasDataBlock($ShellLink::CONSOLE_PROPS) ) {
return;
}
# remove CONSOLE_PROPS and save
if ( $pscmdlet.ShouldProcess($path, "Remove CONSOLE_PROPS") ) {
write-verbose "Removing CONSOLE_PROPS from $path";
$lnk.RemoveDataBlock($ShellLink::CONSOLE_PROPS);
$hr = $lnk.Save();
}
if ( $AdjustPermissions -and $pscmdlet.ShouldProcess($path, "Adjust permissions") ) {
# deny current user write permissions
$item = get-item $path;
$acl = $item.GetAccessControl('access'); # https://stackoverflow.com/a/6646551
$acl.SetAccessRuleProtection($true, $true); # Disable inheritance, assume inherited
$item.SetAccessControl($acl);
$acl = $item.GetAccessControl('access'); # Reload ACL to obtain inherited permissions
$acl.RemoveAccessRuleAll($rule);
$acl.AddAccessRule($rule);
$item.SetAccessControl($acl);
}
$_;
}
catch {
"Failed to process $($path): $_" | write-error;
}
}
}
finally {
$lnk.Dispose();
}
using System;
using System.Runtime.InteropServices;
namespace space.wtfbox.win32 {
[ ComImport()
, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)
, Guid("0000010B-0000-0000-C000-000000000046") ]
public interface IPersistFile {
int GetClassID(out Guid pClassID);
[PreserveSig()]
int IsDirty();
int Load([MarshalAs(UnmanagedType.LPWStr)] string pszFileName, int dwMode);
int Save([MarshalAs(UnmanagedType.LPWStr)] string pszFileName, [MarshalAs(UnmanagedType.Bool)] bool fRemember);
int SaveCompleted([MarshalAs(UnmanagedType.LPWStr)] string pszFileName);
int GetCurFile(out IntPtr ppszFileName);
}
[ ComImport()
, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)
, Guid("45E2B4AE-B1C3-11D0-B92F-00A0C90312E1") ]
public interface IShellLinkDataList {
void AddDataBlock(IntPtr pDataBlock);
[PreserveSig()]
int CopyDataBlock(uint dwSig, out IntPtr ppDataBlock);
void RemoveDataBlock(uint dwSig);
void GetFlags(out int dwFlags);
void SetFlags(uint dwFlags);
}
// custom interface
public class ShellLink : IDisposable {
public ShellLink(object lnk) {
_handle = (IPersistFile)lnk;
}
public ShellLink(object lnk, string path, int mode) {
_handle = (IPersistFile)lnk;
Load(path,mode);
}
public void Dispose() {
Marshal.ReleaseComObject(_handle);
}
public void Load(string path, int mode){
_handle.Load(path, mode);
}
public void RemoveDataBlock(uint signature) {
(_handle as IShellLinkDataList).RemoveDataBlock(signature);
}
public bool HasDataBlock(uint signature) {
IntPtr block;
int hResult = (_handle as IShellLinkDataList).CopyDataBlock(signature,out block);
if ( hResult != 0 ) {
return false;
}
Marshal.FreeHGlobal(block);
return true;
}
public int Save() {
return _handle.Save(null,true);
}
private IPersistFile _handle;
public const uint CONSOLE_PROPS = 0xA0000002;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.