Upload
duongxuyen
View
219
Download
4
Embed Size (px)
Citation preview
Background Services
2
CONTENTS
OVERVIEW ............................................................................................................................................. 3
EXERCISE 1: TRIGGER-START SERVICE ............................................................................................ 7 Task 1 – Implement Service Configuration Changes.......................................................................... 7
Task 2 – Add Code to Register the Service as Trigger-Start ............................................................. 10
Task 3 – Enable the UsbCopyService to Trigger-Start ...................................................................... 12
SUMMARY ............................................................................................................................................ 18
Background Services
3
Overview
Windows 7 and Windows Vista have introduced many improvements in background processing. The
modern challenges of implementing efficient background processes are:
Performance – boot latency, logon latency, shutdown latency; background work interfering with
foreground processing
Power consumption
Security – increased attack surface
Windows 7 background services and scheduled tasks offer a variety of mechanisms for minimizing
power consumption, reducing the system’s attack surface and improving application and system
performance. Among these mechanisms are:
Service requested security privileges
Service SIDs
Delayed auto-start services
Trigger-start services
Scheduled tasks conditions and settings
With the abundance of services in any modern installation of Windows, background processing must be
designed to meet security, performance and power consumption requirements.
Objectives
In this Hands-On Lab, you will learn how to:
Design and implement a trigger-start service
Minimize the amount of privileges requested by a service
System Requirements
You must have the following items to complete this lab:
Microsoft Visual Studio 2008
Windows 7
Windows 7 SDK
Background Services
4
Windows Sysinternals Process Explorer
Setup
This lab requires that the Windows 7 SDK is correctly integrated with Visual Studio 2008. To do this you
can follow these steps:
1. From the Start Menu, go to All Programs | Microsoft Windows SDK v7.0 | Visual Studio
Registration and click Windows SDK Configuration Tool. On the Windows SDK Configuration
Tool dialog, verify that the v7.0 version is selected and then click Make Current.
Figure 1
Windows SDK Configuration tool
2. You also need update Visual C++ directories to point to the Windows 7 SDK headers, libraries
and tools. These paths should match your local Windows 7 SDK installation. To do this, from the
Microsoft Visual Studio 2008 menu, select Tools | Options to open the Options dialog. Expand
the Projects and Solutions node from the left panel, select VC++ Directories and follow these
steps:
a. From the right panel, select Executable files from the combo located at the upper-right
corner. Then select the button, browse to the %Windows7SDKInstallDir%\v7.0\Bin
folder and click Select Folder.
Background Services
5
Figure 2
Configuring the VC++ directories
Note: Windows 7 SDK is installed by default under the C:\Program Files\Microsoft
SDKs\Windows folder.
b. Repeat the previous step adding the %Windows7SDKInstallDir%\v7.0\Include folder for
the Include files option, and the %Windows7SDKInstallDir%\v7.0\Lib folder for the
Library Files option.
c. Finally, click OK to save the settings.
Additionally, this lab requires specific “hard coded” folders to work with that you need to create:
1. On the local C drive you need to create a C:\FromUSB folder to which all the images from the
USB key will be copied.
2. On the generic USB key you need to create a ToCopy folder from which all the images will be
copied from.
Note: The service demonstrated in this exercise is not well-designed with regard to the design goals for
background processes on Windows. It is a just sample application showing how to use the Trigger Start
Services in Windows 7. For more information on designing efficient background processes, consult the
Microsoft whitepaper “Designing Efficient Background Processes”.
Background Services
7
Exercise 1: Trigger-Start Service
In this exercise, you will modify an existing service to be a better citizen of the operating system. You
will change the service’s requested security privileges to run with least privileges; and you will change
the service configuration so that it automatically starts when necessary instead of running and polling
the system in the background.
The sample service used for this purpose is a USB file copy service, which copies files from a specific
directory on any USB storage device inserted to the system. The techniques used in this exercise are
applicable to any service that can trigger-start when certain criteria are met.
Task 1 – Implement Service Configuration Changes
To modify service configuration programmatically, the Win32 API function ChangeServiceConfig2 must
be called with the appropriate parameters.
1. Open the starting solution Begin.sln located under the
%TrainingKitInstallDir%\BackgroundServices\Ex1-TriggerStartService\Begin folder, choosing
the language of your preference (C# or VB).
Note: Visual Studio 2008 must be run in elevated mode. To do this, right-click the Visual
Studio 2008 icon and select Run as Administrator.
2. In the ServiceControlInterop project, locate the ServiceControlInterop.cpp file, under the
Source Files folder, and add code in the RemoveAllPrivilegesFromService method to specify
that the service requires no privileges:
a. Allocate a SERVICE_REQUIRED_PRIVILEGES_INFO structure and set its
pmszRequiredPrivileges member to SE_CHANGE_NOTIFY_NAME L"\0".
b. Call the ChangeServiceConfig2 method with the
SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO information level and pass the address
of the structure allocated in the previous step.
Use the following code as a complete example (shown in bold):
C++
void ServiceControl::RemoveAllPrivilegesFromService(System::String^
serviceName)
{
....
if (hService == NULL)
{
Background Services
8
DWORD dwLastError = GetLastError();
CloseServiceHandle(hSCManager);
throw Marshal::GetExceptionForHR(dwLastError);
}
//This effectively strips out all other privileges.
SERVICE_REQUIRED_PRIVILEGES_INFO requiredPrivileges = {0};
requiredPrivileges.pmszRequiredPrivileges = SE_CHANGE_NOTIFY_NAME L"\0";
if (ChangeServiceConfig2(hService,
SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO, &requiredPrivileges) == FALSE)
{
DWORD dwLastError = GetLastError();
CloseServiceHandle(hService);
CloseServiceHandle(hSCManager);
throw Marshal::GetExceptionForHR(dwLastError);
}
CloseServiceHandle(hService);
CloseServiceHandle(hSCManager);
}
3. In the SetServiceTriggerStartOnUSBArrival method, add code to set the service to trigger-start
when a generic USB disk becomes available:
a. Allocate a SERVICE_TRIGGER_SPECIFIC_DATA_ITEM structure. To do this:
i. Set its dwDataType member to SERVICE_TRIGGER_DATA_TYPE_STRING.
ii. Set its cbData member to the length of the string L"USBSTOR\\GenDisk" in
bytes.
iii. Set its pData member to that string.
b. Allocate a SERVICE_TRIGGER structure. To do this:
i. Set its dwTriggerType member to
SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL.
ii. Set its dwAction member to SERVICE_TRIGGER_ACTION_SERVICE_START.
iii. Set its pTriggerSubtype member to the address of the GUID_USBDevice GUID.
iv. Set its cDataItems member to 1 and its pDataItems member to the address of
the structure allocated in the previous step.
c. Allocate a SERVICE_TRIGGER_INFO structure. To do this:
Background Services
9
i. Set set its cTriggers member to 1 and its pTriggers member to the address of the
structure allocated in the previous step.
d. Call the ChangeServiceConfig2 function with the SERVICE_CONFIG_TRIGGER_INFO
information level and pass to it the address of the structure allocated in the previous
step.
Use the following code as a complete example (shown in bold):
C++
void ServiceControl::SetServiceTriggerStartOnUSBArrival(System::String^
serviceName)
{
...
if (hService == NULL)
{
DWORD dwLastError = GetLastError();
CloseServiceHandle(hSCManager);
throw Marshal::GetExceptionForHR(dwLastError);
}
LPCWSTR lpszDeviceString = L"USBSTOR\\GenDisk";
SERVICE_TRIGGER_SPECIFIC_DATA_ITEM deviceData = {0};
deviceData.dwDataType = SERVICE_TRIGGER_DATA_TYPE_STRING;
deviceData.cbData = (wcslen(lpszDeviceString)+1) * sizeof(WCHAR);
deviceData.pData = (PBYTE)lpszDeviceString;
SERVICE_TRIGGER serviceTrigger = {0};
serviceTrigger.dwTriggerType =
SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL;
serviceTrigger.dwAction = SERVICE_TRIGGER_ACTION_SERVICE_START;
serviceTrigger.pTriggerSubtype = (GUID*)&GUID_USBDevice;
serviceTrigger.cDataItems = 1;
serviceTrigger.pDataItems = &deviceData;
SERVICE_TRIGGER_INFO serviceTriggerInfo = {0};
serviceTriggerInfo.cTriggers = 1;
serviceTriggerInfo.pTriggers = &serviceTrigger;
if (ChangeServiceConfig2(hService, SERVICE_CONFIG_TRIGGER_INFO,
&serviceTriggerInfo) == FALSE)
{
DWORD dwLastError = GetLastError();
CloseServiceHandle(hService);
CloseServiceHandle(hSCManager);
throw Marshal::GetExceptionForHR(dwLastError);
}
Background Services
10
CloseServiceHandle(hService);
CloseServiceHandle(hSCManager);
}
This completes the C++/CLI wrapper necessary for .NET interaction with the service trigger-start
APIs.
Task 2 – Add Code to Register the Service as Trigger-Start
Services can be registered as trigger-start from the sc.exe command line utility (using the sc triggerinfo
command from an elevated Command Shell), or using the ChangeServiceConfig2 API programmatically,
which we wrapped as part of the previous task.
1. In the RegisterService project, locate the RegisterServiceForm.cs (C#) or
RegisterServiceForm.vb (Visual Basic) code file and within it add code to the
btnRegisterTriggerStart_Click method to change the service configuration to trigger-start using
the C++/CLI wrapper, as follows (shown in bold):
C#
private void btnRegisterTriggerStart_Click(object sender, EventArgs e)
{
AddService();
ServiceControl.SetServiceTriggerStartOnUSBArrival(ServiceName);
StopLogReaderTimer();
StartLogReaderTimer();
}
Visual Basic
Private Sub btnRegisterTriggerStart_Click() Handles
btnRegisterTriggerStart.Click
AddService()
ServiceControl.SetServiceTriggerStartOnUSBArrival(ServiceName)
StopLogReaderTimer()
StartLogReaderTimer()
End Sub
2. In the btnRemovePrivileges_Click method, add code to change the service configuration to
request the minimum possible privileges using the C++/CLI wrapper, as follows:
C#
Background Services
11
private void btnRemovePrivileges_Click(object sender, EventArgs e)
{
ServiceControl.RemoveAllPrivilegesFromService(ServiceName);
}
Visual Basic
Private Sub btnRemovePrivileges_Click() Handles btnRemovePrivileges.Click
ServiceControl.RemoveAllPrivilegesFromService(ServiceName)
End Sub
You have now completed the changes to the service registration UI application so that the
service can be registered as a trigger-start service with the minimum amount of necessary
privileges.
Background Services
12
Task 3 – Enable the UsbCopyService to Trigger-Start
Unlike auto-start services which typically poll for interesting events using periodic timers, trigger-start
services act when a trigger starts them and then deactivate (stop) themselves when there is no more
work to perform.
1. In the UsbCopyService project, locate and open the USBService.cs (C#) or USBService.vb (Visual
Basic) code file.
2. (For Visual Basic users only) Add the ProcessTriggerStart method, which will be called when the
service is configured to trigger-start.
Visual Basic
Private Sub ProcessTriggerStart()
DoWork()
End Sub
3. Modify the OnStart method to check whether the service is configured to trigger-start using the
ServiceControl.IsServiceTriggerStart method (pass the ServiceName property inherited from
the ServiceBase class as the parameter). To do this, replace the method implementation with
the following code:
C#
protected override void OnStart(string[] args)
{
if (ServiceControl.IsServiceTriggerStart(ServiceName))
{
}
else
{
_timer = new Timer(delegate
{
DoWork();
});
_timer.Change(0, 5000);
}
}
Visual Basic
Protected Overrides Sub OnStart(ByVal args() As String)
If ServiceControl.IsServiceTriggerStart(ServiceName) Then
Else
_timer = New Timer(AddressOf ProcessTimerEvent)
_timer.Change(0, 5000)
End If
Background Services
13
End Sub
4. Now if the service is configured to trigger-start, instead of creating the periodic timer the
method will enqueue the work to be performed only once. To do this, add the following code
(shown in bold):
C#
protected override void OnStart(string[] args)
{
if (ServiceControl.IsServiceTriggerStart(ServiceName))
{
ThreadPool.QueueUserWorkItem(delegate
{
DoWork();
});
}
else
{
_timer = new Timer(delegate
{
DoWork();
});
_timer.Change(0, 5000);
}
}
Visual Basic
Protected Overrides Sub OnStart(ByVal args() As String)
If ServiceControl.IsServiceTriggerStart(ServiceName) Then
ThreadPool.QueueUserWorkItem(AddressOf ProcessTriggerStart)
Else
_timer = New Timer(AddressOf ProcessTimerEvent)
_timer.Change(0, 5000)
End If
End Sub
5. After completing the work, the service should stop using the Stop method inherited from the
ServiceBase class. To do this, add the following code (shown in bold) to the OnStart method
(C#) or to the ProcessTriggerStart method (Visual Basic):
C#
protected override void OnStart(string[] args)
{
if (ServiceControl.IsServiceTriggerStart(ServiceName))
{
Background Services
14
ThreadPool.QueueUserWorkItem(delegate
{
DoWork();
Stop();
});
}
else
{
...
}
}
Visual Basic
Private Sub ProcessTriggerStart()
DoWork()
Me.Stop()
End Sub
6. You are now ready to test the service. Run the RegisterService project.
Note: The project should be run as administrator. You can do this either from the elevated
Visual Studio instance or by running the executable elevated from the shell or command
prompt.
7. Register the service as demand-start by clicking the Register Demand-Start button and start it
by clicking the Start button. It will continuously poll for a USB device every 5 seconds. When a
USB device with the ToCopy directory is detected, it will copy that directory’s contents to the
C:\FromUSB directory.
Figure 3
Registering the service as demand-start
Background Services
15
8. Inspect the service’s access token by launching Sysinternals Process Explorer. Right-click on the
UsbCopyService.exe process in the process tree, select Properties and switch to the Security
tab. Under the service’s privilege list, many privileges are either enabled or just present
(disabled).
Figure 4
Inspecting service privileges
Note: Find the download link for Sysinternals Process Explorer (procexp.exe) in the
Prerequisites section of this hands-on lab.
9. Stop the service using the Stop button.
Background Services
16
10. Remove privileges from the service by clicking the Remove Privileges button.
11. Start the service by clicking the Start button and inspect its access token again using
Sysinternals Process Explorer. The service’s privilege list should now contain only the
SeChangeNotifyPrivilege (which is always retained for application compatibility purposes).
Figure 5
Service privileges (after removing privileges)
12. Stop the service using the Stop button and delete it using the Delete button.
13. Remove the USB Device from the computer.
14. Register the service as trigger-start by clicking the Register Trigger-Start button. Do not start
the service manually.
Background Services
17
15. Inspect the service’s trigger-start configuration by running the following command from an
elevated command prompt.
Command
sc qtriggerinfo UsbCopyService
Figure 6
Inspecting service's trigger-start configuration
16. Insert a USB device with a ToCopy directory. The service should automatically start, copy the
files and then shut itself down.
17. You can verify that the service is down by running the following command from an elevated
command prompt.
Command
sc queryex UsbCopyService
Figure 7
Verifying service is down
Background Services
18
18. When done, delete the service by clicking the Delete button.
19. You can ensure that the service has been deleted by running the same previous command from
an elevated command prompt.
Command
sc queryex UsbCopyService
Figure 8
Verifying service has been deleted
In this exercise, you have modified an existing service to trigger-start when a USB device is detected in
the system. You have also modified the service’s requested privilege list to the minimum necessary
amount of privileges, thus reducing the system’s attack surface. The full exercise solution can be found
in the %TrainingKitInstallDir%\BackgroundServices\Ex1-TriggerStartService\End directory, depending
on the language of your preference.
Summary
In this lab you have improved the performance and security of a Windows service to become a better
citizen of the operating system. You used the trigger-start service mechanism to launch the service only
when there is actual work for it to perform, and you have minimized the service’s attack surface by
removing unnecessary privileges from its process’ token.
You can find a full demo of the USB copy service and the Weather Updater service as part of this
Windows 7 course’s code samples.