はじめに
PowerShellのスクリプトで、実行しているプロセスに関連付けられているアクセストークンの特権を変更する必要があって、方法を調べてみるとスクリプト内で特権を変更する方法を見つけて、それを少し改良してみた。
【参考】
ソース
function Set-Privilege {
param([string]$Privilege, [bool]$Enable)
$definition = @'
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
public static class TempClass
{
public static void AdjustPrivilege(string privilegeName, bool enable)
{
NativeMethods.LUID priLuid;
if (!NativeMethods.LookupPrivilegeValue(null, privilegeName, out priLuid))
throw new ArgumentException("指定された特権名は存在しません。", "privilegeName");
var curProc = Process.GetCurrentProcess();
if (curProc.Handle == IntPtr.Zero)
throw new InvalidOperationException("プロセスハンドルが取得できませんでした。");
SafeProcessTokenHandle hToken;
if (!NativeMethods.OpenProcessToken(curProc.Handle,
NativeMethods.TOKEN_ADJUST_PRIVILEGES | NativeMethods.TOKEN_QUERY,
out hToken))
throw new InvalidOperationException("トークンハンドルが取得できませんでした。", new Win32Exception());
using (hToken)
{
var tokenPrivileges = new NativeMethods.TOKEN_PRIVILEGES()
{
PrivilegeCount = 1,
Privileges = new int[3]
{
priLuid.LowPart,
priLuid.HighPart,
enable ? NativeMethods.SE_PRIVILEGE_ENABLED : 0
}
};
int ReturnLength;
NativeMethods.AdjustTokenPrivileges(
hToken, false, tokenPrivileges,
Marshal.SizeOf(tokenPrivileges), IntPtr.Zero, out ReturnLength);
int err = Marshal.GetLastWin32Error();
if (err != 0)
throw new Win32Exception(err);
}
}
private static class NativeMethods
{
internal const int SE_PRIVILEGE_ENABLED = 0x2;
internal const int TOKEN_QUERY = 0x8;
internal const int TOKEN_ADJUST_PRIVILEGES = 0x20;
[StructLayout(LayoutKind.Sequential)]
internal class TOKEN_PRIVILEGES
{
public int PrivilegeCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public int[] Privileges;
}
[StructLayout(LayoutKind.Sequential)]
internal struct LUID
{
public int LowPart;
public int HighPart;
}
[DllImport("Kernel32.dll", SetLastError = true)]
internal static extern bool CloseHandle(IntPtr hObject);
[DllImport("Advapi32.dll", SetLastError = true), DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
internal static extern bool OpenProcessToken(IntPtr hProcess, uint desiredAccess, out SafeProcessTokenHandle hToken);
[DllImport("Advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode), DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
internal static extern bool LookupPrivilegeValue(
[MarshalAs(UnmanagedType.LPWStr), In] string lpSystemName,
[MarshalAs(UnmanagedType.LPWStr), In] string lpName,
out LUID Luid);
[DllImport("Advapi32.dll", SetLastError = true), DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
internal static extern bool AdjustTokenPrivileges(SafeProcessTokenHandle TokenHandle,
bool DisableAllPrivileges, [MarshalAs(UnmanagedType.LPStruct), In] TOKEN_PRIVILEGES NewState,
int BufferLength, IntPtr PreviousState, out int ReturnLength);
}
private class SafeProcessTokenHandle :
Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
{
public SafeProcessTokenHandle()
: this(IntPtr.Zero)
{
}
internal SafeProcessTokenHandle(IntPtr handle)
: base(true)
{
this.SetHandle(handle);
}
protected override bool ReleaseHandle() { return NativeMethods.CloseHandle(this.handle); }
}
}
'@
$type = Add-Type $definition -PassThru
$type[0]::AdjustPrivilege($Privilege, $Enable)
}
使い方
Set-Privilege SeTakeOwnershipPrivilege $true
おわりに
PowerShellで特権を変更する方法を探してる過程でC#コードをそのまま埋め込んでるサンプルを見つけ、その埋め込まれたコードが微妙だったから↑のコードを起こし、それをPowerShellに移植しただけという・・・
PowerShellで使えるC#のバージョンが古くて使えない構文だったところを直した程度で、ほとんどそのままコピペで動いた。
C#のコードがそのまま実行できるなら、PowerShellで色んなことが出来ちゃいそう・・・