Windows service
Posted: Sat Oct 27, 2018 8:51 pm
A Windows service template that can also launch a GUI application in System Administrator mode.
Have fun...
Have fun...
http://www.purebasic.com
https://www.purebasic.fr/english/
Code: Select all
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
; * 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
I already saved it offline, too.Rings wrote:to preserve this purebasic code, i
extracted it from the web page
Code: Select all
path.s = GetPathPart(Mid(sExeName, 1, FindString(sExeName + " ", " ")))
file.s = Mid(sExeName, Len(path) + 1)
ProcedureReturn(CreateProcessAsUser_(hUserTokenDup, ;Client's access token
#Null, ;File to execute
@file, ;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
@path, ;BYVAL %NULL, _ 'Name of current directory
@StartupInf, ;Pointer to STARTUPINFO structure
@ProcessInfo)) ;Receives information about new process
I have downloadDownload (rename to .zip)
Thanks!Pierre Bellisle wrote:Satisfy curiosity is the main purpose... ...