管理员模式下如何获取用户模式的Running Object Table条目?
解决管理员模式进程访问用户模式Running Object Table的问题
问题根源
Windows的UAC机制会将同一用户的**提升(管理员)和非提升(普通用户)**进程隔离在不同的安全上下文和会话中,Running Object Table (ROT) 是按安全上下文分区的,因此默认情况下管理员进程无法直接访问用户模式进程注册的ROT条目。
解决方案:模拟用户的非提升安全上下文
管理员进程运行时会持有两个访问令牌:一个是提升的管理员令牌,另一个是原始的非提升用户令牌。我们可以获取并模拟这个非提升令牌,在该上下文下访问用户模式的ROT。
实现步骤
- 获取当前进程的非提升用户令牌
- 模拟该令牌的安全上下文
- 在模拟上下文内调用
Marshal.GetActiveObject()或枚举ROT - 恢复原安全上下文
C# 代码示例
using System; using System.Runtime.InteropServices; using System.Security.Principal; public class RotAccessHelper { // P/Invoke 声明 [DllImport("advapi32.dll", SetLastError = true)] private static extern bool OpenProcessToken(IntPtr processHandle, uint desiredAccess, out IntPtr tokenHandle); [DllImport("advapi32.dll", SetLastError = true)] private static extern bool GetTokenInformation(IntPtr tokenHandle, TOKEN_INFORMATION_CLASS tokenInfoClass, IntPtr tokenInfo, uint tokenInfoLength, out uint returnLength); [DllImport("advapi32.dll", SetLastError = true)] private static extern bool ImpersonateLoggedOnUser(IntPtr tokenHandle); [DllImport("advapi32.dll", SetLastError = true)] private static extern bool RevertToSelf(); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr GetCurrentProcess(); private enum TOKEN_INFORMATION_CLASS { TokenLinkedToken = 19 } // 获取非提升令牌 private static IntPtr GetUnelevatedToken() { if (!OpenProcessToken(GetCurrentProcess(), 0x0008 /* TOKEN_QUERY */, out IntPtr hToken)) throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); uint returnLength = 0; GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenLinkedToken, IntPtr.Zero, 0, out returnLength); IntPtr linkedTokenPtr = Marshal.AllocHGlobal((int)returnLength); try { if (!GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenLinkedToken, linkedTokenPtr, returnLength, out returnLength)) throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); IntPtr hLinkedToken = Marshal.ReadIntPtr(linkedTokenPtr); return hLinkedToken; } finally { Marshal.FreeHGlobal(linkedTokenPtr); CloseHandle(hToken); } } [DllImport("kernel32.dll", SetLastError = true)] private static extern bool CloseHandle(IntPtr hObject); // 在非提升上下文获取COM对象 public static object GetActiveObjectFromUserContext(string progId) { IntPtr unelevatedToken = IntPtr.Zero; try { unelevatedToken = GetUnelevatedToken(); if (!ImpersonateLoggedOnUser(unelevatedToken)) throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); // 在此上下文调用GetActiveObject return Marshal.GetActiveObject(progId); } finally { RevertToSelf(); if (unelevatedToken != IntPtr.Zero) CloseHandle(unelevatedToken); } } } // 使用示例 class Program { static void Main() { try { object comObj = RotAccessHelper.GetActiveObjectFromUserContext("Excel.Application"); Console.WriteLine("成功获取COM对象"); } catch (Exception ex) { Console.WriteLine($"获取失败: {ex.Message}"); } } }
补充:枚举ROT条目
如果GetActiveObject()无法找到目标对象,可以枚举ROT中的所有条目,在模拟上下文内筛选:
using System.Collections.Generic; using System.Runtime.InteropServices.ComTypes; public static IEnumerable<object> EnumerateRotObjects() { List<object> rotObjects = new List<object>(); IBindCtx bindCtx = null; IRunningObjectTable rot = null; IEnumMoniker enumMoniker = null; try { Marshal.ThrowExceptionForHR(CreateBindCtx(0, out bindCtx)); bindCtx.GetRunningObjectTable(out rot); rot.EnumRunning(out enumMoniker); IMoniker[] monikers = new IMoniker[1]; int fetched; while (enumMoniker.Next(1, monikers, out fetched) == 0 && fetched == 1) { object obj; rot.GetObject(monikers[0], out obj); rotObjects.Add(obj); Marshal.ReleaseComObject(monikers[0]); } } finally { if (enumMoniker != null) Marshal.ReleaseComObject(enumMoniker); if (rot != null) Marshal.ReleaseComObject(rot); if (bindCtx != null) Marshal.ReleaseComObject(bindCtx); } return rotObjects; } [DllImport("ole32.dll")] private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);
将此枚举方法放在ImpersonateLoggedOnUser之后调用,即可获取用户模式ROT中的所有对象。
注意事项
- 该方法仅适用于同一用户账户下的进程,跨用户无法访问
- 确保目标COM对象已正确注册到ROT中(部分程序仅在特定条件下注册)
- Windows 10/11的UAC机制可能对令牌模拟有额外限制,需测试不同版本系统
内容的提问来源于stack exchange,提问作者abs2789
相关产品推荐
相关产品推荐

