How to start a “Trigger Start” windows service with Powershell without elevation / admin rights

Some Windows services can be triggered to start at certain events. These services have ‘Tigger Start’ in their startup name behind whatever you configured (like Manual).

Powershell does not have a native method to register the type of event that triggers such a service, C++ and C# do…..and Powershell can natively run C#.

To trigger a service, you’ll need its guid first:

run sc triggerinfo <SERVICENAME>

This will give you a GUID, for example for the WebClient service:

22b6d684-fa63-4578-87c9-effcbe6643c7

You can then use this GUID in the following script to trigger your service from Powershell 🙂


$Source = @"
using System;
using System.Text;
using System.Security;
using System.Collections.Generic;
using System.Runtime.Versioning;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.Diagnostics.CodeAnalysis;
namespace JosL.WebClient{
public static class Starter{
[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;
}

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

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

bool success = false;

if (output == 0){
EVENT_DESCRIPTOR desc = new EVENT_DESCRIPTOR();
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);
}
}
"@
$compilerParameters = New-Object System.CodeDom.Compiler.CompilerParameters
$compilerParameters.CompilerOptions="/unsafe"
Add-Type -TypeDefinition $Source -Language CSharp -CompilerParameters $compilerParameters
[JosL.WebClient.Starter]::startService()

3
Leave a Reply

avatar
2 Comment threads
1 Thread replies
3 Followers
 
Most reacted comment
Hottest comment thread
3 Comment authors
JosHenry BruceMatthew Reinsmith Recent comment authors

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  Subscribe  
newest oldest most voted
Notify of
Matthew Reinsmith
Guest
Matthew Reinsmith

Thanks for the Article, got me headed in the right direction!

FYI – At least in windows 10, the command to get the GUID is “sc qtriggerinfo ” e.g. “sc qtriggerinfo WebClient”

Henry Bruce
Guest
Henry Bruce

I stumbled across this 3 years after you posted and used it for a C# project. I needed to send a custom trigger event so ended up using EventWriteString() which made the code much simpler (no need for event descriptor or data structures).

Anyway, thanks for sharing, you saved my bacon!