A Windows service that can launch a GUI application in System Administrator mode.

Download icon. ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; * A Windows service that can launch a GUI application in System Administrator mode. * ; * * ; * Compiler used: PureBasic 5.42 32/64 - Tested under Windows 7 and Windows 10 * ; * * ; * The exe produced is a service named "Able.exe" that react to those parameters: * ; * /Install ; To install the service so Windows is aware of it. * ; * /Uninstall ; To uninstall the service. It must have been stopped before. * ; * /Status ; Call Service Manager to get the current service state. * ; * /Help ; Show these command line switch * ; * /? ; Show these command line switch * ; * * ; * To install, start a command window as Administrator and type "Able /Install". * ; * You can Start/Stop/Pause/Continue the service via the Service Manager, * ; * to start it click Start button, choose Execute and type "Services.msc". * ; * You can also act at the command prompt: * ; * "Net start Able" * ; * "Net stop Able" * ; * "Net pause Able" * ; * "Net continue Able" * ; * * ; * If you set #Debug to TRUE in code you will need a DebugView utility * ; * running in "Global capture" mode started as Administrator. * ; * This is great to see what the service is doing and for debugging. * ; * You can download one from Microsoft or CobaltFusion: * ; * https://docs.microsoft.com/en-us/sysinternals/downloads/debugview * ; * https://github.com/CobaltFusion/DebugViewPP/releases * ; * See in code for #DelayedStart, #RunAsSystemAdmin, and #AutoStop options. * ; * * ; * Once started, with #RunAsSystemAdmin = #True, * ; * the service, bypassing UAC, will start RegEdit.exe in the System Administrator mode, * ; * this is more powerfull than the Administrator mode, be carefull of what you do. * ; * Of course, you may alter the code to start some other applications. * ; * * ; * In Regedit, as System Admin, you will see subkeys like * ; * HKLM\SECURITY\Sam\* (Cache-Policy-Recovery-RXACT-SAM), you won't see this in normal Administrator mode. * ; * * ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; Normal folder for Windows service is System32, I do not care about this in the current demo. ; In memory problem, try to start Task Manager / Process (Check All users) / Right click "Able" and stop the process. EnableExplicit #PB_Compiler_Processor = #PB_Processor_x64 #PB_Compiler_ExecutableFormat = #PB_Compiler_Console ;#PB_Compiler_Filename = "Able.exe" ;************************************************************************************************************************************************ #Version = "2018-09-02 18:11:00" ; * #AppName = "Able" ;Dictionary: able - ^bl, adjective having enough strength, power or means (To do a thing). * #Debug = #True ;#False #True - See service called procedure and progress in a DebugView utility, must be in CAPTURE GLOBAL mode. * #DelayedStart = #False ;#False #True - To boot faster Windows will start the service after boot up is completely finished. * #RunAsSystemAdmin = #True ;#False #True - Start the GUI application RegEdit.exe with the powerfull SystemAdmin privilege. * #AutoStop = #False ;#False #True - Service stop by itself after RunAsSystemAdmin * ;************************************************************************************************************************************************ Declare WinMain() Declare serviceInstall() Declare serviceUninstall() Declare.l serviceQueryServiceStatus(sServer.s, sService.s) Declare.s serviceStringType(dwServiceType.l) Declare.s serviceStringStatus(dwServiceStatus.l) Declare.s serviceStringControlIsAccepted(dwControlAccepted.l) Declare.s serviceStringControl(dwServiceControl.l) Declare serviceMain(dwArgs.l, lpszArgv.i) Declare serviceThreadCreate() Declare serviceHandler(ControlValue.l) Declare serviceThread(idThread.l) Declare serviceSetServiceStatus(CurrentStatus.l, ExitCode.l, SpecificExitCode.l, Checkpoint.l, WaitHint.l) Declare servicePause() Declare serviceResume() Declare serviceStop() Declare serviceStopRaw() Declare serviceTerminate(ErrorCode.l) Declare.l RunAsSystemAdmin(sExeName.s) Declare StdOut(Text.s) #SERVICES_ACTIVE_DATABASE = "ServicesActive" #MAX_SERVICE_NAME_LEN = 128 #SERVICE_ADAPTER = $4 #SERVICE_RECOGNIZER_DRIVER = $8 #SERVICE_USER_OWN_PROCESS = $50 #SERVICE_USER_SHARE_PROCESS = $60 #SERVICE_INTERACTIVE_PROCESS = $100 #SERVICE_CONTROL_PARAMCHANGE = $06 #SERVICE_CONTROL_NETBINDADD = $07 #SERVICE_CONTROL_NETBINDREMOVE = $08 #SERVICE_CONTROL_NETBINDENABLE = $09 #SERVICE_CONTROL_NETBINDDISABLE = $0A #SERVICE_CONTROL_DEVICEEVENT = $0B #SERVICE_CONTROL_HARDWAREPROFILECHANGE = $0C #SERVICE_CONTROL_POWEREVENT = $0D #SERVICE_CONTROL_SESSIONCHANGE = $0E #SERVICE_CONTROL_PRESHUTDOWN = $0F #SERVICE_CONTROL_TIMECHANGE = $10 #SERVICE_CONTROL_TRIGGEREVENT = $20 #SERVICE_CONTROL_USERMODEREBOOT = $40 #TokenPrimary = 1 #NameSamCompatible = 2 Structure SERVICE_DELAYED_AUTO_START_INFO fDelayedAutostart.l ;Delayed autostart flag EndStructure Structure GlobalType ;All usefull variables in one place zComputerName.s{#MAX_COMPUTERNAME_LENGTH + 1} zServiceName.s{#MAX_SERVICE_NAME_LEN} zServiceDisplayName.s{#MAX_SERVICE_NAME_LEN} zExeName.s{#MAX_PATH} hServiceStatus.i hInstance.i hEvent.i hThread.i CurrentServiceStatus.l ServiceIsRunning.l ServiceIsPaused.l EndStructure Import "Kernel32.lib" WTSGetActiveConsoleSessionId() ProcessIdToSessionId(ProcessEntry_th32ProcessID.l, ProcessSessionId.l) EndImport Global *pg.GlobalType ;A single global pointer to a structure of many needed variables WinMain() ;Starting point ;_____________________________________________________________________________ Procedure WinMain() Protected g.GlobalType If #Debug : OutputDebugString_("WinMain") : EndIf g\zServiceName = #AppName ;Set the service name and display name g\zServiceDisplayName = #AppName + " service" ;Viewed in service manager, 256 char g\hInstance = GetModuleHandle_(0) Protected ComputerNameLen.l = SizeOf(g\zComputerName) GetComputerName_(@g\zComputerName, @ComputerNameLen) ;Use "" for default local service *pg = @g ;Set the GlobalType variable pointer Protected sCmdLine.s sCmdLine.s = LCase(ProgramParameter(0)) If FindString(sCmdLine, "/uninstall") serviceUninstall() ElseIf FindString(sCmdLine, "/install") serviceInstall() ElseIf FindString(sCmdLine, "/status") serviceQueryServiceStatus(*pg\zComputerName, *pg\zServiceName) ElseIf FindString(sCmdLine, "/help") | FindString(sCmdLine, "/?") StdOut(#AppName) StdOut(" /install") StdOut(" /uninstall") StdOut(" /status") StdOut(" /help") StdOut(" /?") StdOut(" Net start " + #AppName) StdOut(" Net stop " + #AppName) StdOut(" Net pause " + #AppName) StdOut(" Net continue " + #AppName) StdOut(" Services.msc (For Service Manager)") ElseIf Len(sCmdLine) ;Unknown command StdOut(" Unknown command! (Try /? for help.)") Else ;Command line is empty, Dim ServiceTable.SERVICE_TABLE_ENTRY(1) ;Last entry must be blank ServiceTable(0)\lpServiceName = @g\zServiceName ServiceTable(0)\lpServiceProc = @ServiceMain() If #Debug : OutputDebugString_("WinMain:StartServiceCtrlDispatcher") : EndIf ;StartServiceCtrlDispatcher_() connects the main thread of a service process to the service control manager, ;which causes the thread to be the service control dispatcher thread for the calling process. ;When Services.msc starts a service, it waits up to 30 sec for the service process to call StartServiceCtrlDispatcher, ;and does not return until all running services in the process have entered the SERVICE_STOPPED If StartServiceCtrlDispatcher_(@ServiceTable(0)) ;Service was started pointing to ServiceMain() and following code will be executed after SERVICE_STOPPED If #Debug : OutputDebugString_("WinMain:StartServiceCtrlDispatcher done : Stopped_OK") : EndIf Else Protected LastError.l LastError = GetLastError_() ;Might br ERROR_FAILED_SERVICE_CONTROLLER_CONNECT - ERROR_INVALID_DATA - ERROR_SERVICE_ALREADY_RUNNING If #Debug : OutputDebugString_("WinMain:StartServiceCtrlDispatcher not done : Error " + Str(LastError)) : EndIf ExitProcess_(LastError) EndIf EndIf EndProcedure ;_____________________________________________________________________________ Procedure.s serviceStringControlIsAccepted(dwControlAccepted.l) Protected sCtrlAccept.s If dwControlAccepted & #SERVICE_ACCEPT_STOP : sCtrlAccept + "SERVICE_ACCEPT_STOP, " : EndIf ;0x001 If dwControlAccepted & #SERVICE_ACCEPT_PAUSE_CONTINUE : sCtrlAccept + "SERVICE_ACCEPT_PAUSE_CONTINUE, " : EndIf ;0x002 If dwControlAccepted & #SERVICE_ACCEPT_SHUTDOWN : sCtrlAccept + "SERVICE_ACCEPT_SHUTDOWN, " : EndIf ;0x004 If dwControlAccepted & #SERVICE_ACCEPT_PARAMCHANGE : sCtrlAccept + "SERVICE_ACCEPT_PARAMCHANGE, " : EndIf ;0x008 If dwControlAccepted & #SERVICE_ACCEPT_NETBINDCHANGE : sCtrlAccept + "SERVICE_ACCEPT_NETBINDCHANGE, " : EndIf ;0x010 If dwControlAccepted & #SERVICE_ACCEPT_PRESHUTDOWN : sCtrlAccept + "SERVICE_ACCEPT_PRESHUTDOWN, " : EndIf ;0x100 If dwControlAccepted = 0 : sCtrlAccept = "Service not started, " : EndIf ;0x000 If Len(sCtrlAccept) = 0 : sCtrlAccept = "SERVICE_ACCEPT_UNKNOWN--" : EndIf ; ProcedureReturn(Left(sCtrlAccept, Len(sCtrlAccept) - 2) + " (0x" + Hex(dwControlAccepted) + ")") EndProcedure ;_____________________________________________________________________________ Procedure.s serviceStringType(dwServiceType.l) Protected sServiceType.s If dwServiceType & #SERVICE_KERNEL_DRIVER : sServiceType + "SERVICE_KERNEL_DRIVER, " : EndIf ;0x001 If dwServiceType & #SERVICE_FILE_SYSTEM_DRIVER : sServiceType + "SERVICE_FILE_SYSTEM_DRIVER, " : EndIf ;0x002 If dwServiceType & #SERVICE_ADAPTER : sServiceType + "SERVICE_ADAPTER, " : EndIf ;0x004 If dwServiceType & #SERVICE_RECOGNIZER_DRIVER : sServiceType + "SERVICE_RECOGNIZER_DRIVER, " : EndIf ;0x008 If dwServiceType & #SERVICE_WIN32_OWN_PROCESS : sServiceType + "SERVICE_WIN32_OWN_PROCESS, " : EndIf ;0x010 If dwServiceType & #SERVICE_WIN32_SHARE_PROCESS : sServiceType + "SERVICE_WIN32_SHARE_PROCESS, " : EndIf ;0x020 If dwServiceType & #SERVICE_USER_OWN_PROCESS : sServiceType + "SERVICE_USER_OWN_PROCESS, " : EndIf ;0x050 If dwServiceType & #SERVICE_USER_SHARE_PROCESS : sServiceType + "SERVICE_USER_SHARE_PROCESS, " : EndIf ;0x060 If dwServiceType & #SERVICE_INTERACTIVE_PROCESS : sServiceType + "SERVICE_INTERACTIVE_PROCESS, " : EndIf ;0x100 If Len(sServiceType) = 0 : sServiceType = "SERVICE_TYPE_UNKNOWN--" : EndIf ; ProcedureReturn(Left(sServiceType, Len(sServiceType) - 2) + " (0x" + Hex(dwServiceType) + ")") EndProcedure ;_____________________________________________________________________________ Procedure.s serviceStringStatus(dwServiceStatus.l) Protected sServiceStatus.s Select dwServiceStatus Case #SERVICE_STOPPED : sServiceStatus = "SERVICE_STOPPED" ;1 The service is not running. Case #SERVICE_START_PENDING : sServiceStatus = "SERVICE_START_PENDING" ;2 The service is starting. Case #SERVICE_STOP_PENDING : sServiceStatus = "SERVICE_STOP_PENDING" ;3 The service is stopping. Case #SERVICE_RUNNING : sServiceStatus = "SERVICE_RUNNING" ;4 The service is running. Case #SERVICE_CONTINUE_PENDING : sServiceStatus = "SERVICE_CONTINUE_PENDING" ;5 The service continue is pending. Case #SERVICE_PAUSE_PENDING : sServiceStatus = "SERVICE_PAUSE_PENDING" ;6 The service pause is pending. Case #SERVICE_PAUSED : sServiceStatus = "SERVICE_PAUSED" ;7 The service is paused. Default : sServiceStatus = "SERVICE_STATUS_UNKNOWN" ; EndSelect ProcedureReturn(sServiceStatus + " (" + Str(dwServiceStatus) + ")") EndProcedure ;_____________________________________________________________________________ Procedure.s serviceStringControl(dwServiceControl.l) Protected sServiceControl.s Select dwServiceControl Case #SERVICE_CONTROL_STOP : sServiceControl = "SERVICE_CONTROL_STOP" ;0x01 Notifies a service that it should stop. Case #SERVICE_CONTROL_PAUSE : sServiceControl = "SERVICE_CONTROL_PAUSE" ;0x02 Notifies a service that it should pause. Case #SERVICE_CONTROL_CONTINUE : sServiceControl = "SERVICE_CONTROL_CONTINUE" ;0x03 Notifies a paused service that it should resume. Case #SERVICE_CONTROL_INTERROGATE : sServiceControl = "SERVICE_CONTROL_INTERROGATE" ;0x04 Notifies a service to report its current status Case #SERVICE_CONTROL_SHUTDOWN : sServiceControl = "SERVICE_CONTROL_SHUTDOWN" ;0x05 Notifies a service to report its current status Case #SERVICE_CONTROL_PARAMCHANGE : sServiceControl = "SERVICE_CONTROL_PARAMCHANGE" ;0x06 Notifies a service that service-specific startup parameters have changed. Case #SERVICE_CONTROL_NETBINDADD : sServiceControl = "SERVICE_CONTROL_NETBINDADD" ;0x07 Notifies a network service that there is a new component for binding. Case #SERVICE_CONTROL_NETBINDREMOVE : sServiceControl = "SERVICE_CONTROL_NETBINDREMOVE" ;0x08 Notifies a network service that a component for binding has been removed. Case #SERVICE_CONTROL_NETBINDENABLE : sServiceControl = "SERVICE_CONTROL_NETBINDENABLE" ;0x09 Notifies a network service that a disabled binding has been enabled. Case #SERVICE_CONTROL_NETBINDDISABLE : sServiceControl = "SERVICE_CONTROL_NETBINDDISABLE" ;0x0A Notifies a network service that one of its bindings has been disabled. Case #SERVICE_CONTROL_DEVICEEVENT : sServiceControl = "SERVICE_CONTROL_DEVICEEVENT" ;0x0B Notifies a service of device events. Case #SERVICE_CONTROL_HARDWAREPROFILECHANGE : sServiceControl = "SERVICE_CONTROL_HARDWAREPROFILECHANGE" ;0x0C Notifies a service that the computer's hardware profile has changed. Case #SERVICE_CONTROL_POWEREVENT : sServiceControl = "SERVICE_CONTROL_POWEREVENT" ;0x0D Notifies a service of system power events. Case #SERVICE_CONTROL_SESSIONCHANGE : sServiceControl = "SERVICE_CONTROL_SESSIONCHANGE" ;0x0E Notifies a service of session change events. Case #SERVICE_CONTROL_PRESHUTDOWN : sServiceControl = "SERVICE_CONTROL_PRESHUTDOWN" ;0x0F Notifies a service that the system will be shutting down. Case #SERVICE_CONTROL_TIMECHANGE : sServiceControl = "SERVICE_CONTROL_TIMECHANGE" ;0x10 Notifies a service that the system time has changed. Case #SERVICE_CONTROL_TRIGGEREVENT : sServiceControl = "SERVICE_CONTROL_TRIGGEREVENT" ;0x20 Notifies a service registered for a service trigger event that the event has occurred. Case #SERVICE_CONTROL_USERMODEREBOOT : sServiceControl = "SERVICE_CONTROL_USERMODEREBOOT" ;0x40 Notifies a service that the user has initiated a reboot. Default : sServiceControl = "SERVICE_CONTROL_UNKNOWN" ; EndSelect ProcedureReturn(sServiceControl + " (0x" + Hex(dwServiceControl) + ")") EndProcedure ;_____________________________________________________________________________ Procedure.l serviceQueryServiceStatus(sServer.s, sService.s) Protected ServiceStat.SERVICE_STATUS Protected hScManager.i Protected hService.i Protected LastError.l If #Debug : OutputDebugString_("serviceQueryServiceStatus") : EndIf hScManager = OpenSCManager_(@sServer, #SERVICES_ACTIVE_DATABASE, #SC_MANAGER_ENUMERATE_SERVICE) If hScManager hService = OpenService_(hScManager, @sService, #SC_MANAGER_ENUMERATE_SERVICE) LastError = GetLastError_() If hService If QueryServiceStatus_(hService, @ServiceStat) StdOut(" Server: " + sServer) StdOut(" Service: " + sService) StdOut(" CurrentState: " + serviceStringStatus(ServiceStat\dwCurrentState)) StdOut(" ServiceType: " + serviceStringType(ServiceStat\dwServiceType)) StdOut(" ControlsAccepted: " + serviceStringControlIsAccepted(ServiceStat\dwControlsAccepted)) StdOut(" Win32ExitCode: " + Str(ServiceStat\dwWin32ExitCode)) StdOut(" SpecificExitCode: " + Str(ServiceStat\dwServiceSpecificExitCode)) StdOut(" CheckPoint: " + Str(ServiceStat\dwCheckPoint)) StdOut(" WaitHint: " + Str(ServiceStat\dwWaitHint)) ProcedureReturn(ServiceStat\dwCurrentState) EndIf CloseServiceHandle_(hService) Else Select LastError Case #ERROR_SERVICE_DOES_NOT_EXIST : StdOut(" Service " + sService + " : ERROR_SERVICE_DOES_NOT_EXIST") Case #ERROR_INVALID_NAME : StdOut(" Service " + sService + " : ERROR_INVALID_NAME") Case #ERROR_ACCESS_DENIED : StdOut(" Service " + sService + " : ERROR_ACCESS_DENIED") Case #ERROR_INVALID_HANDLE : StdOut(" Service " + sService + " : ERROR_INVALID_HANDLE") Default : StdOut(" Service " + sService + " : ERROR 0x" + Hex(LastError)) EndSelect EndIf CloseServiceHandle_(hScManager) Else StdOut(" serviceQueryServiceStatus:OpenSCManager failed") EndIf EndProcedure ;_____________________________________________________________________________ Procedure.l RunAsSystemAdmin(sExeName.s) ;Launches the given application with full system admin rights bypassing the UAC prompt ;sExeName The name of the application to launch Protected ProcessInfo.PROCESS_INFORMATION Protected ProcessEntry.PROCESSENTRY32 Protected SecurityAttributes.SECURITY_ATTRIBUTES Protected StartupInf.STARTUPINFO Static zWinSta0.s{20} Protected hUserTokenDup.l Protected hProcess.l Protected hProcessSnapshot.l Protected hProcessToken.l Protected SessionId.l Protected ProcessSessionId.l Protected CreationFlags.l Protected RetValProc.l Protected *FunctionPointer If #Debug : OutputDebugString_("RunAsSystemAdmin") : EndIf ;Obtain the currently active session id; every logged on user in the system has a unique session id SessionId = WTSGetActiveConsoleSessionId() ;Get the session-id of the console session: SessionId = 1 Console Active UserName or 0 Services Disconnected "" If SessionId <> #INVALID_HANDLE_VALUE ;Get the ProcessId of the WinLogon that have the same SessionId as the User's dwSessionId ;Obtain the process id of the winlogon process that is running within the currently active session If #Debug : OutputDebugString_("RunAsSystemAdmin:SessionId:OK") : EndIf hProcessSnapshot = CreateToolhelp32Snapshot_(#TH32CS_SNAPPROCESS | #TH32CS_SNAPTHREAD, #Null) If hProcessSnapshot <> #INVALID_HANDLE_VALUE If #Debug : OutputDebugString_("RunAsSystemAdmin:hProcessSnapshot:OK") : EndIf ProcessEntry\dwSize = SizeOf(PROCESSENTRY32) RetValProc = Process32First_(hProcessSnapshot, @ProcessEntry) While RetValProc Protected *BStr Protected *BStrData.String *BStr = @ProcessEntry\szExeFile *BStrData.String = @*BStr If LCase(*BStrData\s) = "winlogon.exe" ;May be more than one If #Debug : OutputDebugString_("RunAsSystemAdmin:winlogon:OK") : EndIf ProcessIdToSessionId(ProcessEntry\th32ProcessID, @ProcessSessionId) ;Process session id RunAsAdmin for winlogon, return nonzero on success If ProcessSessionId = SessionId ;Need WinLogOn sessionId like in TaskManager ;Obtain a handle to the winlogon process If #Debug : OutputDebugString_("RunAsSystemAdmin:ProcessSessionId:OK") : EndIf hProcess = OpenProcess_(#MAXIMUM_ALLOWED, #False, ProcessEntry\th32ProcessID) If hProcess If #Debug : OutputDebugString_("RunAsSystemAdmin:OpenProcess_:OK") : EndIf If OpenProcessToken_(hProcess, #TOKEN_DUPLICATE, @hProcessToken) If #Debug : OutputDebugString_("RunAsSystemAdmin:OpenProcessToken_:OK") : EndIf ;Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser SecurityAttributes\nLength = SizeOf(SECURITY_ATTRIBUTES) ;Copy the access token of the winlogon process, the newly created token will be a primary token If DuplicateTokenEx_(hProcessToken, #MAXIMUM_ALLOWED, SecurityAttributes, #SecurityIdentification, #TokenPrimary, @hUserTokenDup) If #Debug : OutputDebugString_("RunAsSystemAdmin:DuplicateTokenEx_:OK") : EndIf ;By default CreateProcessAsUser creates a process on a non-interactive window station, meaning ;the window station has a desktop that is invisible and the process is incapable of receiving ;user input. To remedy this we set the lpDesktop parameter to indicate we want to enable user ;interaction with the new process. zWinSta0 = "winsta0\default" StartupInf\lpDesktop = @zWinSta0 ;Interactive window station parameter; basically this indicates that the process created can display a GUI on the desktop StartupInf\cb = SizeOf(STARTUPINFO) ;Flags that specify the priority and creation method of the process CreationFlags = #NORMAL_PRIORITY_CLASS | #CREATE_NEW_CONSOLE If #Debug : OutputDebugString_("RunAsSystemAdmin:sExeName: " + sExeName) : EndIf Protected zCurDir.s{#MAX_PATH} If FindString(sExeName, "\") zCurDir = Left(sExeName, Len(sExeName) - FindString(ReverseString(sExeName), "\")) Else EndIf If #Debug : OutputDebugString_("RunAsSystemAdmin:zCurDir: " + zCurDir) : EndIf If #Debug : OutputDebugString_("RunAsSystemAdmin:CreateProcessAsUser") : EndIf ;Create a new process in the current user's logon session, Ok if return TRUE ;RegEdit/Admin can't see HKEY_LOCAL_MACHINE\SECURITY\* (Cache-Policy-Recovery-RXACT-SAM) ;RegEdit/SystemAdmin can see HKEY_LOCAL_MACHINE\SECURITY\* (Cache-Policy-Recovery-RXACT-SAM) ProcedureReturn(CreateProcessAsUser_(hUserTokenDup, ;Client's access token #Null, ;File to execute @sExeName, ;Command line @SecurityAttributes, ;Pointer to process SECURITY_ATTRIBUTES @SecurityAttributes, ;Pointer to thread SECURITY_ATTRIBUTES #False, ;Handles are not inheritable CreationFlags, ;Creation flags #Null, ;Pointer to new environment block @zCurDir, ;BYVAL %NULL, _ 'Name of current directory @StartupInf, ;Pointer to STARTUPINFO structure @ProcessInfo)) ;Receives information about new process If #Debug : OutputDebugString_(" With SystemAdmin right you have access to HKLM\SECURITY\* subkey, aka (Cache-Policy-Recovery-RXACT-SAM)") : EndIf CloseHandle_(hUserTokenDup) EndIf CloseHandle_(hProcessToken) EndIf CloseHandle_(hProcess) EndIf Break ;Job done EndIf EndIf RetValProc = Process32Next_(hProcessSnapshot, ProcessEntry) Wend CloseHandle_(hProcessSnapshot) EndIf EndIf EndProcedure ;_____________________________________________________________________________ Procedure serviceInstall() Protected ServiceDelayed.SERVICE_DELAYED_AUTO_START_INFO Protected ServiceDesc.SERVICE_DESCRIPTION Protected sServiceDescription.s Protected hServiceControlManager.i Protected hService.i If #Debug : OutputDebugString_("serviceInstall()") : EndIf hServiceControlManager = OpenSCManager_(*pg\zComputerName, #Null, #SC_MANAGER_CREATE_SERVICE) If hServiceControlManager GetModuleFileName_(*pg\hInstance, @*pg\zExeName, #MAX_PATH) ;Get exe full name hService = CreateService_(hServiceControlManager, *pg\zServiceName, *pg\zServiceDisplayName, #SERVICE_ALL_ACCESS, #SERVICE_WIN32_OWN_PROCESS | #SERVICE_INTERACTIVE_PROCESS, #SERVICE_AUTO_START, #SERVICE_ERROR_NORMAL, *pg\zExeName, #Null, #Null, #Null, #Null, #Null) ;SERVICE_DEMAND_START SERVICE_ERROR_IGNORE If hService ChangeServiceConfig_(hService, #SERVICE_NO_CHANGE, #SERVICE_AUTO_START, #SERVICE_ERROR_NORMAL, #NUL, #NUL, 0, #NUL, #NUL, #NUL, #NUL) sServiceDescription = #AppName + " start a GUI as SYSTEM Administrator." ;1024 bytes ServiceDesc\lpDescription = @sServiceDescription ChangeServiceConfig2_(hService, #SERVICE_CONFIG_DESCRIPTION, @ServiceDesc) If #DelayedStart ;Delayed service start, giving Windows time to breath on starting ServiceDelayed\fDelayedAutostart = #True ChangeServiceConfig2_(hService, #SERVICE_CONFIG_DELAYED_AUTO_START_INFO, @ServiceDelayed) EndIf StdOut("Service install successfull.") ProcedureReturn(#True) CloseServiceHandle_(hService) Else StdOut("Install - CreateService : Error.") EndIf CloseServiceHandle_(hServiceControlManager) Else StdOut("Install - OpenSCManager : Error.") StdOut("Need to be run as admin.") EndIf EndProcedure ;_____________________________________________________________________________ Procedure serviceUnInstall() Protected hServiceControlManager.i Protected hService.i If #Debug : OutputDebugString_("serviceUnInstall()") : EndIf hServiceControlManager = OpenSCManager_(*pg\zComputerName, #Null, #SC_MANAGER_CREATE_SERVICE) If hServiceControlManager hService = OpenService_(hServiceControlManager, *pg\zServiceName, #SERVICE_ALL_ACCESS) If hService If DeleteService_(hService) StdOut("Uninstall successfull.") ProcedureReturn(#True) Else StdOut("Uninstall - DeleteService : Error.") EndIf CloseServiceHandle_(hService) Else StdOut("Uninstall - OpenService : Error.") EndIf CloseServiceHandle_(hServiceControlManager) Else StdOut("Uninstall - OpenSCManager : Error.") EndIf EndProcedure ;_____________________________________________________________________________ Procedure serviceMain(dwArgs.l, lpszArgv.i) ;dwArgc The number of arguments in the lpszArgv array. ;lpszArgv The null-terminated argument strings passed to the service by the call to the StartService function ; that started the service. If there are no arguments, this parameter can be NULL. ; Otherwise, the first argument (lpszArgv[0]) is the name of the service, followed ; by any additional arguments (lpszArgv[1] through lpszArgv[dwArgc-1]). ; If the user starts a manual service using the Services snap-in from the Control Panel, ; the strings for the lpszArgv parameter come from the properties dialog box ; for the service (from the Services snap-in, right-click the service entry, click Properties, ; and enter the parameters in Start parameters.) ;All initialization tasks are done in ServiceMain() when the service is started. Protected SecurityAttribute.SECURITY_ATTRIBUTES Protected RetVal.l If #Debug : OutputDebugString_("serviceMain()") : EndIf *pg\hServiceStatus = RegisterServiceCtrlHandler_(*pg\zServiceName, @ServiceHandler()) If *pg\hServiceStatus ;Did Not work ;Startup is pending If serviceSetServiceStatus(#SERVICE_START_PENDING, #NO_ERROR, 0, 1, 5000) ;Create the termination event *pg\hEvent = CreateEvent_(SecurityAttribute, #True, #False, "") If *pg\hEvent ;Service startup is still pending If serviceSetServiceStatus(#SERVICE_START_PENDING, #NO_ERROR, 0, 2, 1000) RetVal = serviceThreadCreate() ;Start the service If RetVal ;Service did start If serviceSetServiceStatus(#SERVICE_RUNNING, #NO_ERROR, 0, 0, 0) ;Service is now running ;Wait for the signal to end If #Debug : OutputDebugString_("serviceMain : Service thread started : SERVICE_RUNNING : WaitForSingleObject") : EndIf WaitForSingleObject_(*pg\hEvent, #INFINITE) EndIf EndIf EndIf EndIf EndIf EndIf ServiceTerminate(GetLastError_()) EndProcedure ;_____________________________________________________________________________ Procedure serviceThreadCreate() Protected SecurityAttribute.SECURITY_ATTRIBUTES Protected idThread.l If #Debug : OutputDebugString_("serviceThreadCreate()") : EndIf *pg\hThread = CreateThread_(SecurityAttribute, 0, @ServiceThread(), 0, 0, idThread) If *pg\hThread ;The thread start OK *pg\ServiceIsRunning = #True ;Set the global to running ProcedureReturn(#True) EndIf EndProcedure ;_____________________________________________________________________________ Procedure serviceHandler(ControlValue.l) ;Service Control Handler - https://docs.microsoft.com/en-us/windows/desktop/services/service-control-handler-function ;Procedure will be called by Services.msc (Service Manager) If #Debug : OutputDebugString_("serviceHandler(Received request for " + serviceStringControl(ControlValue) + ")") : EndIf Select ControlValue Case #SERVICE_CONTROL_STOP, ;0x1 Service should stop. #SERVICE_CONTROL_SHUTDOWN, ;0x5 To do on service shutdown #CTRL_SHUTDOWN_EVENT ;0xA signal that the system sends when the system is shutting down. If *pg\ServiceIsPaused ;Resuming before ending serviceSetServiceStatus(#SERVICE_CONTINUE_PENDING, #NO_ERROR, 0, 1, 1000) ;Tell the SCM that we are resuming ServiceResume() ;Resume the service *pg\CurrentServiceStatus = #SERVICE_RUNNING ;Set the current state EndIf *pg\CurrentServiceStatus = #SERVICE_STOP_PENDING ;Set global status serviceSetServiceStatus(#SERVICE_STOP_PENDING, #NO_ERROR, 0, 1, 5000) ServiceStop() Case #SERVICE_CONTROL_PAUSE ;0x2 Service should pause. If (*pg\ServiceIsRunning <> #False) And (*pg\ServiceIsPaused = #False) ;Running And Not paused serviceSetServiceStatus(#SERVICE_PAUSE_PENDING, #NO_ERROR, 0, 1, 1000) ;Tell the SCM that we are pausing ServicePause() ;Pause it *pg\CurrentServiceStatus = #SERVICE_PAUSED ;Set the current state EndIf Case #SERVICE_CONTROL_CONTINUE ;0x3 Service should resume If (*pg\ServiceIsRunning <> #False) And (*pg\ServiceIsPaused <> #False) ;Running and paused serviceSetServiceStatus(#SERVICE_CONTINUE_PENDING, #NO_ERROR, 0, 1, 1000) ;Tell the SCM that we are resuming ServiceResume() ;Resume the service *pg\CurrentServiceStatus = #SERVICE_RUNNING ;Set the current state EndIf Case #SERVICE_CONTROL_INTERROGATE ;0x4 Service should report its current status to the control manager ;Simply return NO_ERROR; the SCM is aware of the current state of the service. ProcedureReturn(#NO_ERROR) ;A return value is needed else the command line "Net Start" will be erratic EndSelect serviceSetServiceStatus(*pg\CurrentServiceStatus, #NO_ERROR, 0, 0, 0) ProcedureReturn(#NO_ERROR) EndProcedure ;_____________________________________________________________________________ Procedure serviceThread(idThread.l) ;Here goes the service's job... If #RunAsSystemAdmin If #Debug : OutputDebugString_("-") : EndIf If #Debug ;Show UserNameEx and UserName Protected zUserName.s{#UNLEN} Protected UserNameLen.l = #UNLEN GetUserNameEx_(#NameSamCompatible, @zUserName, @UserNameLen) OutputDebugString_("UserNameEx: " + zUserName) GetUserName_(@zUserName, @UserNameLen) OutputDebugString_("UserName: " + zUserName + " <<<<<<<<<<<<<<<<<<< Will be 'System' (System Administrator).") EndIf ;Call RegEdit.exe Protected WindowsDirectory.s{#MAX_PATH} Protected FullNameExe.s{#MAX_PATH} GetWindowsDirectory_(@WindowsDirectory, #MAX_PATH) FullNameExe = WindowsDirectory + "\RegEdit.exe" If #Debug OutputDebugString_("serviceThread(RunAsSystemAdmin(" + FullNameExe + ")") OutputDebugString_("In RegEdit you can see HKLM\SECURITY\* (Cache-Policy-Recovery-RXACT-SAM") OutputDebugString_(" you can't in normal Administrator mode.") EndIf RunAsSystemAdmin(FullNameExe) ;Usually "C:\Windows\RegEdit.exe") Sleep_(2000) EndIf If #AutoStop ServiceStopRaw() ;Used when a service want To End by itself after his job is done Else Repeat If #Debug : OutputDebugString_("serviceThread() running " + FormatDate("%hh:%ii:%ss", Date())) : EndIf Sleep_(2000) ForEver EndIf EndProcedure ;_____________________________________________________________________________ Procedure serviceSetServiceStatus(CurrentStatus.l, ExitCode.l, ServiceSpecificExitCode.l, Checkpoint.l, WaitHint.l) If #Debug : OutputDebugString_("serviceSetServiceStatus(" + serviceStringStatus(CurrentStatus) + ")") : EndIf Protected ServiceStatus.SERVICE_STATUS ServiceStatus\dwServiceType = #SERVICE_WIN32_OWN_PROCESS ;Setup the UDT. ServiceStatus\dwCurrentState = CurrentStatus If CurrentStatus = #SERVICE_START_PENDING ServiceStatus\dwControlsAccepted = 0 Else ServiceStatus\dwControlsAccepted = #SERVICE_ACCEPT_STOP | #SERVICE_ACCEPT_PAUSE_CONTINUE | #SERVICE_ACCEPT_SHUTDOWN EndIf If ServiceSpecificExitCode = 0 ServiceStatus\dwWin32ExitCode = ExitCode Else ServiceStatus\dwWin32ExitCode = #ERROR_SERVICE_SPECIFIC_ERROR EndIf ServiceStatus\dwServiceSpecificExitCode = ServiceSpecificExitCode ServiceStatus\dwCheckPoint = Checkpoint ServiceStatus\dwWaitHint = WaitHint If SetServiceStatus_(*pg\hServiceStatus, ServiceStatus) ProcedureReturn(#True) Else ;Something went wrong, so stop the service ServiceStop() EndIf EndProcedure ;_____________________________________________________________________________ Procedure servicePause() If #Debug : OutputDebugString_("servicePause() " + FormatDate("%hh:%ii:%ss", Date())) : EndIf *pg\ServiceIsPaused = #True ;Set the global indicating that we are paused SuspendThread_(*pg\hThread) EndProcedure ;_____________________________________________________________________________ Procedure serviceResume() If #Debug : OutputDebugString_("serviceResume() " + FormatDate("%hh:%ii:%ss", Date())) : EndIf *pg\ServiceIsPaused = #False ;Set the global indicating that we are not paused ResumeThread_(*pg\hThread) EndProcedure ;_____________________________________________________________________________ Procedure serviceStop() If #Debug : OutputDebugString_("Service thread stopping at " + FormatDate("%hh:%ii:%ss", Date())) : EndIf *pg\ServiceIsRunning = #False ;Set the global flag indicating that the service is not running SetEvent_(*pg\hEvent) ;Set the event so the service will stop EndProcedure ;_____________________________________________________________________________ Procedure serviceStopRaw() ;Use when a service want to end by itself Protected ServiceStat.SERVICE_STATUS Protected hServiceControlManager.i Protected hService.i If #Debug : OutputDebugString_("serviceStopRaw()") : EndIf hServiceControlManager = OpenSCManager_(*pg\zComputerName, #Null, #SC_MANAGER_CREATE_SERVICE) If hServiceControlManager hService = OpenService_(hServiceControlManager, *pg\zServiceName, #SERVICE_ALL_ACCESS) If hService ControlService_(hService, #SERVICE_CONTROL_STOP, ServiceStat) EndIf CloseServiceHandle_(hServiceControlManager) EndIf EndProcedure ;_____________________________________________________________________________ Procedure serviceTerminate(ErrorCode.l) If #Debug : OutputDebugString_("serviceTerminate()") : EndIf If *pg\hEvent CloseHandle_(*pg\hEvent) EndIf If *pg\hServiceStatus serviceSetServiceStatus(#SERVICE_STOPPED, ErrorCode, 0, 0, 0) EndIf If *pg\hThread CloseHandle_(*pg\hThread) EndIf EndProcedure ;_____________________________________________________________________________ Procedure StdOut(Text.s) Protected CharDone.l Protected Reserved.l WriteConsole_(GetStdHandle_(#STD_OUTPUT_HANDLE), Text + #CRLF$, Len(Text) + 2, CharDone, Reserved) EndProcedure ;_____________________________________________________________________________ ; ; IDE Options = PureBasic 5.42 LTS (Windows - x64) ; ExecutableFormat = Console ; CursorPosition = 614 ; FirstLine = 591 ; Folding = ---- ; EnableAsm ; EnableUnicode ; EnableThread ; EnableXP ; EnableAdmin ; UseIcon = 274.ico ; Executable = Able.exe ; DisableDebugger ; CompileSourceDirectory ; Compiler = PureBasic 5.42 LTS (Windows - x64) ; IncludeVersionInfo ; VersionField0 = 1.0.0.0 ; VersionField1 = 1.0.0.0 ; VersionField2 = Bellisle ; VersionField3 = Able ; VersionField4 = 1.0.0.0 ; VersionField5 = 1.0.0.0 ; VersionField6 = Service to start a GUI as SYSTEM Administrator ; VersionField7 = Able ; VersionField8 = Able ; VersionField9 = 2018 ; VersionField10 = 2018