Windows service
- Pierre Bellisle
- User
- Posts: 35
- Joined: Wed Jun 27, 2018 5:12 am
Windows service
A Windows service template that can also launch a GUI application in System Administrator mode.
Have fun...
Have fun...
Re: Windows service
Thanks for this... will try it later.
Re: Windows service
@Pierre Bellisle: nice website with Powerbasic codes.
to preserve this purebasic code, i
extracted it from the web page:
to preserve this purebasic code, i
extracted it from the web page:
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
SPAMINATOR NR.1
Re: Windows service
I already saved it offline, too.Rings wrote:to preserve this purebasic code, i
extracted it from the web page
Re: Windows service
@Pierre Bellisle Thanks for the excellent code
@Rings Thanks for bringing it here
It will be usefull to have a possibilty to send paramters to the program launched that way
@Rings Thanks for bringing it here
It will be usefull to have a possibilty to send paramters to the program launched that way
Re: Windows service
thanks for giving the info!
Re: Windows service
I've found how to send paramters
But now i have another problem, i want to launch the exe with NORMAL rights, not System. For example, i want to open registry and see it normaly, without the "HKEY_LOCAL_MACHINE\SECURITY\*"
I understantd that the rights has to be changed, but how?
thanks
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 understantd that the rights has to be changed, but how?
thanks
- Pierre Bellisle
- User
- Posts: 35
- Joined: Wed Jun 27, 2018 5:12 am
Re: Windows service
It's been a year, still, if you never found a solution...
Just change...
If LCase(*BStrData\s) = "winlogon.exe"
to
If LCase(*BStrData\s) = "explorer.exe"
Just change...
If LCase(*BStrData\s) = "winlogon.exe"
to
If LCase(*BStrData\s) = "explorer.exe"
- Pierre Bellisle
- User
- Posts: 35
- Joined: Wed Jun 27, 2018 5:12 am
Re: Windows service
I recently worked with tatanas to insert some SERVICE_CONTROL_SESSIONCHANGE notifications.
While at it, I did many minors corrections...
The service can now write to a log file in addition to the OutputDebugString() command.
More, the service will now start an application launcher, so experimentation will be much easier.
This application launcher is named SysAdminExec.exe, it will tell you if you are Administrator, or System-Administrator, a corresponding icon will be chosen. From an .ini file, SysAdminExec will memorize the screen position and size, and the last 150 chosen executables. Thanks to a mutex, only one instance is allowed. An attempt to restart will bring the original to fore. SysAdminExec accepts <Drag and Drop> messages from applications with a lower elevation. You can also specify a list of executables via the command line.
I used compiler v. 5.72.
APIs lover should like.
Able service
Able service Able (Alternate address)
While at it, I did many minors corrections...
The service can now write to a log file in addition to the OutputDebugString() command.
More, the service will now start an application launcher, so experimentation will be much easier.
This application launcher is named SysAdminExec.exe, it will tell you if you are Administrator, or System-Administrator, a corresponding icon will be chosen. From an .ini file, SysAdminExec will memorize the screen position and size, and the last 150 chosen executables. Thanks to a mutex, only one instance is allowed. An attempt to restart will bring the original to fore. SysAdminExec accepts <Drag and Drop> messages from applications with a lower elevation. You can also specify a list of executables via the command line.
I used compiler v. 5.72.
APIs lover should like.
Able service
Able service Able (Alternate address)
Last edited by Pierre Bellisle on Thu Dec 17, 2020 4:12 am, edited 1 time in total.
- RichAlgeni
- Addict
- Posts: 914
- Joined: Wed Sep 22, 2010 1:50 am
- Location: Bradenton, FL
Re: Windows service
Elegant code is a joy to read! Nicely done!!!
- Kwai chang caine
- Always Here
- Posts: 5353
- Joined: Sun Nov 05, 2006 11:42 pm
- Location: Lyon - France
Re: Windows service
Waooouh !!! impressive all this collection of powerfulls codes
It's a pity it's PowerBasic codes
Mainly the USB codes ....they missing to PB
http://codesite.atwebpages.com/PureBASIC/Able.zip.txt
and rename to ZIP but impossible to uncompress W10/X64
It's a pity it's PowerBasic codes
Mainly the USB codes ....they missing to PB
I have downloadDownload (rename to .zip)
http://codesite.atwebpages.com/PureBASIC/Able.zip.txt
and rename to ZIP but impossible to uncompress W10/X64
The happiness is a road...
Not a destination
Not a destination
- Pierre Bellisle
- User
- Posts: 35
- Joined: Wed Jun 27, 2018 5:12 am
Re: Windows service
Rich,
I'm glad you liked. :-)
-
Hey Kwai!
Yep, it is mostly PowerBASIC code. Conversion to Pure should be not too hard to do in many cases I guess...
I took a look at Able.zip.txt and had the same problem you do.
Since this web server refuse zip files, I renamed it to .txt as a bypass.
After upload, checking the .txt file give an incorect bytes count.
Mabe an automated Windows to Unix CRLF/LF conversion, I'm not sure...
I did re-upload with a .zzz extention, then rename it with a .txt extention on the server.
The bytes count is now correct and my download/unzip try worked fine.
Give it a try...
Able service
Able service (Alternate address)
I'm glad you liked. :-)
-
Hey Kwai!
Yep, it is mostly PowerBASIC code. Conversion to Pure should be not too hard to do in many cases I guess...
I took a look at Able.zip.txt and had the same problem you do.
Since this web server refuse zip files, I renamed it to .txt as a bypass.
After upload, checking the .txt file give an incorect bytes count.
Mabe an automated Windows to Unix CRLF/LF conversion, I'm not sure...
I did re-upload with a .zzz extention, then rename it with a .txt extention on the server.
The bytes count is now correct and my download/unzip try worked fine.
Give it a try...
Able service
Able service (Alternate address)
Re: Windows service
What are the possible uses for this code?
- Pierre Bellisle
- User
- Posts: 35
- Joined: Wed Jun 27, 2018 5:12 am
Re: Windows service
Satisfy curiosity is the main purpose... :-)
The three significant aspects of this code are, how to code a service, get System account rights, and start a GUI from a service.
As is, it give super power to an application of your choice.
For example, if you want to access the registry HKEY_LOCAL_MACHINE\SAM via RegEdit.exe you'll need System rights.
You may do a web search with Windows "system account", many interesting things can be found, like
System Account in Windows or Identity and access protection - System account
etc...
The three significant aspects of this code are, how to code a service, get System account rights, and start a GUI from a service.
As is, it give super power to an application of your choice.
For example, if you want to access the registry HKEY_LOCAL_MACHINE\SAM via RegEdit.exe you'll need System rights.
You may do a web search with Windows "system account", many interesting things can be found, like
System Account in Windows or Identity and access protection - System account
etc...
Re: Windows service
Thanks!Pierre Bellisle wrote:Satisfy curiosity is the main purpose... ...