The Process Explorer answer works once, but you probably want this to apply even after the computer is rebooted. To do that, you can use PowerShell:
Param (
[string[]]$ProcessNames,
[string]$DenyUsername
)
$cscode = @"
using System;
using System.Security;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
public class ProcessSecurity : NativeObjectSecurity
{
public ProcessSecurity(SafeHandle processHandle)
: base(false, ResourceType.KernelObject, processHandle, AccessControlSections.Access)
{
}
public void AddAccessRule(ProcessAccessRule rule)
{
base.AddAccessRule(rule);
}
// this is not a full impl- it only supports writing DACL changes
public void SaveChanges(SafeHandle processHandle)
{
Persist(processHandle, AccessControlSections.Access);
}
public override Type AccessRightType
{
get { return typeof(ProcessAccessRights); }
}
public override AccessRule AccessRuleFactory(System.Security.Principal.IdentityReference identityReference, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType type)
{
return new ProcessAccessRule(identityReference, (ProcessAccessRights)accessMask, isInherited, inheritanceFlags, propagationFlags, type);
}
public override Type AccessRuleType
{
get { return typeof(ProcessAccessRule); }
}
public override AuditRule AuditRuleFactory(System.Security.Principal.IdentityReference identityReference, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AuditFlags flags)
{
throw new NotImplementedException();
}
public override Type AuditRuleType
{
get { throw new NotImplementedException(); }
}
}
public class ProcessAccessRule : AccessRule
{
public ProcessAccessRule(IdentityReference identityReference, ProcessAccessRights accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType type)
: base(identityReference, (int)accessMask, isInherited, inheritanceFlags, propagationFlags, type)
{
}
public ProcessAccessRights ProcessAccessRights { get { return (ProcessAccessRights)AccessMask; } }
}
[Flags]
public enum ProcessAccessRights
{
STANDARD_RIGHTS_REQUIRED = (0x000F0000),
DELETE = (0x00010000), // Required to delete the object.
READ_CONTROL = (0x00020000), // Required to read information in the security descriptor for the object, not including the information in the SACL. To read or write the SACL, you must request the ACCESS_SYSTEM_SECURITY access right. For more information, see SACL Access Right.
WRITE_DAC = (0x00040000), // Required to modify the DACL in the security descriptor for the object.
WRITE_OWNER = (0x00080000), // Required to change the owner in the security descriptor for the object.
PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF, //All possible access rights for a process object.
PROCESS_CREATE_PROCESS = (0x0080), // Required to create a process.
PROCESS_CREATE_THREAD = (0x0002), // Required to create a thread.
PROCESS_DUP_HANDLE = (0x0040), // Required to duplicate a handle using DuplicateHandle.
PROCESS_QUERY_INFORMATION = (0x0400), // Required to retrieve certain information about a process, such as its token, exit code, and priority class (see OpenProcessToken, GetExitCodeProcess, GetPriorityClass, and IsProcessInJob).
PROCESS_QUERY_LIMITED_INFORMATION = (0x1000),
PROCESS_SET_INFORMATION = (0x0200), // Required to set certain information about a process, such as its priority class (see SetPriorityClass).
PROCESS_SET_QUOTA = (0x0100), // Required to set memory limits using SetProcessWorkingSetSize.
PROCESS_SUSPEND_RESUME = (0x0800), // Required to suspend or resume a process.
PROCESS_TERMINATE = (0x0001), // Required to terminate a process using TerminateProcess.
PROCESS_VM_OPERATION = (0x0008), // Required to perform an operation on the address space of a process (see VirtualProtectEx and WriteProcessMemory).
PROCESS_VM_READ = (0x0010), // Required to read memory in a process using ReadProcessMemory.
PROCESS_VM_WRITE = (0x0020), // Required to write to memory in a process using WriteProcessMemory.
SYNCHRONIZE = (0x00100000), // Required to wait for the process to terminate using the wait functions.
}
"@
Add-Type -TypeDefinition $cscode
$ProcessNames | % {
Get-Process -ProcessName $_ | % {
$handle = $_.SafeHandle
$acl = New-Object ProcessSecurity $handle
$ident = New-Object System.Security.Principal.NTAccount $DenyUsername
$ace = New-Object ProcessAccessRule ($ident, 'PROCESS_TERMINATE, PROCESS_SUSPEND_RESUME, WRITE_DAC', $false, 'None', 'None', 'Deny')
$acl.AddAccessRule($ace)
$acl.SaveChanges($handle)
}
}
It's based on this Stack Overflow answer. Basically, you provide it with the list of processes to protect and the user to protect against, and it fiddles the processes' ACLs appropriately. Save it as a .ps1
file (somewhere the user can read but not write), then put a batch file containing something like this in the user's Startup:
powershell \path\to\script.ps1 ('snippingtool', 'mspaint') 'Guest' -executionpolicy bypass
That protects snippingtool.exe
and mspaint.exe
(the Snipping Tool and Paint) from being killed by Guest.
Note that this has to run after those processes start. You might have to add a sleep 10
or so after the Param
block of the PowerShell script. Once it finishes, trying to kill those processes with Task Manager will cause this:
![access denied](https://cdn.statically.io/img/i.sstatic.net/JwTFI.png)
Also note that it will do nothing useful if the account you test it with is an administrator, or more precisely has SeDebugPrivilege
.
Clicking the X on their windows or using the applications' own close functionality will still make the processes exit, since all processes are free to decide to stop running. You may need to hide the notification area, as described in another answer. Also, since these important processes run as the guest user, that user is the process objects' owner, and will be able to adjust the ACL back anyway, or could use PROCESS_VM_WRITE
abilities to scribble over the processes' memory and crash them. Those could be solved by adding a blank ACE for OWNER RIGHTS
and by changing 'PROCESS_TERMINATE, PROCESS_SUSPEND_RESUME, WRITE_DAC'
to 'PROCESS_ALL_ACCESS'
, respectively.
Denying access to Task Manager via GPO would stop the user from using Task Manager (obviously) and is the most straightforward solution, but there's nothing stopping them from running their own program (or taskkill
) that doesn't obey Group Policy. It would be best if the processes you're trying to defend ran as a different user than the one you're trying to defend against.
Of course, if your guest is willing to go to all that trouble to circumvent these various "protections," you might have more of a social problem than a technical one.