3

This code has been running fine for years inside a utility program. We recently updated the program to enforce UAC but we find this code only works when NOT running as administrator; the code inside the while loop is never executed when run as admin but the same code returns a list of moniker names when running unelevated.

using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

namespace ROTExplorer
{
    class Program
    {
    [DllImport("ole32.dll")]
    static extern int GetRunningObjectTable(uint reserved, out IRunningObjectTable rot);

    [DllImport("Ole32.Dll")]
    static extern int CreateBindCtx(int reserved, out IBindCtx bindCtx);

    static void Main(string[] args)
    {
        FindEntryInROT();
        Console.WriteLine("Press any key to continue.");
        Console.ReadKey();
    }

    private static string FindEntryInROT()
    {
        IRunningObjectTable rot = null;
        IBindCtx bindCtx = null;
        IEnumMoniker enumMoniker = null;
        IMoniker[] monikers = new IMoniker[1];
        string displayName = null;
        try
        {
            GetRunningObjectTable(0, out rot);
            CreateBindCtx(0, out bindCtx);
            rot.EnumRunning(out enumMoniker);
            IntPtr fetched = IntPtr.Zero;
            while (enumMoniker.Next(1, monikers, fetched) == 0)
            {
                string tempName;
                monikers[0].GetDisplayName(bindCtx, null, out tempName);
                Marshal.ReleaseComObject(monikers[0]);
                monikers[0] = null;
                try
                {
                    Console.WriteLine(tempName);
                }
                catch
                {
                    Console.WriteLine("Bad string");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Failure while examining ROT: " + ex.Message);
        }
        finally
        {
            ReleaseCOMObject(monikers[0]);
            ReleaseCOMObject(enumMoniker);
            ReleaseCOMObject(bindCtx);
            ReleaseCOMObject(rot);
        }
        Console.WriteLine(displayName);
        return displayName;
    }

    private static void ReleaseCOMObject(object comObject)
    {
        if (comObject != null)
        {
            Marshal.ReleaseComObject(comObject);
            comObject = null;
        }
    }

}

I've tried this on 2 machines. Can someone else please try this and confirm that this code only returns the moniker list when NOT running as administrator.

Does anyone have any thoughts about why the IEnumMoniker returns no monikers when running in an elevated process but returns a list when not run as admin?

sartoris
  • 816
  • 1
  • 7
  • 21
  • A real world example is that the way it is now, an administrative utility cannot take advantage of OLE to send output to a spreadsheet without starting Excel as admin. – sartoris Apr 24 '19 at 23:23
  • So have you had any luck resolving this issue? – n0ne Jun 11 '20 at 07:06
  • 1
    not really (see answer below) - either disable UAC or only use OLE between programs started with the same privileges (both elevated or both un-elevated) – sartoris Jun 11 '20 at 20:26
  • 1
    Got it. Proper registration in ROT using ROTFLAGS_ALLOWANYCLIENT helped me to resolve the similar problem, of course it won't help if you don't control registration procedure. – n0ne Jun 12 '20 at 02:59

1 Answers1

4

I opened a ticket with Microsoft. It was escalated and I finally got an answer: it's working as designed. Here's the relevant conversation:

Microsoft Support:

The SCM/RPCSS service is where the Running Object Table lives. When the table is enumerated, the service does several checks. One of these checks is specifically to match the elevation level of the client token with the elevation level of the token's entry. If it doesn't match, then the entry will not be returned. The behavior you are seeing is by design.

Me:

Can you send me a link to where this is documented? Having 'elevated' privileges should give the user access to more objects, not less. What you're describing seems to be akin to simply logging in as a different user.

Microsoft Support:

It’s not documented directly.
In some ways, your last statement is correct. An admin user has two security tokens, one for normal operation, and one for elevated. They are never used at the same time. https://learn.microsoft.com/en-us/windows/security/identity-protection/user-account-control/how-user-account-control-works When an administrator logs on, two separate access tokens are created for the user: a standard user access token and an administrator access token. The standard user access token contains the same user-specific information as the administrator access token, but the administrative Windows privileges and SIDs are removed. I believe the reasoning behind all of this is security related, but I can’t explain that very well.

Me:

The administrator token has access to the standard user's files; why are the user's COM objects treated differently from the user's file objects?

Microsoft Support:

Because that’s the design decision the product group made. I don’t have further information on exactly why they made it like this.

Me:

It really sounds like a bug to me, or a faulty design. Is there a way to put this on the product group's radar?

Microsoft Support:

Unfortunately, there’s no leverage for getting any change in this area.

sartoris
  • 816
  • 1
  • 7
  • 21