/// <summary> /// The function gets the integrity level of the current process. Integrity /// level is only available on Windows Vista and newer operating systems, thus /// GetProcessIntegrityLevel throws a C++ exception if it is called on systems /// prior to Windows Vista. /// </summary> /// <returns> /// Returns the integrity level of the current process. It is usually one of /// these values: /// /// SECURITY_MANDATORY_UNTRUSTED_RID - means untrusted level. It is used /// by processes started by the Anonymous group. Blocks most write access. /// (SID: S-1-16-0x0) /// /// SECURITY_MANDATORY_LOW_RID - means low integrity level. It is used by /// Protected Mode Internet Explorer. Blocks write acess to most objects /// (such as files and registry keys) on the system. (SID: S-1-16-0x1000) /// /// SECURITY_MANDATORY_MEDIUM_RID - means medium integrity level. It is /// used by normal applications being launched while UAC is enabled. /// (SID: S-1-16-0x2000) /// /// SECURITY_MANDATORY_HIGH_RID - means high integrity level. It is used /// by administrative applications launched through elevation when UAC is /// enabled, or normal applications if UAC is disabled and the user is an /// administrator. (SID: S-1-16-0x3000) /// /// SECURITY_MANDATORY_SYSTEM_RID - means system integrity level. It is /// used by services and other system-level applications (such as Wininit, /// Winlogon, Smss, etc.) (SID: S-1-16-0x4000) /// /// </returns> /// <exception cref="System.ComponentModel.Win32Exception"> /// When any native Windows API call fails, the function throws a Win32Exception /// with the last error code. /// </exception> internal int GetProcessIntegrityLevel() { int IL = -1; SafeTokenHandle hToken = null; int cbTokenIL = 0; IntPtr pTokenIL = IntPtr.Zero; try { // Open the access token of the current process with TOKEN_QUERY. if (!NativeMethod.OpenProcessToken(Process.GetCurrentProcess().Handle, NativeMethod.TOKEN_QUERY, out hToken)) { throw new Win32Exception(); } // Then we must query the size of the integrity level information // associated with the token. Note that we expect GetTokenInformation // to return false with the ERROR_INSUFFICIENT_BUFFER error code // because we've given it a null buffer. On exit cbTokenIL will tell // the size of the group information. if (!NativeMethod.GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenIntegrityLevel, IntPtr.Zero, 0, out cbTokenIL)) { int error = Marshal.GetLastWin32Error(); if (error != NativeMethod.ERROR_INSUFFICIENT_BUFFER) { // When the process is run on operating systems prior to // Windows Vista, GetTokenInformation returns false with the // ERROR_INVALID_PARAMETER error code because // TokenIntegrityLevel is not supported on those OS's. throw new Win32Exception(error); } } // Now we allocate a buffer for the integrity level information. pTokenIL = Marshal.AllocHGlobal(cbTokenIL); if (pTokenIL == IntPtr.Zero) { throw new Win32Exception(); } // Now we ask for the integrity level information again. This may fail // if an administrator has added this account to an additional group // between our first call to GetTokenInformation and this one. if (!NativeMethod.GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenIntegrityLevel, pTokenIL, cbTokenIL, out cbTokenIL)) { throw new Win32Exception(); } // Marshal the TOKEN_MANDATORY_LABEL struct from native to .NET object. TOKEN_MANDATORY_LABEL tokenIL = (TOKEN_MANDATORY_LABEL) Marshal.PtrToStructure(pTokenIL, typeof(TOKEN_MANDATORY_LABEL)); // Integrity Level SIDs are in the form of S-1-16-0xXXXX. (e.g. // S-1-16-0x1000 stands for low integrity level SID). There is one // and only one subauthority. IntPtr pIL = NativeMethod.GetSidSubAuthority(tokenIL.Label.Sid, 0); IL = Marshal.ReadInt32(pIL); } finally { // Centralized cleanup for all allocated resources. if (hToken != null) { hToken.Close(); hToken = null; } if (pTokenIL != IntPtr.Zero) { Marshal.FreeHGlobal(pTokenIL); pTokenIL = IntPtr.Zero; cbTokenIL = 0; } } return(IL); }
/// <summary> /// 此函数获取当前线程的完整性级别。完整性级别只有在Windows Vista及后 /// 续版本的Windows中有效。所以在Windows Vista之前的版本中执行GetProcessIntegrityLevel, /// 它会抛出一个C++异常。 /// </summary> /// <returns> /// 返回当前进程的完整性级别。它可能是以下某一个值。 /// /// SECURITY_MANDATORY_UNTRUSTED_RID (SID: S-1-16-0x0) /// 表示不被信任的级别。它被用于一个匿名组成员起动的进程。这时大多数 /// 写入操作被禁止。 /// /// SECURITY_MANDATORY_LOW_RID (SID: S-1-16-0x1000) /// 表示低完整性级别。它被用于保护模式下的IE浏览器。这时大多数系统中对 /// 象(包括文件及注册表键值)的写入操作被禁止。 /// /// SECURITY_MANDATORY_MEDIUM_RID (SID: S-1-16-0x2000) /// 表示中完整性级别。它被用于在UAC开启时启动普通应用程序。 /// /// /// SECURITY_MANDATORY_HIGH_RID (SID: S-1-16-0x3000) /// 表示高完整性级别。它被用于用户通过UAC提升权限启动一个管理员应用程序。 /// 或则当UAC关闭时,管理员用户启动一个普通程序。 /// /// /// SECURITY_MANDATORY_SYSTEM_RID (SID: S-1-16-0x4000) /// 表示系统完整性级别。它被用于服务或则其他系统级别的应用程序(比如 /// Wininit, Winlogon, Smss,等等) /// </returns> /// <exception cref="System.ComponentModel.Win32Exception"> /// 如果任何原生的Windows API函数出错,此函数会抛出一个包含最后错误代码的Win32Exception。 /// </exception> internal int GetProcessIntegrityLevel() { int IL = -1; SafeTokenHandle hToken = null; int cbTokenIL = 0; IntPtr pTokenIL = IntPtr.Zero; try { // 使用TOKEN_QUERY打开线程的主访问令牌。 if (!NativeMethod.OpenProcessToken(Process.GetCurrentProcess().Handle, NativeMethod.TOKEN_QUERY, out hToken)) { throw new Win32Exception(); } // 然后我们必须查询令牌完整性级别信息的大小。注意:我们预期得到一个FALSE结果及错误 // ERROR_INSUFFICIENT_BUFFER, 这是由于我们在GetTokenInformation输入一个 // 空缓冲。同时,在cbTokenIL中我们会得知完整性级别信息的大小。 if (!NativeMethod.GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenIntegrityLevel, IntPtr.Zero, 0, out cbTokenIL)) { int error = Marshal.GetLastWin32Error(); if (error != NativeMethod.ERROR_INSUFFICIENT_BUFFER) { // 当进程运行于Windows Vista之前的系统中,GetTokenInformation返回 // FALSE和错误码ERROR_INVALID_PARAMETER。这是由于这些操作系统不支 // 持TokenElevation。 throw new Win32Exception(error); } } // 现在我们为完整性级别信息分配一个缓存。 pTokenIL = Marshal.AllocHGlobal(cbTokenIL); if (pTokenIL == IntPtr.Zero) { throw new Win32Exception(); } // 现在我们需要再次查询完整性级别信息。如果在第一次调用GetTokenInformation // 和本次之间一个管理员把当前账户加到另外一个组中,此次调用会失败。 if (!NativeMethod.GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenIntegrityLevel, pTokenIL, cbTokenIL, out cbTokenIL)) { throw new Win32Exception(); } // 转换TOKEN_MANDATORY_LABEL结构(从Native 到.Net) TOKEN_MANDATORY_LABEL tokenIL = (TOKEN_MANDATORY_LABEL) Marshal.PtrToStructure(pTokenIL, typeof(TOKEN_MANDATORY_LABEL)); // 完整性级别SID为S-1-16-0xXXXX形式。(例如:S-1-16-0x1000表示为低完整性 // 级别的SID)。而且有且仅有一个次级授权信息。 IntPtr pIL = NativeMethod.GetSidSubAuthority(tokenIL.Label.Sid, 0); IL = Marshal.ReadInt32(pIL); } finally { // 集中清理所有已分配的内存资源 if (hToken != null) { hToken.Close(); hToken = null; } if (pTokenIL != IntPtr.Zero) { Marshal.FreeHGlobal(pTokenIL); pTokenIL = IntPtr.Zero; cbTokenIL = 0; } } return(IL); }
/// <summary> /// The function launches an application at low integrity level. /// </summary> /// <param name="commandLine"> /// The command line to be executed. The maximum length of this string is 32K /// characters. /// </param> /// <remarks> /// To start a low-integrity process, /// 1) Duplicate the handle of the current process, which is at medium /// integrity level. /// 2) Use SetTokenInformation to set the integrity level in the access token /// to Low. /// 3) Use CreateProcessAsUser to create a new process using the handle to /// the low integrity access token. /// </remarks> internal void CreateLowIntegrityProcess(string commandLine) { SafeTokenHandle hToken = null; SafeTokenHandle hNewToken = null; IntPtr pIntegritySid = IntPtr.Zero; int cbTokenInfo = 0; IntPtr pTokenInfo = IntPtr.Zero; STARTUPINFO si = new STARTUPINFO(); PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); try { // Open the primary access token of the process. if (!NativeMethod.OpenProcessToken(Process.GetCurrentProcess().Handle, NativeMethod.TOKEN_DUPLICATE | NativeMethod.TOKEN_ADJUST_DEFAULT | NativeMethod.TOKEN_QUERY | NativeMethod.TOKEN_ASSIGN_PRIMARY, out hToken)) { throw new Win32Exception(); } // Duplicate the primary token of the current process. if (!NativeMethod.DuplicateTokenEx(hToken, 0, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out hNewToken)) { throw new Win32Exception(); } // Create the low integrity SID. if (!NativeMethod.AllocateAndInitializeSid( ref NativeMethod.SECURITY_MANDATORY_LABEL_AUTHORITY, 1, NativeMethod.SECURITY_MANDATORY_LOW_RID, 0, 0, 0, 0, 0, 0, 0, out pIntegritySid)) { throw new Win32Exception(); } TOKEN_MANDATORY_LABEL tml; tml.Label.Attributes = NativeMethod.SE_GROUP_INTEGRITY; tml.Label.Sid = pIntegritySid; // Marshal the TOKEN_MANDATORY_LABEL struct to the native memory. cbTokenInfo = Marshal.SizeOf(tml); pTokenInfo = Marshal.AllocHGlobal(cbTokenInfo); Marshal.StructureToPtr(tml, pTokenInfo, false); // Set the integrity level in the access token to low. if (!NativeMethod.SetTokenInformation(hNewToken, TOKEN_INFORMATION_CLASS.TokenIntegrityLevel, pTokenInfo, cbTokenInfo + NativeMethod.GetLengthSid(pIntegritySid))) { throw new Win32Exception(); } // Create the new process at the Low integrity level. si.cb = Marshal.SizeOf(si); if (!NativeMethod.CreateProcessAsUser(hNewToken, null, commandLine, IntPtr.Zero, IntPtr.Zero, false, 0, IntPtr.Zero, null, ref si, out pi)) { throw new Win32Exception(); } } finally { // Centralized cleanup for all allocated resources. if (hToken != null) { hToken.Close(); hToken = null; } if (hNewToken != null) { hNewToken.Close(); hNewToken = null; } if (pIntegritySid != IntPtr.Zero) { NativeMethod.FreeSid(pIntegritySid); pIntegritySid = IntPtr.Zero; } if (pTokenInfo != IntPtr.Zero) { Marshal.FreeHGlobal(pTokenInfo); pTokenInfo = IntPtr.Zero; cbTokenInfo = 0; } if (pi.hProcess != IntPtr.Zero) { NativeMethod.CloseHandle(pi.hProcess); pi.hProcess = IntPtr.Zero; } if (pi.hThread != IntPtr.Zero) { NativeMethod.CloseHandle(pi.hThread); pi.hThread = IntPtr.Zero; } } }
/// <summary> /// 此函数以低完整性级别启动一个应用程序 /// </summary> /// <param name="commandLine"> /// 需要被执行的命令行。此字符串最长为32K个字符。 /// </param> /// <remarks> /// 启动一个低完整性进程 /// 1) 复制当前进程的句柄,它拥有中完整性级别 /// 2)使用SetTokenInformation设置访问进程的完整性级别为低。 /// 3)使用CreateProcessAsUser及低完整性级别的访问令牌创建一个新的进程。 /// </remarks> internal void CreateLowIntegrityProcess(string commandLine) { SafeTokenHandle hToken = null; SafeTokenHandle hNewToken = null; string strIntegritySid = "S-1-16-4096"; // Low integrity SID string 低完整性SID字符串 IntPtr pIntegritySid = IntPtr.Zero; int cbTokenInfo = 0; IntPtr pTokenInfo = IntPtr.Zero; STARTUPINFO si = new STARTUPINFO(); PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); try { // 打开进程的主访问令牌。 if (!NativeMethod.OpenProcessToken(Process.GetCurrentProcess().Handle, NativeMethod.TOKEN_DUPLICATE | NativeMethod.TOKEN_ADJUST_DEFAULT | NativeMethod.TOKEN_QUERY | NativeMethod.TOKEN_ASSIGN_PRIMARY, out hToken)) { throw new Win32Exception(); } // 复制当前进程的主令牌。 if (!NativeMethod.DuplicateTokenEx(hToken, 0, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out hNewToken)) { throw new Win32Exception(); } // 创建低完整性SID。 if (!NativeMethod.ConvertStringSidToSid(strIntegritySid, out pIntegritySid)) { throw new Win32Exception(); } TOKEN_MANDATORY_LABEL tml; tml.Label.Attributes = NativeMethod.SE_GROUP_INTEGRITY; tml.Label.Sid = pIntegritySid; // 转换TOKEN_MANDATORY_LABEL结构至native内存。 cbTokenInfo = Marshal.SizeOf(tml); pTokenInfo = Marshal.AllocHGlobal(cbTokenInfo); Marshal.StructureToPtr(tml, pTokenInfo, false); // 设置访问令牌的完整性级别为低。 if (!NativeMethod.SetTokenInformation(hNewToken, TOKEN_INFORMATION_CLASS.TokenIntegrityLevel, pTokenInfo, cbTokenInfo + NativeMethod.GetLengthSid(pIntegritySid))) { throw new Win32Exception(); } // 以低完整性级别创建一个新进程。 si.cb = Marshal.SizeOf(si); if (!NativeMethod.CreateProcessAsUser(hNewToken, null, commandLine, IntPtr.Zero, IntPtr.Zero, false, 0, IntPtr.Zero, null, ref si, out pi)) { throw new Win32Exception(); } } finally { // 集中清理已分配的资源 if (hToken != null) { hToken.Close(); hToken = null; } if (hNewToken != null) { hNewToken.Close(); hNewToken = null; } if (pIntegritySid != IntPtr.Zero) { Marshal.FreeHGlobal(pIntegritySid); pIntegritySid = IntPtr.Zero; } if (pTokenInfo != IntPtr.Zero) { Marshal.FreeHGlobal(pTokenInfo); pTokenInfo = IntPtr.Zero; cbTokenInfo = 0; } if (pi.hProcess != IntPtr.Zero) { NativeMethod.CloseHandle(pi.hProcess); pi.hProcess = IntPtr.Zero; } if (pi.hThread != IntPtr.Zero) { NativeMethod.CloseHandle(pi.hThread); pi.hThread = IntPtr.Zero; } } }