PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197
Windows Service: Running a remote process in the System account-VBForums
Results 1 to 3 of 3

Thread: Windows Service: Running a remote process in the System account

  1. #1

    Thread Starter
    Bad man! ident's Avatar
    Join Date
    Mar 2009
    Location
    Cambridge
    Posts
    5,216

    Windows Service: Running a remote process in the System account

    I have seen a few examples using an interactive service to launch a process under the User account. But none in Visual Basic to launch under the local system account. I'm sure there are many examples out there. I have been trying to explore more with lately with Win32 APIs so I do not claim to have absolute knowledge so take from this what you will. There is more than likely edits to be made.

    There are a number of APIs I have not included from the WTSAPI32.DLL library such as WTSEnumerateSessions, WTSFreeMemory etc and regarding memory management. I also have not investigated furthor the absolute need to read and write to the buffer from the service if the services only purpose is to launch a seperate process. These APIs would cover WriteFile, ReadFile from kernel32 Libarary. Maybe someone will have some comments later on about that.

    Anyway...

    This is just a simple way to demonstrates how to create/launch a process interactively in the session of the logged-on user/Local from a service application.

    Basic steps:
    1) WTSGetActiveConsoleSessionId to get the ID of the currently active Windows session at the console.
    2) OpenProcess to get the handle of the process to Impersonate.
    3) OpenProcessToken to get the access token of the process to impersonate
    4) DuplicateTokenEx() to duplicate the impersonated process access token.
    5) CreateProcessAsUser with the duplicated token and the created environment.

    Example:
    ("C:\Windows\Notepad.exe", Nothing,"winlogon")

    vb Code:
    1. Imports KaiService.Enums
    2. Imports KaiService.Structures
    3.  
    4. ''' <summary>
    5. '''     Simplifies Running the remote process in the System account.
    6. ''' </summary>
    7. Public Class ProcessManager
    8.     ''' <summary>
    9.     '''     The value of the interactive window station.
    10.     ''' </summary>
    11.     Private Const MWindowStationDesktopCreation As String = "winsta0\default"
    12.  
    13.     ''' <summary>
    14.     '''     Responsible for creating and executing a new process.
    15.     ''' </summary>
    16.     ''' <param name="applicationPath">
    17.     '''     The path to the executable that this class will manage.
    18.     ''' </param>
    19.     ''' <param name="commandArguments">
    20.     '''     The applications command line arguments.
    21.     ''' </param>
    22.     ''' <param name="processName">
    23.     '''     The process to Impersonate process name.
    24.     ''' </param>
    25.     ''' <remarks>
    26.     '''     The <see cref="ProcessManager" /> class is responsible for managing a process that will run with elevated
    27.     '''     privileges..
    28.     ''' </remarks>
    29.     Public Shared Sub TryCreateProcess(applicationPath As String, commandArguments As String, processName As String)
    30.         Const dwCreationFlags As Integer = AccessRights.NormalPriorityClass Or AccessRights.CreateNewConsole
    31.  
    32.         Dim dwSessionId As Integer = NativeMethods.WTSGetActiveConsoleSessionId()
    33.         Dim specifiedId as integer = GetSpecifiedId(processName, dwSessionId)
    34.         Dim processHandle As IntPtr = TryOpenProcess(specifiedId)
    35.  
    36.         If processHandle = IntPtr.Zero Then
    37.             NativeMethods.CloseHandle(processHandle)
    38.             Exit Sub
    39.         End If
    40.  
    41.         Dim application = $"{CreateRelativePath(applicationPath)} {commandArguments}"
    42.         Dim tokenHandle As IntPtr = IntPtr.Zero
    43.  
    44.         Dim openProcessToken As Boolean = TryOpenProcessToken(processHandle, tokenHandle)
    45.         If Not openProcessToken Then
    46.             NativeMethods.CloseHandle(processHandle)
    47.             Exit Sub
    48.         End If
    49.  
    50.         Dim hToken As IntPtr = IntPtr.Zero
    51.         Dim attributes As SecurityAttributes = GetSecurityAttributes()
    52.  
    53.         Dim duplicateToken As Boolean = TryDuplicateToken(attributes, tokenHandle, hToken)
    54.         If Not duplicateToken Then
    55.             NativeMethods.CloseHandle(processHandle)
    56.             NativeMethods.CloseHandle(tokenHandle)
    57.             Exit Sub
    58.         End If
    59.  
    60.         Dim startupInfo As StartupInfoA = GetStartupInfo()
    61.         Dim processInformation As New ProcessInformation()
    62.  
    63.         TryCreateProcessAsUser(dwCreationFlags, startupInfo, processInformation, application, attributes, hToken)
    64.  
    65.         NativeMethods.CloseHandle(processHandle)
    66.         NativeMethods.CloseHandle(tokenHandle)
    67.         NativeMethods.CloseHandle(hToken)
    68.     End Sub
    69.  
    70.     ''' <summary>
    71.     '''     Using the calling process we can then call the OpenProcess Win32 API to obtain a handle from the specified ID.
    72.     ''' </summary>
    73.     ''' <param name="specifiedId">
    74.     '''     The specified process identifier of the process to open.
    75.     ''' </param>
    76.     ''' <returns>
    77.     '''     If the function succeeds, the return value is an open handle to the specified process. If the function fails, the
    78.     '''     return value is NULL.
    79.     ''' </returns>
    80.     Private Shared Function TryOpenProcess(specifiedId As Integer) As IntPtr
    81.         Return NativeMethods.OpenProcess(AccessRights.QueryInformation, False, specifiedId)
    82.     End Function
    83.  
    84.     ''' <summary>
    85.     '''     Using the OpenProcessToken function to retrieve a handle to the primary token of a process.
    86.     ''' </summary>
    87.     ''' <param name="processHandle">
    88.     '''     The unique identifier for the associated process.
    89.     ''' </param>
    90.     ''' <param name="tokenHandle">
    91.     '''     A pointer to a handle that identifies the newly opened access token when the function returns.
    92.     ''' </param>
    93.     ''' <returns>
    94.     '''     The access token associated with the process returned from <see cref="GetSpecifiedId" /> method.
    95.     ''' </returns>
    96.     Private Shared Function TryOpenProcessToken(processHandle As IntPtr, ByRef tokenHandle As IntPtr) As Boolean
    97.         Return NativeMethods.OpenProcessToken(processHandle, AccessRights.TokenDuplicate, tokenHandle)
    98.     End Function
    99.  
    100.     ''' <summary>
    101.     '''     The OpenProcessToken function opens the access token associated with a process.
    102.     ''' </summary>
    103.     ''' <param name="attributes">
    104.     '''     The security attributes returned from <see cref="GetSecurityAttributes" /> method.
    105.     ''' </param>
    106.     ''' <param name="tokenHandle">
    107.     '''     The access token associated with the process.
    108.     ''' </param>
    109.     ''' <param name="hToken">
    110.     '''     A pointer to a variable that receives a handle to the duplicate token.
    111.     ''' </param>
    112.     ''' <returns>
    113.     '''     The duplicate access token associated with the process returned from <see cref="TryOpenProcessToken" /> method.
    114.     ''' </returns>
    115.     Private Shared Function TryDuplicateToken(ByRef attributes As SecurityAttributes, tokenHandle As IntPtr, ByRef hToken As IntPtr) As Boolean
    116.         Return NativeMethods.DuplicateTokenEx(tokenHandle, AccessRights.MaximumAllowed, attributes, SecurityImpersonationLevel.SecurityIdentification, TokenType.TokenPrimary, hToken)
    117.     End Function
    118.  
    119.     ''' <summary>
    120.     '''     Creates a new process and its primary thread. The new process runs in the security context of the user represented
    121.     '''     by the specified token.
    122.     ''' </summary>
    123.     ''' <param name="dwCreationFlags">
    124.     '''     The access flags to control the ability of the process.
    125.     ''' </param>
    126.     ''' <param name="startupInfo">
    127.     '''     The startup attributes returned from the <see cref="GetStartupInfo" /> method.
    128.     ''' </param>
    129.     ''' <param name="processInformation">
    130.     '''     The <see cref="ProcessInformation" /> that contains the process startup information.
    131.     ''' </param>
    132.     ''' <param name="application">
    133.     '''     The application to create including any command line parameters.
    134.     ''' </param>
    135.     ''' <param name="attributes">
    136.     '''     The security attributes returned from the <see cref="SecurityAttributes" /> method.
    137.     ''' </param>
    138.     ''' <param name="hToken">
    139.     '''     The duplicate access token associated with the process returned from <see cref="TryDuplicateToken" /> method.
    140.     ''' </param>
    141.     Private Shared Sub TryCreateProcessAsUser(dwCreationFlags As Integer, ByRef startupInfo As StartupInfoA, ByRef processInformation As ProcessInformation, application As String, ByRef attributes As SecurityAttributes, hToken As IntPtr)
    142.         NativeMethods.CreateProcessAsUser(hToken, Nothing, application, attributes, attributes, True, dwCreationFlags, IntPtr.Zero, Nothing, startupInfo, processInformation)
    143.     End Sub
    144.  
    145.     ''' <summary>
    146.     '''     A structure and associated data that contains the startup information for a process at creation time.
    147.     ''' </summary>
    148.     ''' <returns>
    149.     '''     The <see cref="StartupInfoA" /> that contains the startup attributes.
    150.     ''' </returns>
    151.     Private Shared Function GetStartupInfo() As StartupInfoA
    152.         Dim startupInfo As New StartupInfoA() With {.cb = Marshal.SizeOf(startupInfo), .lpDesktop = MWindowStationDesktopCreation, .dwFlags = startupInfo.dwFlags Or AccessRights.StartFUsestdhandles}
    153.         Return startupInfo
    154.     End Function
    155.  
    156.     ''' <summary>
    157.     '''     A structure and associated data that contains the security information for a securable object.
    158.     ''' </summary>
    159.     ''' <returns>
    160.     '''     The <see cref="SecurityAttributes" /> that contains the security attributes.
    161.     ''' </returns>
    162.     Private Shared Function GetSecurityAttributes() As SecurityAttributes
    163.         Dim attributes As New SecurityAttributes() With {.nLength = Marshal.SizeOf(attributes), .bInheritHandle = True}
    164.         Return attributes
    165.     End Function
    166.  
    167.     ''' <summary>
    168.     '''     Gets the process ID where the consoles session identifier matches the Terminal Services session identifier.
    169.     ''' </summary>
    170.     ''' <param name="processName">
    171.     '''     The name that the system uses to identify the process to the user.
    172.     ''' </param>
    173.     ''' <param name="dwSessionId">
    174.     '''     The session identifier of the console session.
    175.     ''' </param>
    176.     ''' <returns>
    177.     '''     The unique identifier for the associated process.
    178.     ''' </returns>
    179.     Private Shared Function GetSpecifiedId(processName As String, dwSessionId As Integer) As Integer
    180.         Return (From p In Process.GetProcessesByName(processName) Where p.SessionId = dwSessionId Select p.Id).FirstOrDefault()
    181.     End Function
    182.  
    183.     ''' <summary>
    184.     '''     Formats forward slashes in favor of back slashes to avoid command line option specifier conflicts(DOS).
    185.     ''' </summary>
    186.     ''' <param name="value">
    187.     '''     The path to evaluate.
    188.     ''' </param>
    189.     ''' <returns>
    190.     '''     A correctly formatted path.
    191.     ''' </returns>
    192.     Private Shared Function CreateRelativePath(value As String) As String
    193.         Return Text.RegularExpressions.Regex.Replace(value, "/", "")
    194.     End Function
    195. End Class

    Nativemethods.vb

    vb Code:
    1. Imports KaiService.Enums
    2. Imports KaiService.Structures
    3.  
    4. Public Class NativeMethods
    5.     ''' <summary>
    6.     '''     Retrieves the session identifier of the console session. The console session is the session that is currently
    7.     '''     attached to the physical console.
    8.     '''     Note that it is not necessary that Remote Desktop Services be running for this function to succeed.
    9.     ''' </summary>
    10.     ''' <returns>
    11.     '''     The session identifier of the session that is attached to the physical console. If there is no session attached to
    12.     '''     the physical console,
    13.     '''     (for example, if the physical console session is in the process of being attached or detached), this function
    14.     '''     returns 0xFFFFFFFF.
    15.     ''' </returns>
    16.     ''' <remarks>
    17.     '''     See [url]https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-wtsgetactiveconsolesessionid[/url]
    18.     ''' </remarks>
    19.     <DllImport("kernel32.dll")>
    20.     Public Shared Function WTSGetActiveConsoleSessionId() As UInteger
    21.     End Function
    22.  
    23.     ''' <summary>
    24.     '''     Closes an open object handle.
    25.     ''' </summary>
    26.     ''' <param name="hObject">
    27.     '''     A valid handle to an open object.
    28.     ''' </param>
    29.     ''' <returns>
    30.     '''     If the function succeeds, the return value is nonzero.
    31.     ''' </returns>
    32.     ''' <remarks>
    33.     '''     See [url]https://msdn.microsoft.com/en-us/library/windows/desktop/ms724211(v=vs.85).aspx[/url]
    34.     ''' </remarks>
    35.     <DllImport("kernel32.dll", SetLastError:=True)>
    36.     Public Shared Function CloseHandle(<[In]> hObject As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
    37.     End Function
    38.  
    39.     ''' <summary>
    40.     '''     Opens an existing local process object.
    41.     ''' </summary>
    42.     ''' <param name="dwDesiredAccess">
    43.     '''     The access to the process object. This access right is checked against the
    44.     '''     security descriptor for the process. This parameter can be one or more of the process access rights.
    45.     ''' </param>
    46.     ''' <param name="bInheritHandle">
    47.     '''     If this value is TRUE, processes created by this process will inherit the handle.
    48.     '''     Otherwise, the processes do not inherit this handle.
    49.     ''' </param>
    50.     ''' <param name="dwProcessId">
    51.     '''     The identifier of the local process to be opened.
    52.     ''' </param>
    53.     ''' <returns>
    54.     '''     If the function succeeds, the return value is an open handle to the specified process.
    55.     '''     If the function fails, the return value is NULL.
    56.     ''' </returns>
    57.     ''' <remarks>
    58.     '''     See [url]https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-openprocess[/url]
    59.     ''' </remarks>
    60.     <DllImport("kernel32.dll", SetLastError:=True)>
    61.     Public Shared Function OpenProcess(<[In]> dwDesiredAccess As AccessRights,
    62.                                        <[In]> bInheritHandle As Boolean,
    63.                                        <[Out]> dwProcessId As Integer) As IntPtr
    64.     End Function
    65.  
    66.     ''' <summary>
    67.     '''     Creates a new primary token or impersonation token that duplicates an existing token.
    68.     ''' </summary>
    69.     ''' <param name="ProcessHandle">
    70.     '''     A handle to the process whose access token is opened.
    71.     '''     The process must have the PROCESS_QUERY_INFORMATION access permission.
    72.     ''' </param>
    73.     ''' <param name="DesiredAccess">
    74.     '''     Specifies an access mask that specifies the requested types of access to the access token.
    75.     '''     These requested access types are compared with the discretionary access control list (DACL) of the token to
    76.     '''     determine which accesses are granted or denied.
    77.     ''' </param>
    78.     ''' <param name="TokenHandle">
    79.     '''     A pointer to a handle that identifies the newly opened access token when the function returns.
    80.     ''' </param>
    81.     ''' <returns>
    82.     '''     If the function succeeds, the return value is nonzero.
    83.     ''' </returns>
    84.     ''' <remarks>
    85.     '''     See [url]https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-openprocesstoken[/url]
    86.     ''' </remarks>
    87.     <DllImport("advapi32.dll", SetLastError:=True)>
    88.     Public Shared Function OpenProcessToken(ByVal processHandle As IntPtr,
    89.                                             ByVal desiredAccess As Integer,
    90.                                             ByRef tokenHandle As IntPtr) As Boolean
    91.     End Function
    92.  
    93.     ''' <summary>
    94.     '''     The DuplicateTokenEx function creates a new access token that duplicates an existing token. This function
    95.     '''     can create either a primary token or an impersonation token.
    96.     ''' </summary>
    97.     ''' <param name="hExistingToken">
    98.     '''     A handle to an access token opened with TOKEN_DUPLICATE access.
    99.     ''' </param>
    100.     ''' <param name="dwDesiredAccess">
    101.     '''     Specifies the requested access rights for the new token. The DuplicateTokenEx function compares the requested
    102.     '''     access rights with the existing token's
    103.     '''     discretionary access control list (DACL) to determine which rights are granted or denied. To request the same
    104.     '''     access rights as the existing token,
    105.     '''     specify zero. To request all access rights that are valid for the caller, specify MAXIMUM_ALLOWED.
    106.     ''' </param>
    107.     ''' <param name="lpTokenAttributes">
    108.     '''     A pointer to a SECURITY_ATTRIBUTES structure that specifies a security descriptor for the new token and determines
    109.     '''     whether child processes can inherit
    110.     '''     the token. If lpTokenAttributes is NULL, the token gets a default security descriptor and the handle cannot be
    111.     '''     inherited. If the security descriptor
    112.     '''     contains a system access control list (SACL), the token gets ACCESS_SYSTEM_SECURITY access right, even if it was
    113.     '''     not requested in dwDesiredAccess.
    114.     ''' </param>
    115.     ''' <param name="impersonationLevel">
    116.     '''     Specifies a value from the SECURITY_IMPERSONATION_LEVEL enumeration that indicates the impersonation level of the
    117.     '''     new token.
    118.     ''' </param>
    119.     ''' <param name="tokenType">
    120.     '''     Specifies one of the following values from the TOKEN_TYPE enumeration.
    121.     ''' </param>
    122.     ''' <param name="phNewToken">
    123.     '''     A pointer to a HANDLE variable that receives the new token.
    124.     ''' </param>
    125.     ''' <returns>
    126.     '''     If the function succeeds, the function returns a nonzero value.
    127.     ''' </returns>
    128.     ''' <remarks>
    129.     '''     See [url]https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-duplicatetokenex[/url]
    130.     ''' </remarks>
    131.     <DllImport("advapi32.dll", EntryPoint:="DuplicateTokenEx")>
    132.     Public Shared Function DuplicateTokenEx(hExistingToken As IntPtr,
    133.                                             dwDesiredAccess As UInteger,
    134.                                             ByRef lpTokenAttributes As SecurityAttributes,
    135.                                             impersonationLevel As Integer,
    136.                                             tokenType As Integer,
    137.                                             ByRef phNewToken As IntPtr) As Boolean
    138.     End Function
    139.  
    140.     ''' <summary>
    141.     '''     Creates a new process and its primary thread. The new process runs in the security context of the user represented
    142.     '''     by the specified token.
    143.     ''' </summary>
    144.     ''' <param name="hToken">
    145.     '''     A handle to the primary token that represents a user. The handle must have the TOKEN_QUERY, TOKEN_DUPLICATE, and
    146.     '''     TOKEN_ASSIGN_PRIMARY access rights.
    147.     '''     For more information, see Access Rights for Access-Token Objects. The user represented by the token must have read
    148.     '''     and execute access to the application
    149.     '''     specified by the lpApplicationName or the lpCommandLine parameter.
    150.     ''' </param>
    151.     ''' <param name="lpApplicationName">
    152.     '''     The name of the module to be executed. This module can be a Windows-based application. It can be some other type of
    153.     '''     module (for example, MS-DOS or OS/2)
    154.     '''     if the appropriate subsystem is available on the local computer.
    155.     ''' </param>
    156.     ''' <param name="lpCommandLine">
    157.     '''     The command line to be executed. The maximum length of this string is 32K characters. If lpApplicationName is NULL,
    158.     '''     the module name portion of lpCommandLine is
    159.     '''     limited to MAX_PATH characters.
    160.     ''' </param>
    161.     ''' <param name="lpProcessAttributes">
    162.     '''     A pointer to a SECURITY_ATTRIBUTES structure that specifies a security descriptor for the new process object and
    163.     '''     determines whether child processes can
    164.     '''     inherit the returned handle to the process. If lpProcessAttributes is NULL or lpSecurityDescriptor is NULL, the
    165.     '''     process gets a default security descriptor
    166.     '''     and the handle cannot be inherited. The default security descriptor is that of the user referenced in the hToken
    167.     '''     parameter. This security descriptor may not
    168.     '''     allow access for the caller, in which case the process may not be opened again after it is run. The process handle
    169.     '''     is valid and will continue
    170.     '''     to have full access rights.
    171.     ''' </param>
    172.     ''' <param name="lpThreadAttributes">
    173.     '''     A pointer to a SECURITY_ATTRIBUTES structure that specifies a security descriptor for the new thread object and
    174.     '''     determines whether child processes can
    175.     '''     inherit the returned handle to the thread. If lpThreadAttributes is NULL or lpSecurityDescriptor is NULL, the
    176.     '''     thread gets a default security descriptor
    177.     '''     and the handle cannot be inherited. The default security descriptor is that of the user referenced in the hToken
    178.     '''     parameter. This security descriptor may
    179.     '''     not allow access for the caller.
    180.     ''' </param>
    181.     ''' <param name="bInheritHandle">
    182.     '''     If this parameter is TRUE, each inheritable handle in the calling process is inherited by the new process. If the
    183.     '''     parameter is FALSE, the handles are
    184.     '''     not inherited. Note that inherited handles have the same value and access rights as the original handles.
    185.     ''' </param>
    186.     ''' <param name="dwCreationFlags">
    187.     '''     The flags that control the priority class and the creation of the process. For a list of values, see Process
    188.     '''     Creation Flags.
    189.     ''' </param>
    190.     ''' <param name="lpEnvironment">
    191.     '''     A pointer to an environment block for the new process. If this parameter is NULL, the new process uses the
    192.     '''     environment of the calling process.
    193.     ''' </param>
    194.     ''' <param name="lpCurrentDirectory">
    195.     '''     The full path to the current directory for the process. The string can also specify a UNC path.
    196.     ''' </param>
    197.     ''' <param name="lpStartupInfo">
    198.     '''     A pointer to a STARTUPINFO or STARTUPINFOEX structure.
    199.     ''' </param>
    200.     ''' <param name="lpProcessInformation">
    201.     '''     A pointer to a PROCESS_INFORMATION structure that receives identification information about the new process.
    202.     ''' </param>
    203.     ''' <returns>
    204.     '''     If the function succeeds, the return value is nonzero.
    205.     ''' </returns>
    206.     <DllImport("advapi32.dll", EntryPoint:="CreateProcessAsUser", SetLastError:=True)>
    207.     Public Shared Function CreateProcessAsUser(ByVal hToken As IntPtr, lpApplicationName As String,
    208.                                                ByVal lpCommandLine As String,
    209.                                                ByRef lpProcessAttributes As SecurityAttributes,
    210.                                                ByRef lpThreadAttributes As SecurityAttributes,
    211.                                                ByVal bInheritHandle As Boolean, dwCreationFlags As Integer,
    212.                                                ByVal lpEnvironment As IntPtr, lpCurrentDirectory As String,
    213.                                                ByRef lpStartupInfo As StartupInfoA,
    214.                                                <Out> ByRef lpProcessInformation As ProcessInformation) As Boolean
    215.     End Function
    216. End Class

    Please note the form software is corrupting CreateRelativePath Function by removing the backslash.

    Can't edit attachments http://www.mediafire.com/file/hseuqn...ution.zip/file

    Only tested Windows 10.
    Attached Files Attached Files

  2. #2

    Thread Starter
    Bad man! ident's Avatar
    Join Date
    Mar 2009
    Location
    Cambridge
    Posts
    5,216

    Re: Windows Service: Running a remote process in the System account

    Adding to that with a simple service manager. I have taken parts of what I use which I use but normally do not require any failure information. This is a simple example with PInvoke starting, stopping, installing and uninstalling a service.

    vb Code:
    1. Imports ServiceManager.NativeMethods
    2. Imports ServiceManager.Enums.ServiceError
    3. Imports ServiceManager.Enums.ServiceStart
    4. Imports ServiceManager.Enums.ServiceAccess
    5. Imports ServiceManager.Enums.ServiceType
    6. Imports ServiceManager.Enums.ServiceControlManagerRights
    7. Imports ServiceManager.Structures
    8.  
    9. ''' <summary>
    10. '''     Simplifies Controlling a Service.
    11. ''' </summary>
    12. Public Class Manager
    13.     ''' <summary>
    14.     '''     Using the sc create command attempt to create an entry for the service in the registry and in the Service Control Manager database.
    15.     ''' </summary>
    16.     ''' <param name="servicePath">
    17.     '''     Specifies the path given to service application on the system.
    18.     ''' </param>
    19.     ''' <param name="serviceName">
    20.     '''     Specifies the name given to the Service key in the registry.
    21.     ''' </param>
    22.     Friend Shared Sub InstallService(servicePath As String, serviceName As String)
    23.         Dim manager As IntPtr = GetManager()
    24.  
    25.         If manager = IntPtr.Zero Then
    26.             DisplayLastError()
    27.             CloseServiceHandle(manager)
    28.             Exit Sub
    29.         End If
    30.  
    31.         Dim service = TryCreateService(servicePath, serviceName, manager)
    32.  
    33.         If service = IntPtr.Zero Then
    34.             DisplayLastError()
    35.         End If
    36.  
    37.         CloseServiceHandle(manager)
    38.         CloseServiceHandle(service)
    39.     End Sub
    40.  
    41.     ''' <summary>
    42.     '''     Using the sc create command attempt to delete an entry for the service in the registry and in the Service Control Manager database.
    43.     ''' </summary>
    44.     ''' <param name="serviceName">
    45.     '''     Specifies the name given to the Service key in the registry.
    46.     ''' </param>
    47.     Friend Shared Sub UninstallService(serviceName As String)
    48.         Dim manager As IntPtr = GetManager()
    49.  
    50.         If manager = IntPtr.Zero Then
    51.             DisplayLastError()
    52.             CloseServiceHandle(manager)
    53.             Exit Sub
    54.         End If
    55.  
    56.         Dim service As IntPtr = TryGetService(serviceName, manager)
    57.  
    58.         If service = IntPtr.Zero OrElse Not DeleteService(service) Then
    59.             DisplayLastError()
    60.         End If
    61.  
    62.         CloseServiceHandle(manager)
    63.         CloseServiceHandle(service)
    64.     End Sub
    65.  
    66.     ''' <summary>
    67.     '''     Using the sc start command attempt to start an entry for the service in the registry and in the Service Control Manager database.
    68.     ''' </summary>
    69.     ''' <param name="serviceName">
    70.     '''     Specifies the name given to the Service key in the registry.
    71.     ''' </param>
    72.     Friend Shared Sub ServiceStart(serviceName As String)
    73.         Dim manager As IntPtr = GetManager()
    74.  
    75.         If manager = IntPtr.Zero Then
    76.             DisplayLastError()
    77.             CloseServiceHandle(manager)
    78.             Exit Sub
    79.         End If
    80.  
    81.         Dim service As IntPtr = TryGetService(serviceName, manager)
    82.  
    83.         If service = IntPtr.Zero OrElse TryStartService(service) Then
    84.             DisplayLastError()
    85.         End If
    86.  
    87.         CloseServiceHandle(manager)
    88.         CloseServiceHandle(service)
    89.     End Sub
    90.  
    91.     ''' <summary>
    92.     '''     Using the sc stop command attempt to stop an entry for the service in the registry and in the Service Control Manager database.
    93.     ''' </summary>
    94.     ''' <param name="serviceName">
    95.     '''     Specifies the name given to the Service key in the registry.
    96.     ''' </param>
    97.     Friend Shared Sub ServiceStop(serviceName As String)
    98.         Const serviceControlStop As Integer = &H1
    99.  
    100.         Dim manager As IntPtr = GetManager()
    101.  
    102.         If manager = IntPtr.Zero Then
    103.             DisplayLastError()
    104.             CloseServiceHandle(manager)
    105.             Exit Sub
    106.         End If
    107.  
    108.         Dim status As New ServiceStatus()
    109.         Dim service As IntPtr = TryGetService(serviceName, manager)
    110.  
    111.         If service = IntPtr.Zero OrElse Not TrySetServiceStatus(serviceControlStop, status, service) Then
    112.             DisplayLastError()
    113.         End If
    114.  
    115.         CloseServiceHandle(manager)
    116.         CloseServiceHandle(service)
    117.     End Sub
    118.  
    119.     ''' <summary>
    120.     '''     Attempts using OpenSCManager to open a handle to a service control manager database.
    121.     ''' </summary>
    122.     ''' <returns>
    123.     '''     If successful a handle to a service, otherwise NULL.
    124.     ''' </returns>
    125.     Private Shared Function GetManager() As IntPtr
    126.         Return OpenSCManager(Nothing, Nothing, ServiceControlManagerCreateService)
    127.     End Function
    128.  
    129.     ''' <summary>
    130.     '''  Attempts using OpenService with no privileges to obtain a handle for the process that called OpenService.
    131.     ''' </summary>
    132.     ''' <param name="serviceName">
    133.     '''     Specifies the name given to the Service key in the registry.
    134.     ''' </param>
    135.     ''' <param name="manager">
    136.     '''     The GetManager(), instance returned from <see cref="GetManager" /> method.
    137.     ''' </param>
    138.     ''' <returns>
    139.     '''     If the function fails, the return value is NULL. To get extended error information, call GetLastError.
    140.     ''' </returns>
    141.     Private Shared Function TryGetService(serviceName As String, manager As IntPtr) As IntPtr
    142.         Return OpenService(manager, serviceName, All)
    143.     End Function
    144.  
    145.     ''' <summary>
    146.     '''     Attempts using StartService(), the Service Control Manager (SCM) will spawn the service process.
    147.     ''' </summary>
    148.     ''' <param name="service">
    149.     '''     The StartService instance returned from <see cref="TryGetService" /> method.
    150.     ''' </param>
    151.     ''' <returns>
    152.     '''     If the function fails, the return value is zero. To get extended error information, call GetLastError.
    153.     ''' </returns>
    154.     Private Shared Function TryStartService(service As IntPtr) As Boolean
    155.         Return Not StartService(service, 0, Nothing)
    156.     End Function
    157.  
    158.     ''' <summary>
    159.     '''
    160.     ''' </summary>
    161.     ''' <param name="serviceControlStop">
    162.     '''     The constant pointer that notifies a service that it should stop.
    163.     ''' </param>
    164.     ''' <param name="status">
    165.     '''     A pointer to a <see cref="ServiceStatus"/> structure that receives the latest service status information.
    166.     ''' </param>
    167.     ''' <param name="service">
    168.     '''     A pointer to a <see cref="TryGetService"/> method.
    169.     ''' </param>
    170.     ''' <returns>
    171.     '''     If the function fails, the return value is zero. To get extended error information, call GetLastError.
    172.     ''' </returns>
    173.     Private Shared Function TrySetServiceStatus(serviceControlStop As Integer, ByRef status As ServiceStatus, service As IntPtr) As Boolean
    174.         Return ControlService(service, serviceControlStop, status)
    175.     End Function
    176.  
    177.     ''' <summary>
    178.     '''    Using the CreateService function creates a service object and installs it in the service control manager database by creating a key with the same name
    179.     '''    as the service under the following registry key:HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
    180.     ''' </summary>
    181.     ''' <param name="servicePath">
    182.     '''      Specifies the path given to service application on the system.
    183.     ''' </param>
    184.     ''' <param name="serviceName">
    185.     '''     Specifies the name given to the Service key in the registry.
    186.     ''' </param>
    187.     ''' <param name="manager">
    188.     '''     The OpenService instance returned from <see cref="GetManager" /> method.
    189.     ''' </param>
    190.     ''' <returns>
    191.     '''     If the function fails, the return value is NULL. To get extended error information, call GetLastError.
    192.     ''' </returns>
    193.     Private Shared Function TryCreateService(servicePath As String, serviceName As String, manager As IntPtr) As IntPtr
    194.         Dim intPtr As IntPtr = CreateService(manager, serviceName, serviceName, All, ServiceWin32OwnProcess Or ServiceInteractiveProcess,
    195.                                              ServiceAutoStart, ServiceErrorNormal, servicePath, Nothing, Nothing, Nothing, Nothing, Nothing)
    196.         Return intPtr
    197.     End Function
    198.  
    199.     ''' <summary>
    200.     '''     Displays the value of the calling thread's last-error code.
    201.     ''' </summary>
    202.     Private Shared Sub DisplayLastError()
    203.         Dim [error] = GetLastError()
    204.         MessageBox.Show($"Service manage encountered an error. {[error]}", Application.ProductName, MessageBoxButtons.OK,
    205.                         MessageBoxIcon.Error)
    206.     End Sub
    207.  
    208. End Class

    vb Code:
    1. Imports ServiceManager.Structures
    2.  
    3. Public Class NativeMethods
    4.     ''' <summary>
    5.     '''     Sends a control code to a service.
    6.     ''' </summary>
    7.     ''' <param name="hService">
    8.     '''     A handle to the service. This handle is returned by the OpenService or CreateService function.
    9.     '''     The access rights required for this handle depend on the dwControl code requested.
    10.     ''' </param>
    11.     ''' <param name="dwControl">
    12.     '''     This parameter can be one of the following control codes.
    13.     ''' </param>
    14.     ''' <param name="lpServiceStatus">
    15.     '''     A pointer to a SERVICE_STATUS structure that receives the latest service status information.
    16.     '''     The information returned reflects the most recent status that the service reported to the service control manager.
    17.     ''' </param>
    18.     ''' <returns>
    19.     '''     If the function succeeds, the return value is nonzero.
    20.     ''' </returns>
    21.     ''' <remarks>
    22.     '''     See [url]https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/nf-winsvc-controlservice[/url]
    23.     ''' </remarks>
    24.     <DllImport("advapi32.dll", SetLastError:=True)>
    25.     Public Shared Function ControlService(hService As IntPtr,
    26.                                           dwControl As ServiceControlManagerRights,
    27.                                           ByRef lpServiceStatus As ServiceStatus) As Boolean
    28.     End Function
    29.  
    30.     ''' <summary>
    31.     '''     Starts a service.
    32.     ''' </summary>
    33.     ''' <param name="hService">
    34.     '''     A handle to the service. This handle is returned by the OpenService or CreateService function,
    35.     '''     and it must have the SERVICE_START access right. For more information, see Service Security and Access Rights.
    36.     ''' </param>
    37.     ''' <param name="dwNumServiceArgs">
    38.     '''     The number of strings in the lpServiceArgVectors array. If lpServiceArgVectors is NULL, this parameter can be zero.
    39.     ''' </param>
    40.     ''' <param name="lpServiceArgVectors">
    41.     '''     The null-terminated strings to be passed to the ServiceMain function for the service as arguments. If there are no arguments, this parameter can be NULL.
    42.     '''     Otherwise, the first argument (lpServiceArgVectors[0]) is the name of the service, followed by any additional arguments (lpServiceArgVectors[1] through
    43.     '''     lpServiceArgVectors[dwNumServiceArgs-1]).
    44.     ''' </param>
    45.     ''' <returns>
    46.     '''     If the function succeeds, the return value is nonzero.
    47.     ''' </returns>
    48.     ''' <remarks>
    49.     '''     See [url]https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/nf-winsvc-startservicea[/url]
    50.     ''' </remarks>
    51.     <DllImport("advapi32", SetLastError:=True)>
    52.     Public Shared Function StartService(hService As IntPtr,
    53.                                         dwNumServiceArgs As Integer,
    54.                                         lpServiceArgVectors() As String) As <MarshalAs(UnmanagedType.Bool)> Boolean
    55.     End Function
    56.  
    57.     ''' <summary>
    58.     '''     Marks the specified service for deletion from the service control manager database.
    59.     ''' </summary>
    60.     ''' <param name="hService">
    61.     '''     A handle to the service. This handle is returned by the OpenService or CreateService function,
    62.     '''     and it must have the DELETE access right. For more information, see Service Security and Access Rights.
    63.     ''' </param>
    64.     ''' <returns>
    65.     '''     If the function succeeds, the return value is nonzero.
    66.     ''' </returns>
    67.     ''' <remarks>
    68.     '''     See [url]https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/nf-winsvc-deleteservice[/url]
    69.     ''' </remarks>
    70.     <DllImport("advapi32.dll", SetLastError:=True)>
    71.     Public Shared Function DeleteService(hService As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
    72.     End Function
    73.  
    74.     ''' <summary>
    75.     '''     Opens an existing service.
    76.     ''' </summary>
    77.     ''' <param name="hScManager">
    78.     '''     A handle to the service control manager database. The OpenSCManager function returns this handle. For more information, see Service Security
    79.     '''     and Access Rights.
    80.     ''' </param>
    81.     ''' <param name="lpServiceName">
    82.     '''     The name of the service to be opened. This is the name specified by the lpServiceName parameter of
    83.     '''     the CreateService function when the service object was created, not the service display name that is shown by user interface
    84.     '''     applications to identify the service.
    85.     ''' </param>
    86.     ''' <param name="dwDesiredAccess">
    87.     '''     The access to the service.
    88.     ''' </param>
    89.     ''' <returns>
    90.     '''     If the function succeeds, the return value is a handle to the service.
    91.     ''' </returns>
    92.     ''' <remarks>
    93.     '''     See [url]https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/nf-winsvc-openservicea[/url]
    94.     ''' </remarks>
    95.     <DllImport("advapi32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
    96.     Public Shared Function OpenService(hScManager As IntPtr,
    97.                                        lpServiceName As String,
    98.                                        dwDesiredAccess As Int32) As IntPtr
    99.     End Function
    100.  
    101.     ''' <summary>
    102.     '''     Retrieves the calling thread's last-error code value. The last-error code is maintained on
    103.     '''     a per-thread basis. Multiple threads do not overwrite each others last-error code.
    104.     ''' </summary>
    105.     ''' <returns>
    106.     '''     The return value is the calling thread's last-error code.
    107.     ''' </returns>
    108.     ''' <remarks>
    109.     '''     See [url]https://msdn.microsoft.com/en-us/library/windows/desktop/ms679360(v=vs.85).aspx[/url]
    110.     ''' </remarks>
    111.     <DllImport("kernel32.dll")>
    112.     Public Shared Function GetLastError() As UInteger
    113.     End Function
    114.  
    115.     ''' <summary>
    116.     '''     Closes a handle to a service control manager or service object.
    117.     ''' </summary>
    118.     ''' <param name="hScObject">
    119.     '''     A handle to the service control manager object or the service object to close. Handles to service control manager objects are
    120.     '''     returned by the OpenSCManager function, and handles to service objects are returned by either the OpenService or CreateService
    121.     '''     function.
    122.     ''' </param>
    123.     ''' <returns>
    124.     '''     If the function succeeds, the return value is nonzero.
    125.     ''' </returns>
    126.     ''' <remarks>
    127.     '''     See [url]https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/nf-winsvc-closeservicehandle[/url]
    128.     ''' </remarks>
    129.     <DllImport("advapi32.dll", SetLastError:=True)>
    130.     Public Shared Function CloseServiceHandle(hScObject As IntPtr) As Boolean
    131.     End Function
    132.  
    133.     ''' <summary>
    134.     '''     Establishes a connection to the service control manager on the specified computer and opens the specified service control manager database.
    135.     ''' </summary>
    136.     ''' <param name="lpMachineName">
    137.     '''     The name of the target computer. If the pointer is NULL or points to an empty string, the function connects to the service control
    138.     '''     manager on the local computer.
    139.     ''' </param>
    140.     ''' <param name="lpDatabaseName">
    141.     '''     The name of the service control manager database. This parameter should be set to SERVICES_ACTIVE_DATABASE. If it is NULL, the
    142.     '''     SERVICES_ACTIVE_DATABASE database is opened by default.
    143.     ''' </param>
    144.     ''' <param name="dwDesiredAccess">
    145.     '''     The access to the service control manager. For a list of access rights, see Service Security and Access Rights.
    146.     ''' </param>
    147.     ''' <returns>
    148.     '''     If the function succeeds, the return value is a handle to the specified service control manager database.
    149.     ''' </returns>
    150.     ''' <remarks>
    151.     '''     See [url]https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/nf-winsvc-openscmanagera[/url]
    152.     ''' </remarks>
    153.     <DllImport("advapi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
    154.     Public Shared Function OpenSCManager(lpMachineName As String,
    155.                                          lpDatabaseName As String,
    156.                                          dwDesiredAccess As Int32) As IntPtr
    157.     End Function
    158.  
    159.     ''' <summary>
    160.     '''     Creates a service object and adds it to the specified service control manager database.
    161.     ''' </summary>
    162.     ''' <param name="hScManager">
    163.     '''     A handle to the service control manager database. This handle is returned by the OpenSCManager function and must have the
    164.     '''     SC_MANAGER_CREATE_SERVICE access right.
    165.     ''' </param>
    166.     ''' <param name="lpServiceName">
    167.     '''     The name of the service to install. The maximum string length is 256 characters. The service control manager database preserves
    168.     '''     the case of the characters, but service name comparisons are always case insensitive. Forward-slash (/) and backslash () are
    169.     '''     not valid service name characters.
    170.     ''' </param>
    171.     ''' <param name="lpDisplayName">
    172.     '''     The display name to be used by user interface programs to identify the service. This string has a maximum length of 256 characters.
    173.     '''     The name is case-preserved in the service control manager. Display name comparisons are always case-insensitive.
    174.     ''' </param>
    175.     ''' <param name="dwDesiredAccess">
    176.     '''     The access to the service. Before granting the requested access, the system checks the access token of the calling process. For a list
    177.     '''     of values, see Service Security and Access Rights.
    178.     ''' </param>
    179.     ''' <param name="dwServiceType">
    180.     '''     The service type. This parameter can be one of the following values.
    181.     ''' </param>
    182.     ''' <param name="dwStartType">
    183.     '''     The service start options. This parameter can be one of the following values.
    184.     ''' </param>
    185.     ''' <param name="dwErrorControl">
    186.     '''     The severity of the error, and action taken, if this service fails to start. This parameter can be one of the following values.
    187.     ''' </param>
    188.     ''' <param name="lpBinaryPathName">
    189.     '''     The fully qualified path to the service binary file. If the path contains a space, it must be quoted so that it is correctly interpreted.
    190.     ''' </param>
    191.     ''' <param name="lpLoadOrderGroup">
    192.     '''     The names of the load ordering group of which this service is a member. Specify NULL or an empty string if the service does not belong to a group.
    193.     ''' </param>
    194.     ''' <param name="lpdwTagId">
    195.     '''     A pointer to a variable that receives a tag value that is unique in the group specified in the lpLoadOrderGroup parameter.
    196.     '''     Specify NULL if you are not changing the existing tag.
    197.     ''' </param>
    198.     ''' <param name="lpDependencies">
    199.     '''     A pointer to a double null-terminated array of null-separated names of services or load ordering groups that the system must start before
    200.     '''     this service. Specify NULL or an empty string if the service has no dependencies.
    201.     ''' </param>
    202.     ''' <param name="lpServiceStartName">
    203.     '''     The name of the account under which the service should run. If the service type is SERVICE_WIN32_OWN_PROCESS, use an account name in the
    204.     '''     form DomainName UserName.
    205.     ''' </param>
    206.     ''' <param name="lpPassword">
    207.     '''     The password to the account name specified by the lpServiceStartName parameter.
    208.     ''' </param>
    209.     ''' <returns>
    210.     '''     If the function succeeds, the return value is a handle to the service.
    211.     ''' </returns>
    212.     ''' <remarks>
    213.     '''     See [url]https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/nf-winsvc-createservicea[/url]
    214.     ''' </remarks>
    215.     <DllImport("advapi32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
    216.     Public Shared Function CreateService(hScManager As IntPtr, lpServiceName As String,
    217.                                          lpDisplayName As String, dwDesiredAccess As Int32, dwServiceType As Int32,
    218.                                          dwStartType As Int32, dwErrorControl As Int32, lpBinaryPathName As String,
    219.                                          lpLoadOrderGroup As String, lpdwTagId As Int32, lpDependencies As String,
    220.                                          lpServiceStartName As String, lpPassword As String) As IntPtr
    221.     End Function
    222. End Class

    Sorry can not upload full project. VBforums refuses to sort out bugs, but here are all the files.
    Attached Files Attached Files

  3. #3

    Thread Starter
    Bad man! ident's Avatar
    Join Date
    Mar 2009
    Location
    Cambridge
    Posts
    5,216

    Re: Windows Service: Running a remote process in the System account

    Now supports command line arguments to run under any process

    vb Code:
    1. Imports System.Text.RegularExpressions
    2. ''' <summary>
    3. '''     Spawns a new process under the account specified.
    4. ''' </summary>
    5. Public Class MainService
    6.     ''' <summary>
    7.     '''     The default instance to run as.
    8.     ''' </summary>
    9.     Private Const MDefaultRunAs As String = "winlogon"
    10.  
    11.     ''' <summary>
    12.     '''     The command line params regular expression pattern.
    13.     ''' </summary>
    14.     Private Const MCreateUserAsPattern As String = "(?i)(?<=--verb )[a-z0-9]+(?=)"
    15.  
    16.     ''' <summary>
    17.     '''     The command line params regular expression match.
    18.     ''' </summary>
    19.     Private Shared ReadOnly MCreateUserAsMatch As New Regex(MCreateUserAsPattern)
    20.  
    21.     ''' <summary>
    22.     '''     The service start up instance.
    23.     ''' </summary>
    24.     ''' <param name="args">
    25.     '''     The start up command lines arguments.
    26.     ''' </param>
    27.     Protected Overrides Sub OnStart(ByVal args() As String)
    28.         dim command = String.Join(" ", args)
    29.  
    30.         Dim runProcessAs = RunAsProcess(command)
    31.  
    32.         For Each argument In args
    33.             ProcessManager.TryCreateProcess(argument, runProcessAs)
    34.         Next
    35.     End Sub
    36.  
    37.     ''' <summary>
    38.     '''     Maybe One day.....Maybe
    39.     ''' </summary>
    40.     Protected Overrides Sub OnStop()
    41.         ' Add code here to perform any tear-down necessary to stop your service.
    42.     End Sub
    43.  
    44.     ''' <summary>
    45.     '''     Determines what process the newly created process will run under.
    46.     ''' </summary>
    47.     ''' <param name="command">
    48.     '''     The command line arguments.
    49.     ''' </param>
    50.     ''' <returns>
    51.     '''     The process name to run under.
    52.     ''' </returns>
    53.     Private Shared Function RunAsProcess(command As string) As String
    54.         Dim runProcessAs As String = MCreateUserAsMatch.Match(command).Value
    55.  
    56.         If runProcessAs = String.Empty Then
    57.             runProcessAs = MDefaultRunAs
    58.         End If
    59.  
    60.         Return runProcessAs
    61.     End Function
    62. End Class

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width