My Training Period: xx hours. Before you begin, read someinstruction here.
The Win32 programming skills:
The service control manager (SCM) controls a service by sending service control events to the service's control handler routine. The service must respond to control events in a timely manner so that the SCM can keep track of the state of the service. Also, the state of the service must match the description of its state that the SCM receives.Due to this communication mechanism between a service and the SCM, you must be careful when using multiple threads in a service. When a service is instructed to stop by the SCM, it must wait for all the threads to exit before reporting to the SCM that the service is stopped. Otherwise, the SCM can become confused about the state of the service and might fail to shut down correctly.
The SCM needs to be notified that the service is responding to the stop control event and that progress is being made in stopping the service. The SCM will assume the service is making progress if the service responds (through SetServiceStatus()) within the time (wait hint) specified in the previous call toSetServiceStatus(), and the check point is updated to be greater than the checkpoint specified in the previous call to SetServiceStatus(). If the service reports to the SCM that the service has stopped before all threads have exited, it is possible that the SCM will interpret this as a contradiction. This might result in a state where the service cannot be stopped or restarted.
Services and Redirected Drives
A service should not directly access local or network resources through mapped drive letters. Additionally, a service should not use the Windows Networking Functions to manage mapped drive letters. A service (or any process running in a different security context) that must access a remote resource should use the Universal Naming Convention (UNC) name to access the resource.
Although the WNet functions may return successfully, the resulting behavior is not as intended. When the system establishes a redirected drive, it is stored on a per-user basis. Only the user is able to manage the redirected drive. The system keeps track of redirected drives based on the user's logon security identifier (SID). The logon SID is a unique identifier for the user's logon session. A single user can have multiple, simultaneous logon sessions on the system. If a service is configured to run under a user account, the system always creates a new logon session for the user, and starts the service in that new logon session. Therefore, a service cannot manage the drive mappings established within the user's other session(s).
Drive letters are global to the system. All users on the system share drive letters A-Z. Users do not get their own sets of drive letters. This means a user can access the redirected drives of another user as long as they have proper security access. Attempting to redirect a drive letter that is already in use results in an error.Although redirected drives are global to all users, only the user who established the mapping can manage it. If a user attempts to remove or query information on a redirected drive established by a different user, the API fails. If a user attempts to enumerate the list of redirected drives, the only redirected drives in the list are those established by that user. Drives redirected by other users are not visible.
Windows Explorer can see all redirected drives because it calls the GetDriveType() function for each drive and displays an icon for each drive found. Windows Explorer creates an icon for redirected drives created by all users because drive letters are global to the system. However, the interactive user cannot use Windows Explorer to disconnect the drive, because the drive was created within a different logon session.If a service is running in the security context of the LocalSystem account establishes a drive mapping, only that service or another process running in the LocalSystem account can disconnect the drive. Note that all processes running in theLocalSystem account are running in the same logon session.
Drive letters are not global to the system. Each logon session receives its own set of drive letters A-Z. Therefore, redirected drives cannot be shared between processes running under different user accounts. Moreover, a service (or any process running within its own logon session) cannot access the drive letters established within a different logon session.
Programmers and system administrators use service configuration programs to modify or query the database of installed services. The database can also be accessed by using the registry functions. However, you should only use the SCM configuration functions, which ensure that the service is properly installed and configured. The SCM configuration functions require either a handle to an SCManager object or a handle to a service object. To obtain these handles, the service configuration program must:
Use the OpenSCManager() function to obtain a handle to the SCM database on a specified machine.
Use the OpenService() or CreateService() function to obtain a handle to the service object.
A configuration program uses the CreateService() function to install a new service in the SCM database. This function specifies the name of the service and provides configuration information that is stored in the database.A configuration program uses the DeleteService() function to remove an installed service from the database.To obtain the service name, call the GetServiceKeyName() function. The service display name, used in the Services control panel applet, can be obtained by calling the GetServiceDisplayName() function.A service configuration program can use the EnumServicesStatus() function to enumerate all services and their statuses. It can also use theEnumDependentServices() function to enumerate which services are dependent on a specified service object.
The system uses the configuration information to determine how to start the service. The configuration information also includes the service display name and its description. For example, for the DHCP service, you could use the display name "Dynamic Host Configuration Protocol Service" and the description "Provides internet addresses for computer on your network." To modify the configuration information for a service object, a configuration program uses the ChangeServiceConfig() or ChangeServiceConfig2() function. To retrieve the configuration information for a service object, the configuration program uses the QueryServiceConfig() or QueryServiceConfig2() function. To modify the security descriptor for either an SCManager object or a service object, a configuration program uses the SetServiceObjectSecurity() function. To retrieve a copy of the security descriptor, the configuration program uses theQueryServiceObjectSecurity() function.
Before you reconfigure a service object, you should use the LockServiceDatabase() function. This function tries to acquire a lock on the database and, if successful, prevents the SCM from starting a service while the database is being reconfigured. Failure to acquire a lock does not prevent a configuration program from successfully reconfiguring a service object. To release the lock on the database when the reconfiguration is complete, use the UnlockServiceDatabase() function. To determine whether the database is locked, use theQueryServiceLockStatus() function.
The Platform SDK contains a command-line utility, sc.exe, which can be used to query or modify the database of installed services. Its commands correspond to the functions provided by the SCM. The syntax is:
sc ServerName [command] ServiceName
ServerName - Optional server name. Use the form \\ServerName.
ServiceName - The name of the service, as specified when it was installed.
A service control program starts and controls services. It performs the following actions:
Starts a service or driver service, if the start type is SERVICE_DEMAND_START.
Sends control requests to a running service.
Queries the current status of a running service.
These actions require an open handle to the service object. To obtain the handle, the service control program must:
Use the OpenSCManager() function to obtain a handle to the SCM database on a specified machine.
Use the OpenService() or CreateService() function to obtain a handle to the service object.
To start a service or driver service, the service control program uses theStartService() function. The StartService() function fails if the database is locked. If this occurs, the service control program should wait a few seconds and call StartService() again. It can check the current lock status of the database by calling theQueryServiceLockStatus() function.
If the service control program is starting a service, it can use theStartService() function to specify an array of arguments to be passed to the service'sServiceMain() function. The StartService() function returns after a new thread has been created to execute theServiceMain() function. The service control program can retrieve the status of the newly started service in a SERVICE_STATUS structure by calling the QueryServiceStatus() function. During initialization, the dwCurrentState member should be SERVICE_START_PENDING. The dwWaitHint member is a time interval, in milliseconds, that indicates how long the service control program should wait before calling QueryServiceStatus() again. When the initialization is complete, the service changesdwCurrentState to SERVICE_RUNNING.If service does not update its status within 80 seconds, plus the last wait hint, the service control manager determines that the service has stopped responding. The service control manager will log an event and stop the service. If the program is starting a driver service, StartService() returns after the device driver has completed its initialization.
To send control requests to a running service, a service control program uses the ControlService() function. This function specifies a control value that is passed to the HandlerEx() function of the specified service. This control value can be a user-defined code, or it can be one of the standard codes that enable the calling program to perform the following actions:
Stop a service (SERVICE_CONTROL_STOP).
Pause a service (SERVICE_CONTROL_PAUSE).
Resume executing a paused service (SERVICE_CONTROL_CONTINUE).
Retrieve updated status information from a service (SERVICE_CONTROL_INTERROGATE).
Each service specifies the control values that it will accept and process. To determine which of the standard control values are accepted by a service, use the QueryServiceStatus() function or specify the SERVICE_CONTROL_INTERROGATE control value in a call to the ControlService() function. ThedwControlsAccepted member of the SERVICE_STATUS structure returned by these functions indicates whether the service can be stopped, paused, or resumed. All services accept the SERVICE_CONTROL_INTERROGATE control value. Take note that the QueryServiceStatus() function reports the most recent status for a specified service, but does not get an updated status from the service itself. Using theSERVICE_CONTROL_INTERROGATE control value in a call to ControlService() ensures that the status information returned is current.
A command-line utility, sc.exe also can be used to control a service. Its commands correspond to the functions provided by the SCM.
Each service executes in the security context of auser account. The user name and password of an account are specified by the CreateService() function at the time the service is installed. The user name and password can be changed by using the ChangeServiceConfig() function. You can use the QueryServiceConfig() function to get the user name (but not the password) associated with a service object.When starting a service, the SCM logs on to the account associated with the service. If the log on is successful, the system produces an access token and attaches it to the new service process. This token identifies the service process in all subsequent interactions with securable objects (objects that have a security descriptor associated with them). For example, if the service tries to open a handle to a pipe, the system compares the service's access token to the pipe's security descriptor before granting access.
The SCM does not maintain the passwords of service user accounts. If a password is expired, the logon fails and the service fails to start. The system administrator who assigns accounts to services can create accounts with passwords that never expire. The administrator can also manage accounts with passwords that expire by using a service configuration program to periodically change the passwords.If a service needs to recognize another service before sharing its information, the second service can either use the same account as the first service, or it can run in an account belonging to an alias that is recognized by the first service. Services that need to run in a distributed manner across the network should run in domain-wide accounts. You can use the following special accounts instead of creating user accounts for the service:
The LocalService account is a predefined local account. It has minimum privileges on the local computer and presents anonymous credentials on the network.The name of the account in all locales is NT AUTHORITY\LocalService. This account does not have a password. If you specify the LocalService account in a call to the CreateService() function, any password information you supply is ignored. The user SID is created from the SECURITY_LOCAL_SERVICE_RID value. The LocalService account has its own subkey under the HKEY_USERS registry key. Therefore, the HKEY_CURRENT_USERregistry key is associated with the LocalService account. The LocalService account has the following privileges:
Any privileges assigned to users and authenticated users
This account is not supported for Windows 2000 and Windows NT.
The NetworkService account is a predefined local account. It has minimum privileges on the local computer and acts as the computer on the network.The name of the account in all locales is NT AUTHORITY\NetworkService. This account does not have a password. If you specify the NetworkService account in a call to the CreateService() function, any password information you supply is ignored. A service that runs in the context of the NetworkService account presents the computer's credentials to remote servers. By default, the remote token contains SIDs for the Everyone andAuthenticated Users groups. The user SID is created from theSECURITY_NETWORK_SERVICE_RID value. The NetworkService account has its own subkey under the HKEY_USERS registry key. Therefore, the HKEY_CURRENT_USER registry key is associated with the NetworkService account. The NetworkService account has the following privileges:
Any privileges assigned to users and authenticated users.
Windows 2000 and Windows NT: This account is not supported.
The LocalSystem account is a predefined local account. It hasextensive privileges on the local computer, and acts as the computer on the network. The name of the account in all locales is .\LocalSystem (in other words, the corresponding C/C++ string with the backslash escaped is ".\\LocalSystem"). The name, "LocalSystem" or "ComputerName/LocalSystem" can also be used. This account does not have a password. If you specify the LocalSystem account in a call to the CreateService() function, any password information you supply is ignored. A service that runs in the context of the LocalSystem account inherits the security context of the SCM. The userSID is created from the SECURITY_LOCAL_SYSTEM_RID value. The account is not associated with any logged-on user account. This has several implications:
The registry key HKEY_CURRENT_USER is associated with the default user, not the current user. To access another user's profile, impersonate the user, and then accessHKEY_CURRENT_USER.
The service can open the registry key HKEY_LOCAL_MACHINE\SECURITY.
The service presents the computer's credentials to remote servers.
Windows NT: The service has limited access to network resources, such as shares and pipes, because it has no credentials and must connect using a null session. The following registry key contains the NullSessionPipes and NullSessionShares values, which are used to specify the pipes and shares to which null sessions may connect:
Alternatively, you could add the REG_DWORD valueRestrictNullSessAccess to the key and set it to 0 to allow all null sessions to access all pipes and shares created on that machine. If the service opens a command window and runs a batch file, the user could hit CTRL+C to terminate the batch file and gain access to a command window with LocalSystem permissions.The LocalSystem account has the following privileges and it is more powerful than the Administrator (Administrators group) account.
Most services do not need such a high privilege level. If your service does not need these privileges, and it is not an interactive service, consider using the LocalService account or the NetworkService account.
More on LocalSystem Account
One advantage of running under the LocalSystem account is that the service has complete unrestricted access to local resources. This is also the disadvantage ofLocalSystem because a LocalSystem service can do things that would bring down the entire system. In particular, a service running as LocalSystem on a domain controller (DC) has unrestricted access to Active Directory. This means that bugs in the service, or security attacks on the service, can damage the system or, if the service is on a DC, damage the entire enterprise network.
For these reasons, domain administrators at sensitive installations will be cautious about allowing services to run as LocalSystem. In fact, they may have policies against it, especially on DCs. If your service must run as LocalSystem, the documentation for your service should justify to domain administrators the reasons for granting the service the right to run at elevated privileges. Services should never run as LocalSystem on a domain controller.
When a service runs under the LocalSystem account on a computer that is a domain member, the service has whatever network access is granted to the computer account, or to any groups of which the computer account is a member. Be aware that in Windows 2000, a domain computer account is a service principal, similar to a user account. This means that a computer account can be in a security group, and an ACE in a security descriptor can grant access to a computer account. Be aware that adding computer accounts to groups is not recommended for two reasons:
Computer accounts are subject to deletion and re-creation if the computer leaves and thenrejoins the domain.
If you add a computer account to a group, all services running as LocalSystem on that computer are permitted the access rights of the group. This is because all LocalSystem services share the computer account of their host server. For this reason, it is particularly important that computer accounts not be made members of any domain administrator groups.
Computer accounts typically have few privileges and do not belong to groups. The defaultAccess Control List (ACL) protection in Active Directory permits minimal access for computer accounts. Consequently, services running as LocalSystem, on computers other than DCs, have only minimal access to Active Directory.
If your service runs under LocalSystem, you must test your service on a member server to ensure that your service has sufficient rights to read/write to Active Directory. A domain controller should not be the only Windows 2000 computer on which you test your service. Be aware that a service running under LocalSystem on a Windows 2000 domain controller has complete access to Active Directory and that a member server runs in the context of the computer account which has substantially fewer rights.
Each service has an associated window station and desktop. Only one window station, Winsta0 can be an interactive. Any desktops created on the interactive window station can display objects to the user and receive user input. Other desktops cannot display objects or receive user input. Processes started by the logged-on user are associated with the default desktop in the Winsta0 window station (Winsta0\default).The particular window station and desktop combination used by a service is determined by the account in which the service is running. If the service is running in the security context of the LocalSystem account and does not include the SERVICE_INTERACTIVE_PROCESS attribute, it uses the following window station and desktop:Service-0x0-3e7$\default. This window station is not interactive, so the service cannot display a user interface. In addition, processes created by the service cannot display a user interface.
If the service is running in the security context of a user account, the name of the window station is based on the user SID: Service-0xZ1-Z2$, where Z1 is the high part of the logon SID and Z2 is the low part of the logon SID. Because a SID is unique to the logon session, two services running in the same security context receive unique window stations.If the service is running in the context of the LocalSystem account and has the SERVICE_INTERACTIVE_PROCESS attribute, it is known as an interactive service. It can display a user interface and receive user input. To determine whether a service is running as an interactive service, call the GetProcessWindowStation() function to retrieve a handle to the window station, and theGetUserObjectInformation() function to test whether the window station has the WSF_VISIBLE attribute. However, note that the followingregistry key contains a value, NoInteractiveServices(), that controls the effect of SERVICE_INTERACTIVE_PROCESS:
The NoInteractiveServices value defaults to zero, which means that services with SERVICE_INTERACTIVE_PROCESS are allowed to run interactively. When NoInteractiveServices is set to a nonzero value, no service started thereafter is allowed to run interactively, regardless of whether it has SERVICE_INTERACTIVE_PROCESS.
Note that running interactive services under the LocalSystem context is a dangerous practice and should be avoided. If a service that is running on a multi-user system must interact with a user, the service should do so through a separate GUI application running within the context of the user session. This GUI application should be designed to communicate with the service through some method of interprocess communication, such as a named pipe. This is a client/server implementation.
The server process (the interactive service) then communicates with the appropriate client through interprocess communication to tell the client when to display the GUI. The client then communicates the results of the user interaction back to the service so that the service can take the appropriate actions. By using named pipes, the server can distinguish between multiple user processes by giving each pipe a unique name.
It is possible to display a message box from a service, even if it is not running in the LocalSystem account or not configured to run interactively. Simply call theMessageBox() function using MB_SERVICE_NOTIFICATION. Do not call MessageBox() during service initialization or from the HandlerEx() routine, unless you call it from a separate thread, so that you return to the SCM in a timely manner.
It is also possible to interact with the desktop from a non-interactive service by modifying the DACLs on the window station and desktop, or by impersonating the logged-on user and opening the interactive window station and desktop directly. This allows services to provide a user interface, while maintaining the security of applications running in the account of the logged-on user.
The Windows security model enables you to control access to service objects and the service control manager (SCM).
The system creates the security descriptor for the SCM. Note that unlike most other securable objects, the security descriptor for the SCM cannot be modified. The following access rights are granted.
Windows 2000 and earlier: The following access rights are granted.
When a process uses the OpenSCManager() function to open a handle to a database of installed services, it can request access rights. The system performs a security check against the security descriptor for the SCM before granting the requested access rights. The following are the specific access rights for the SCM.
Includes STANDARD_RIGHTS_REQUIRED, in addition to all access rights in this table.
Required to call the CreateService() function to create a service object and add it to the database.
Required to connect to the service control manager.
Required to call the EnumServicesStatus() function to list the services that are in the database.
Required to call the LockServiceDatabase() function to acquire a lock on the database.
Required to call the NotifyBootConfigStatus() function.
Required to call the QueryServiceLockStatus() function to retrieve the lock status information for the database.
The following are the generic access rights for the SCM.
Combines the following access types:
Combines the following access types:
Combines the following access types:
A process with the correct access rights can open a handle to the SCM that can be used in the OpenService(),EnumServicesStatus(), and QueryServiceLockStatus() functions. Only processes with Administrator privileges are able to open handles to the SCM that can be used by the CreateService() and LockServiceDatabase() functions.
The SCM creates a service object's security descriptor when the service is installed by the CreateService() function. The default security descriptor of a service object grants the following access.
Power Users group
Administrators group,System Operators group
To get or set the security descriptor for a service object, use theQueryServiceObjectSecurity() and SetServiceObjectSecurity() functions. When a process uses the OpenService() function, the system checks the requested access rights against the security descriptor for the service object. The following are the standard access rights for a service.
Required to call the QueryServiceObjectSecurity() or SetServiceObjectSecurity() function to access the SACL. The proper way to obtain this access is to enable the SE_SECURITY_NAME privilege in the caller's current access token, open the handle for ACCESS_SYSTEM_SECURITY access, and then disable the privilege.
Required to call the DeleteService() function to delete the service.
Required to call the QueryServiceObjectSecurity() function to query the security descriptor of the service object.
Required to call the SetServiceObjectSecurity() function to modify the DACL member of the service object's security descriptor.
Required to call the SetServiceObjectSecurity() function to modify the Owner and Group members of the service object's security descriptor.
The following are the specific access rights for a service.
Includes STANDARD_RIGHTS_REQUIRED in addition to all access rights in this table.
Required to call the ChangeServiceConfig() or ChangeServiceConfig2() function to change the service configuration. Because this grants the caller the right to change the executable file that the system runs, it should be granted only to administrators.
Required to call the EnumDependentServices() function to enumerate all the services dependent on the service.
Required to call the ControlService() function to ask the service to report its status immediately.
Required to call the ControlService() function to pause or continue the service.
Required to call the QueryServiceConfig() and QueryServiceConfig2() functions to query the service configuration.
Required to call the QueryServiceStatus() function to ask the service control manager about the status of the service.
Required to call the StartService() function to start the service.
Required to call the ControlService() function to stop the service.
Required to call the ControlService() function to specify a user-defined control code.
The following are the generic access rights for a service.
Combines the following access types:STANDARD_RIGHTS_READ
Combines the following access types:STANDARD_RIGHTS_WRITE
Combines the following access types:STANDARD_RIGHTS_EXECUTE
Granting certain access rights to untrusted users (such asSERVICE_CHANGE_CONFIG or SERVICE_STOP) can allow them to interfere with the execution of your service, and possibly allow them to run applications under the LocalSystem account.
You can use any one of the following methods to debug your service.
Use your debugger to debug the service while it is running. First, obtain the process identifier (PID) of the service process. After you have obtained the PID, attach to the running process. For syntax information, see the documentation included with your debugger.
Call the DebugBreak() function to invoke the debugger for just-in-time debugging.
Specify a debugger to use when starting a program. To do so, create a key called Image File Execution Options in the following registry location:
Create a subkey with the same name as your service (for example, MYSERVICE.EXE). To this subkey, add a value of type REG_SZ, namedDebugger. Use the full path to the debugger as the string value. In the Services control panel applet, select your service, click Startup and check Allow Service to Interact with Desktop. Note that the service must be an interactive service, or else the debugger cannot run on the default desktop.
To debug the initialization code of an auto-start service, you will have to temporarily install and run the service as a demand-start service. At times, it may be necessary to run a service as a console application for debugging purposes. In this scenario, the StartServiceCtrlDispatcher() function will return ERROR_FAILED_SERVICE_CONTROLLER_CONNECT. Therefore, be sure to structure your code such that service-specific code is not called when this error is returned.
A window station is a securable object that is associated with a process, and contains a clipboard, an atom table, and a set of desktop objects.The interactive window station,Winsta0, is the only window station that can display a user interface or receive user input. It is assigned to the logon session of the interactive user, and contains the keyboard, mouse, and display device. All other window stations are non-interactive, which means they cannot display a user interface or receive user input. Running services under a different account prevents them from accessing the interactive window station. In this Module we will not discuss windows station in detail.
A desktop is a securable object contained within a window station. A desktop has a logical display surface and contains windows, menus, and hooks. A window station can have multiple desktops. The desktops of the interactive window station, Winsta0, can be made to display a user interface and receive user input, but only one of these desktops at a time is active. This active desktop, also known as the input desktop, is the one that is currently visible to the user and that receives user input. Applications can use the OpenInputDesktop() function to get a handle to the input desktop. Applications that have the necessary access can use the SwitchDesktop() function to specify a different input desktop.
A desktop is associated with a thread and can be used to create and manage windows. There are three common desktops: default, Winlogon, and screen-saver. The default desktop is created when the logged-on user starts a process. It is the active desktop, and it is used to interact with the user. If you press the CTRL+ALT+DEL key sequence, you are switched to the Winlogon desktop. Whenever the screen saver activates, you are automatically switched to the screen-saver desktop, which protects the processes on the default desktop from unauthorized users. Unsecured screen savers run on Winsta0\default. Note that messages can be sent only between processes on the same desktop. In addition, the hook procedure of a process running on a particular desktop can only receive messages intended for windows created in the same desktop. In this Module we will not discuss desktop in detail.
----------------------------------------End of Windows Services Part 1----------------------------------
Further reading and digging:
Structure, enum, union and typedef story can be foundC/C++ struct, enum, union & typedef.
Windows data types areWindows data types.
Microsoft Visual C++, online MSDN.