1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Windows/C#】アクセストークンの特権を変更する

Posted at

はじめに

とあるやりたい事を実現するのに、プロセスに関連付けられているアクセストークンの特権を変更しないといけないことが分かり、参考になるソースコードを探してみたら、どれも古いMicrosoftのサンプルをベースにしたものばかりで、しっくり来るものがなかったので、より今どきのC#っぽい綺麗な(?)ものを自作してみた。

【参考】

ソース

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;

/// <summary>
/// 特権を変更するための機能を提供します。
/// </summary>
public static class Privilege
{
	/// <summary>
	/// 現在のプロセスに関連付けられているアクセストークンの特権を変更します。
	/// </summary>
	/// <param name="privilegeName">変更する特権名</param>
	/// <param name="enable">特権を有効にする場合は、true。それ以外は、false。</param>
	public static void Adjust(string privilegeName, bool enable)
	{
		if (!NativeMethods.LookupPrivilegeValue(null, privilegeName, out var priLuid))
			throw new ArgumentException("指定された特権名は存在しません。", nameof(privilegeName));

		var curProc = Process.GetCurrentProcess();
		if (curProc.Handle == IntPtr.Zero)
			throw new InvalidOperationException("プロセスハンドルが取得できませんでした。");

		if (!NativeMethods.OpenProcessToken(curProc.Handle,
			NativeMethods.TOKEN_ADJUST_PRIVILEGES | NativeMethods.TOKEN_QUERY,
			out var 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
				}
			};

			NativeMethods.AdjustTokenPrivileges(
				hToken, false, tokenPrivileges,
				Marshal.SizeOf(tokenPrivileges), IntPtr.Zero, out _);

			int err = Marshal.GetLastWin32Error();
			if (err != 0)
				throw new Win32Exception(err);
		}
	}

	#region NT Defined Privileges
	public const string SeCreateTokenPrivilege = "SeCreateTokenPrivilege";
	public const string SeAssignPrimaryTokenPrivilege = "SeAssignPrimaryTokenPrivilege";
	public const string SeLockMemoryPrivilege = "SeLockMemoryPrivilege";
	public const string SeIncreaseQuotaPrivilege = "SeIncreaseQuotaPrivilege";
	public const string SeUnsolicitedInputPrivilege = "SeUnsolicitedInputPrivilege";
	public const string SeMachineAccountPrivilege = "SeMachineAccountPrivilege";
	public const string SeTcbPrivilege = "SeTcbPrivilege";
	public const string SeSecurityPrivilege = "SeSecurityPrivilege";
	public const string SeTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege";
	public const string SeLoadDriverPrivilege = "SeLoadDriverPrivilege";
	public const string SeSystemProfilePrivilege = "SeSystemProfilePrivilege";
	public const string SeSystemtimePrivilege = "SeSystemtimePrivilege";
	public const string SeProfileSingleProcessPrivilege = "SeProfileSingleProcessPrivilege";
	public const string SeIncreaseBasePriorityPrivilege = "SeIncreaseBasePriorityPrivilege";
	public const string SeCreatePagefilePrivilege = "SeCreatePagefilePrivilege";
	public const string SeCreatePermanentPrivilege = "SeCreatePermanentPrivilege";
	public const string SeBackupPrivilege = "SeBackupPrivilege";
	public const string SeRestorePrivilege = "SeRestorePrivilege";
	public const string SeShutdownPrivilege = "SeShutdownPrivilege";
	public const string SeDebugPrivilege = "SeDebugPrivilege";
	public const string SeAuditPrivilege = "SeAuditPrivilege";
	public const string SeSystemEnvironmentPrivilege = "SeSystemEnvironmentPrivilege";
	public const string SeChangeNotifyPrivilege = "SeChangeNotifyPrivilege";
	public const string SeRemoteShutdownPrivilege = "SeRemoteShutdownPrivilege";
	public const string SeUndockPrivilege = "SeUndockPrivilege";
	public const string SeSyncAgentPrivilege = "SeSyncAgentPrivilege";
	public const string SeEnableDelegationPrivilege = "SeEnableDelegationPrivilege";
	public const string SeManageVolumePrivilege = "SeManageVolumePrivilege";
	public const string SeImpersonatePrivilege = "SeImpersonatePrivilege";
	public const string SeCreateGlobalPrivilege = "SeCreateGlobalPrivilege";
	public const string SeTrustedCredManAccessPrivilege = "SeTrustedCredManAccessPrivilege";
	public const string SeRelabelPrivilege = "SeRelabelPrivilege";
	public const string SeIncreaseWorkingSetPrivilege = "SeIncreaseWorkingSetPrivilege";
	public const string SeTimeZonePrivilege = "SeTimeZonePrivilege";
	public const string SeCreateSymbolicLinkPrivilege = "SeCreateSymbolicLinkPrivilege";
	#endregion

	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 struct 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, 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() => NativeMethods.CloseHandle(this.handle);
	}
}

使い方

Privilege.Adjust(Privilege.SeTakeOwnershipPrivilege, true);
Privilege.Adjust(Privilege.SeRestorePrivilege, true);
Privilege.Adjust(Privilege.SeBackupPrivilege, true);

かんたん。
有効にする権限によっては、管理者権限が必要。

おわりに

IDisposableなクラスで、Dispose時に変更した特権を元に戻すようにしておいて、using句で特権の変更範囲を最小限に留めるような仕組みも面白そう。
ただ実際は、頻繁に使うものでもないし、必要なところに↑のクラスの必要な部分だけ埋め込んでしまう方が実用的かも・・・

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?