2

I'm trying to use C# .Net to trigger Window's Service Trigger Events.

Specifically, I'm trying to trigger this event so that the WebClient service is started from an unprivileged account.

C:\>sc qtriggerinfo WebClient
[SC] QueryServiceConfig2 SUCCESS

SERVICE_NAME: WebClient

        START SERVICE
          CUSTOM                       : 22b6d684-fa63-4578-87c9-effcbe6643c7 [ETW PROVIDER UUID]

I was able to find this reference on kicking off the service in C++, but am unsure how to implement the same feature in C#:

bool StartWebClientService()
{
    const GUID _MS_Windows_WebClntLookupServiceTrigger_Provider =
        { 0x22B6D684, 0xFA63, 0x4578, 
        { 0x87, 0xC9, 0xEF, 0xFC, 0xBE, 0x66, 0x43, 0xC7 } };
    REGHANDLE Handle;
    bool success = false;

    if (EventRegister(&_MS_Windows_WebClntLookupServiceTrigger_Provider,
        nullptr, nullptr, &Handle) == ERROR_SUCCESS)
    {
        EVENT_DESCRIPTOR desc;

        EventDescCreate(&desc, 1, 0, 0, 4, 0, 0, 0);

        success = EventWrite(Handle, &desc, 0, nullptr) == ERROR_SUCCESS;

        EventUnregister(Handle);
    }

    return success;
}

Here is the code I am using:

[StructLayout(LayoutKind.Explicit, Size=12)]
public class EVENT_DESCRIPTOR
{
    [FieldOffset(0)]ushort Id = 1;
    [FieldOffset(2)]byte Version = 0;
    [FieldOffset(3)]byte Channel = 0;
    [FieldOffset(4)]byte Level = 4;
    [FieldOffset(5)]byte Opcode = 0;
    [FieldOffset(6)]ushort Task = 0;
    [FieldOffset(8)]ulong Keyword = 0;
}

//...

    void startService()
    {
        Guid webCleintTrigger = new Guid(0x22B6D684, 0xFA63, 0x4578, 0x87, 0xC9, 0xEF, 0xFC, 0xBE, 0x66, 0x43, 0xC7);

        IntPtr handle;
        uint output = EventRegister(ref webCleintTrigger, IntPtr.Zero, IntPtr.Zero, out handle);

        //This is what is returned:
        //output = 0 <- Good 
        //handle = 65537  <- Good handle?

        bool success = false;

        if (output == 0)
        {
            //Create event descriptor
            EVENT_DESCRIPTOR desc = new EVENT_DESCRIPTOR();

            //Write the event
            uint writeOutput = EventWrite(handle, ref desc, 0, IntPtr.Zero); //Throws PInvokeStackImbalance

            success = writeOutput == 0;

            EventUnregister(handle);
        }
    }

    [DllImport("Advapi32.dll", SetLastError = true)]
    public static extern uint EventRegister(ref Guid guid, [Optional] IntPtr EnableCallback, [Optional] IntPtr CallbackContext, out IntPtr RegHandle);

    [DllImport("Advapi32.dll", SetLastError = true)]
    public static extern uint EventWrite(IntPtr RegHandle, ref EVENT_DESCRIPTOR EventDescriptor, uint UserDataCount, [Optional] IntPtr UserData);

    [DllImport("Advapi32.dll", SetLastError = true)]
    public static extern uint EventUnregister(IntPtr RegHandle);

The call to EventWrite throws a PInvokeStackImbalance exception. Could this be an error with my EVENT_DESCRIPTOR structure?

This is the structure of the native Event_Descriptor:

typedef struct _EVENT_DESCRIPTOR {
  USHORT    Id;
  UCHAR     Version;
  UCHAR     Channel;
  UCHAR     Level;
  UCHAR     Opcode;
  USHORT    Task;
  ULONGLONG Keyword;
} EVENT_DESCRIPTOR, *PEVENT_DESCRIPTOR;typedef const EVENT_DESCRIPTOR *PCEVENT_DESCRIPTOR;

This is my C# structure:

[StructLayout(LayoutKind.Explicit, Size=16)]
public class EVENT_DESCRIPTOR
{
    [FieldOffset(0)]ushort Id = 1;
    [FieldOffset(2)]byte Version = 0;
    [FieldOffset(3)]byte Channel = 0;
    [FieldOffset(4)]byte Level = 4;
    [FieldOffset(5)]byte Opcode = 0;
    [FieldOffset(6)]ushort Task = 0;
    [FieldOffset(8)]ulong Keyword = 0;
}

This is the structure of the native EventWrite function:

ULONG EventWrite(
  _In_      REGHANDLE RegHandle,
  _In_      PCEVENT_DESCRIPTOR EventDescriptor,
  _In_      ULONG UserDataCount,
  _In_opt_  PEVENT_DATA_DESCRIPTOR UserData
);

