Skip to content

Instantly share code, notes, and snippets.

@rosberglinhares
Created March 17, 2018 13:23
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rosberglinhares/239547aadfe08cf60f11025ffb3fc969 to your computer and use it in GitHub Desktop.
Save rosberglinhares/239547aadfe08cf60f11025ffb3fc969 to your computer and use it in GitHub Desktop.
PowerShell script to change the type of the passed services to 'Own Process'. In this way, it is possible to check how much memory is specifically allocated to each service.
<#
.SYNOPSIS
Enables privileges in the access token of the current process. This allows the process to perform system-level actions that it could not previously.
This code was adapted from the following link: https://msdn.microsoft.com/en-us/library/windows/desktop/aa446619(v=vs.85).aspx.
.PARAMETER Privilege
Specifies, as a string array, the privileges to be enabled. More information about privileges can be found at
https://msdn.microsoft.com/en-us/library/windows/desktop/bb530716(v=vs.85).aspx.
.NOTES
Author: Author: Rosberg Linhares (rosberglinhares@gmail.com)
Date: March 16, 2018
#>
param (
[parameter(Mandatory=$true)]
[ValidateSet('SeAssignPrimaryTokenPrivilege', 'SeAuditPrivilege', 'SeBackupPrivilege', 'SeChangeNotifyPrivilege', 'SeCreateGlobalPrivilege', 'SeCreatePagefilePrivilege',
'SeCreatePermanentPrivilege', 'SeCreateSymbolicLinkPrivilege', 'SeCreateTokenPrivilege', 'SeDebugPrivilege', 'SeEnableDelegationPrivilege', 'SeImpersonatePrivilege',
'SeIncreaseBasePriorityPrivilege', 'SeIncreaseQuotaPrivilege', 'SeIncreaseWorkingSetPrivilege', 'SeLoadDriverPrivilege', 'SeLockMemoryPrivilege',
'SeMachineAccountPrivilege', 'SeManageVolumePrivilege', 'SeProfileSingleProcessPrivilege', 'SeRelabelPrivilege', 'SeRemoteShutdownPrivilege', 'SeRestorePrivilege',
'SeSecurityPrivilege', 'SeShutdownPrivilege', 'SeSyncAgentPrivilege', 'SeSystemEnvironmentPrivilege', 'SeSystemProfilePrivilege', 'SeSystemtimePrivilege',
'SeTakeOwnershipPrivilege', 'SeTcbPrivilege', 'SeTimeZonePrivilege', 'SeTrustedCredManAccessPrivilege', 'SeUndockPrivilege', 'SeUnsolicitedInputPrivilege')]
[string[]] $Privilege
)
$ErrorActionPreference = 'Stop' # Stops executing on error instead of silent continue.
Set-StrictMode -Version Latest # Enforces coding rules in expressions, scripts, and script blocks. Uninitialized variables are not permitted.
$winApiAdvApi32Code = @'
using System;
using System.Runtime.InteropServices;
namespace WinApi
{
public class AdvApi32
{
// https://www.pinvoke.net/default.aspx/Structures/TOKEN_PRIVILEGES.html
public const UInt32 SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x00000001;
public const UInt32 SE_PRIVILEGE_ENABLED = 0x00000002;
public const UInt32 SE_PRIVILEGE_REMOVED = 0x00000004;
public const UInt32 SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000;
public const int MAX_PRIVILEGES = 10;
// https://www.pinvoke.net/default.aspx/advapi32/AdjustTokenPrivileges.html
[StructLayout(LayoutKind.Sequential)]
public struct LUID
{
public Int32 LowPart;
public UInt32 HighPart;
}
// https://www.pinvoke.net/default.aspx/Structures/LUID_AND_ATTRIBUTES.html
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct LUID_AND_ATTRIBUTES
{
public LUID Luid;
public UInt32 Attributes;
}
// https://www.pinvoke.net/default.aspx/Structures/TOKEN_PRIVILEGES.html
public struct TOKEN_PRIVILEGES
{
public UInt32 PrivilegeCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=MAX_PRIVILEGES)]
public LUID_AND_ATTRIBUTES [] Privileges;
}
// https://www.pinvoke.net/default.aspx/advapi32/OpenProcessToken.html
[DllImport("advapi32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool OpenProcessToken(IntPtr ProcessHandle,
UInt32 DesiredAccess, out IntPtr TokenHandle);
// https://www.pinvoke.net/default.aspx/advapi32/LookupPrivilegeValue.html
[DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName,
out LUID lpLuid);
// https://www.pinvoke.net/default.aspx/advapi32/AdjustTokenPrivileges.html
[DllImport("advapi32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AdjustTokenPrivileges(IntPtr TokenHandle,
[MarshalAs(UnmanagedType.Bool)]bool DisableAllPrivileges,
ref TOKEN_PRIVILEGES NewState,
UInt32 Zero,
IntPtr Null1,
IntPtr Null2);
}
}
'@
$winApiKernel32Code = @'
using System;
using System.Security;
using System.Runtime.InteropServices;
using System.Runtime.ConstrainedExecution;
namespace WinApi
{
public class Kernel32
{
// https://www.pinvoke.net/default.aspx/Constants/WINERROR.html
public const int ERROR_SUCCESS = 0;
public const int ERROR_NOT_ALL_ASSIGNED = 1300;
// https://www.pinvoke.net/default.aspx/kernel32/CloseHandle.html
[DllImport("kernel32.dll", SetLastError=true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
}
}
'@
Add-Type -TypeDefinition $winApiAdvApi32Code
Add-Type -TypeDefinition $winApiKernel32Code
function Throw-WinApiError([int] $Win32ErrorCode, [string] $FunctionName)
{
$errorMessage = [ComponentModel.Win32Exception]$Win32ErrorCode
throw "Error executing $($FunctionName): $errorMessage"
}
$processHandle = (Get-Process -Id $PID).Handle
$tokenHandle = 0
if (![WinApi.AdvApi32]::OpenProcessToken($processHandle, [Security.Principal.TokenAccessLevels]::AdjustPrivileges, [ref] $tokenHandle))
{
Throw-WinApiError ([Runtime.InteropServices.Marshal]::GetLastWin32Error()) 'OpenProcessToken'
}
try
{
$tokenPrivileges = New-Object WinApi.AdvApi32+TOKEN_PRIVILEGES
$tokenPrivileges.PrivilegeCount = $Privilege.Count
$tokenPrivileges.Privileges = [WinApi.AdvApi32+LUID_AND_ATTRIBUTES[]]::New([WinApi.AdvApi32]::MAX_PRIVILEGES)
for ($index = 0; $index -le $Privilege.Count - 1; $index++)
{
$privilegeLocallyUniqueId = New-Object WinApi.AdvApi32+LUID
if (![WinApi.AdvApi32]::LookupPrivilegeValue($null, $Privilege[$index], [ref] $privilegeLocallyUniqueId))
{
Throw-WinApiError ([Runtime.InteropServices.Marshal]::GetLastWin32Error()) 'LookupPrivilegeValue'
}
$locallyUniqueIdAndAttr = New-Object WinApi.AdvApi32+LUID_AND_ATTRIBUTES
$locallyUniqueIdAndAttr.Luid = $privilegeLocallyUniqueId
$locallyUniqueIdAndAttr.Attributes = [WinApi.AdvApi32]::SE_PRIVILEGE_ENABLED
$tokenPrivileges.Privileges[$index] = $locallyUniqueIdAndAttr
}
if (![WinApi.AdvApi32]::AdjustTokenPrivileges($tokenHandle, $false, [ref] $tokenPrivileges, 0, [IntPtr]::Zero, [IntPtr]::Zero))
{
Throw-WinApiError ([Runtime.InteropServices.Marshal]::GetLastWin32Error()) 'AdjustTokenPrivileges'
}
else
{
$successErrorCode = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
if ($successErrorCode -eq [WinApi.Kernel32]::ERROR_NOT_ALL_ASSIGNED)
{
throw 'The token does not have one or more of the specified privileges.'
}
}
}
finally
{
if (![WinApi.Kernel32]::CloseHandle($tokenHandle))
{
Throw-WinApiError ([Runtime.InteropServices.Marshal]::GetLastWin32Error()) 'CloseHandle'
}
}
<#
.SYNOPSIS
Changes the type of the passed services to 'Own Process'. In this way, it is possible to check how much memory is specifically allocated to each service.
After this cmdlet is used, it is necessary to restart the machine to see the results.
.NOTES
Author: Rosberg Linhares (rosberglinhares@gmail.com)
Date: March 16, 2018
#>
param (
[parameter(Mandatory=$true)]
[string[]] $ServiceName
)
$ErrorActionPreference = 'Stop' # Stops executing on error instead of silent continue.
Set-StrictMode -Version Latest # Enforces coding rules in expressions, scripts, and script blocks. Uninitialized variables are not permitted.
New-Variable ConstChangeServiceTypeOwnProcess -Option ReadOnly -Value 16 -Force
New-Variable ConstChangeServiceReturnSuccess -Option ReadOnly -Value 0 -Force
New-Variable ConstWindowsRegistryServiceRelativePathFormat -Option ReadOnly -Value 'SYSTEM\CurrentControlSet\services\{0}' -Force
New-Variable ConstEnablePrivilegeScriptFilePath -Option ReadOnly -Value (Join-Path $PSScriptRoot 'Enable-Privilege.ps1') -Force
function Set-ServiceTypeToOwnProcess([string] $ServiceName)
{
function Set-ServiceTypeToOwnProcessByCim($Win32Service)
{
$serviceFullName = "$($Win32Service.Name) - $($Win32Service.DisplayName)"
Write-Verbose "[$serviceFullName] Changing type via Cim"
$result = $Win32Service | Invoke-CimMethod -MethodName 'Change' -Arguments @{ ServiceType = $ConstChangeServiceTypeOwnProcess } -Verbose:$false
if ($result.ReturnValue -eq $ConstChangeServiceReturnSuccess)
{
Write-Verbose "[$serviceFullName] Changed to Own Process successfully"
return $true
}
else
{
Write-Warning "[$serviceFullName] Failed to change service to Own Process via Cim. Return value = $($result.ReturnValue)"
return $false
}
}
function Set-ServiceTypeToOwnProcessByWindowsRegistry($Win32Service)
{
$serviceFullName = "$($Win32Service.Name) - $($Win32Service.DisplayName)"
Write-Verbose "[$serviceFullName] Changing type via Windows Registry"
try
{
$registryServicePath = "HKLM:\$($ConstWindowsRegistryServiceRelativePathFormat -f $ServiceName)"
Set-ItemProperty -Path $registryServicePath -Name 'Type' -Value $ConstChangeServiceTypeOwnProcess
Write-Verbose "[$serviceFullName] Service changed to Own Process successfully"
return $true
}
catch
{
Write-Warning "[$serviceFullName] Failed to change service to Own Process via Windows Registry. Error message = $($_.Exception.Message)"
return $false
}
}
function Grant-FullControlRightsOnServiceRegistryKeyToCurrentUser($Win32Service)
{
$serviceFullName = "$($Win32Service.Name) - $($Win32Service.DisplayName)"
Write-Verbose "[$serviceFullName] Granting full control rights on the service registry key"
try
{
# SeTakeOwnershipPrivilege: Necessary to change the current owner of a registry key
# SeRestorePrivilege: Necessary to restore the owner to 'NT AUTHORITY\SYSTEM'
& $ConstEnablePrivilegeScriptFilePath -Privilege SeTakeOwnershipPrivilege, SeRestorePrivilege
$registryServiceRelativePath = $ConstWindowsRegistryServiceRelativePathFormat -f $ServiceName
$registryKey = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($registryServiceRelativePath, [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree, [Security.AccessControl.RegistryRights]::TakeOwnership)
try
{
$accessControl = $registryKey.GetAccessControl()
$currentUser = [Security.Principal.NTAccount][Security.Principal.WindowsIdentity]::GetCurrent().Name
$previousOwner = [Security.Principal.NTAccount]$accessControl.Owner
# It is necessary to change the current owner for modifying access rights on the key
$accessControl.SetOwner($currentUser)
$registryKey.SetAccessControl($accessControl)
# Add FullControl rights to the current user
$accessRule = New-Object System.Security.AccessControl.RegistryAccessRule($currentUser, [Security.AccessControl.RegistryRights]::FullControl, [Security.AccessControl.AccessControlType]::Allow)
$accessControl.SetAccessRule($accessRule)
$registryKey.SetAccessControl($accessControl)
# Restore the previous owner
$accessControl.SetOwner($previousOwner)
$registryKey.SetAccessControl($accessControl)
}
finally
{
$registryKey.Dispose()
}
Write-Verbose "[$serviceFullName] Full control rights granted on the service registry key successfully"
return $true
}
catch
{
Write-Warning "[$serviceFullName] Failed to grant full control rights on the service registry key. Error message = $($_.Exception.Message)"
return $false
}
}
$win32Service = Get-CimInstance -ClassName Win32_Service -Filter "Name = '$ServiceName'" -Verbose:$false
if ($win32Service)
{
if (!(Set-ServiceTypeToOwnProcessByCim $win32Service))
{
if (!(Set-ServiceTypeToOwnProcessByWindowsRegistry $win32Service))
{
if (Grant-FullControlRightsOnServiceRegistryKeyToCurrentUser $win32Service)
{
Set-ServiceTypeToOwnProcessByWindowsRegistry $win32Service | Out-Null
}
}
}
}
else
{
Write-Warning "[$ServiceName] Service not found"
}
}
$ServiceName | ForEach-Object { Set-ServiceTypeToOwnProcess $_ }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment