You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

管理员模式下如何获取用户模式的Running Object Table条目?

解决管理员模式进程访问用户模式Running Object Table的问题

问题根源

Windows的UAC机制会将同一用户的**提升(管理员)和非提升(普通用户)**进程隔离在不同的安全上下文和会话中,Running Object Table (ROT) 是按安全上下文分区的,因此默认情况下管理员进程无法直接访问用户模式进程注册的ROT条目。

解决方案:模拟用户的非提升安全上下文

管理员进程运行时会持有两个访问令牌:一个是提升的管理员令牌,另一个是原始的非提升用户令牌。我们可以获取并模拟这个非提升令牌,在该上下文下访问用户模式的ROT。

实现步骤

  1. 获取当前进程的非提升用户令牌
  2. 模拟该令牌的安全上下文
  3. 在模拟上下文内调用Marshal.GetActiveObject()或枚举ROT
  4. 恢复原安全上下文

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 20:33:22