This is my PInvoke call to Event Write:

    [DllImport("Advapi32.dll", SetLastError = true)]
    public static extern uint EventWrite(IntPtr RegHandle, ref EVENT_DESCRIPTOR EventDescriptor, uint UserDataCount, [Optional] IntPtr UserData);

Here is the call I'm making which is throwing the PInvokeStackImbalance Exception:

    EVENT_DESCRIPTOR desc = new EVENT_DESCRIPTOR();
    uint writeOutput = EventWrite(handle, ref desc, 0, IntPtr.Zero); //Throws PInvokeStackImbalance
Petey B
  • 11,439
  • 25
  • 81
  • 101
  • 1
    The 1st argument must be `ref Guid`. – Hans Passant Apr 07 '15 at 20:58
  • @HansPassant, that allowed the function call to go through, but it still failed (returned max value), any idea why? – Petey B Apr 07 '15 at 22:00
  • 1
    The return value type is uint, not ulong. ULONG in native code is a 32-bit type. – Hans Passant Apr 07 '15 at 23:34
  • @HansPassant, thanks, the output is now the expected 0 (ERROR_SUCCESS) however my handle is still pointing to 65537 every time, which I'm assuming should be a random location in memory. – Petey B Apr 08 '15 at 15:29
  • No, you cannot make any assumption about handle values. If the function didn't fail then you have a good handle. – Hans Passant Apr 08 '15 at 15:37
  • 1
    Handles contain offsets into some data structure. In some cases that data structure is all of virtual memory, and the handle value is a pointer. In other cases it's an array index, in which case the first call in your process always returning `1` is not surprising at all. Some of the bits of the handle may also be used for flags... and that makes consistently seeing `0x10001` entirely feasible and unsurprising. – Ben Voigt Apr 08 '15 at 15:40
  • @BenVoigt, thanks for the explination. Any idea what is causing my PInvokeStackImbalance Exception? – Petey B Apr 08 '15 at 20:02
  • 1
    `REGHANDLE` should be `Int64`, not a `IntPtr`. Actually, you can see Microsoft's own p/invoke declaration for these functions at http://referencesource.microsoft.com/#System.ServiceModel.Internals/System/Runtime/Interop/UnsafeNativeMethods.cs – Ben Voigt Apr 08 '15 at 20:26
  • @BenVoigt, thank you, that helped immensely. Got her working. – Petey B Apr 08 '15 at 21:10

1 Answers1

1

Thanks BenVoigt for pointing me to the Microsoft source reference where they had already implemented the PInvoke calls.

This was the solution:

[StructLayout(LayoutKind.Explicit, Size=16)]
public class EVENT_DESCRIPTOR
{
    [FieldOffset(0)]ushort Id = 1;
    [FieldOffset(2)]byte Version = 0;
    [FieldOffset(3)]byte Channel = 0;
    [FieldOffset(4)]byte Level = 4;
    [FieldOffset(5)]byte Opcode = 0;
    [FieldOffset(6)]ushort Task = 0;
    [FieldOffset(8)]long Keyword = 0;
}

[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct EventData
{
    [FieldOffset(0)]
    internal UInt64 DataPointer;
    [FieldOffset(8)]
    internal uint Size;
    [FieldOffset(12)]
    internal int Reserved;
}

//...

    void startService()
    {
        Guid webCleintTrigger = new Guid(0x22B6D684, 0xFA63, 0x4578, 0x87, 0xC9, 0xEF, 0xFC, 0xBE, 0x66, 0x43, 0xC7);

        long handle = 0;
        uint output = EventRegister(ref webCleintTrigger, IntPtr.Zero, IntPtr.Zero, ref handle);

        //This is what is returned:
        //output = 0 <- Good 
        //handle = 65537  <- Good handle?

        bool success = false;

        if (output == 0)
        {
            //Create event descriptor
            EVENT_DESCRIPTOR desc = new EVENT_DESCRIPTOR();

            //Write the event
            unsafe
            {
                uint writeOutput = EventWrite(handle, ref desc, 0, null);
                success = writeOutput == 0;

                EventUnregister(handle);
            }

        }
    }

    [DllImport("Advapi32.dll", SetLastError = true)]
    public static extern uint EventRegister(ref Guid guid, [Optional] IntPtr EnableCallback, [Optional] IntPtr CallbackContext, [In][Out] ref long RegHandle);

    [DllImport("Advapi32.dll", SetLastError = true)]
    public static extern unsafe uint EventWrite(long RegHandle, ref EVENT_DESCRIPTOR EventDescriptor, uint UserDataCount, EventData* UserData);

    [DllImport("Advapi32.dll", SetLastError = true)]
    public static extern uint EventUnregister(long RegHandle);
Petey B
  • 11,439
  • 25
  • 81
  • 101