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