Windows Server 2008 R2 Developer Readiness Background Services in Windows Server 2008 R2

Windows Server 2008 R2 Developer Lab Series

Background Services In Windows Server 2008 R2

Lab version: 1.0.0

Last updated: 10/30/2009

Contents

Introduction 3

Exercise 1: Trigger-Start Service 4

Part 1: Implement Service Configuration Changes 5

Part 2: Add Code to Register the Service as Trigger-Start 7

Part 3: Enable the UsbCopyService to Trigger-Start 8

Summary 10

Introduction

Windows 7 and Windows Server 2008 R2 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.

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.

Note: Please note that in order for you to test this exercise, you must have USB support. Since these labs are carried out on Virtual Machines running on Hyper-V, you will not have USB support.

If you want to test the finished application, copy it over to your host machine and run it from there.

Part 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 Ex1_Starter solution located in the following directory:

C:\Server 2008 R2 Labs\Background Services in Windows Server 2008 R2\Ex1_Starter\Ex1_Starter.sln

2.  In the ServiceControlInterop project, locate the ServiceControlInterop.cpp file and add code in the RemoveAllPrivilegesFromService method to specify that the service requires no privileges.

3.  Allocate a SERVICE_REQUIRED_PRIVILEGES_INFO structure and set its pmszRequiredPrivileges member to SE_CHANGE_NOTIFY_NAME L"\0".

4.  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. Complete example:

C++

//Here, specifying \0\0 doesn't work so we're using the SeChangeNotifyPrivilege

//which is enabled (for compat reasons) anyway even if privileges are removed.

//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);

}

5.  In the SetServiceTriggerStartOnUSBArrival method, add code to set the service to trigger-start when a generic USB disk becomes available.

6.  Allocate a SERVICE_TRIGGER_SPECIFIC_DATA_ITEM structure,

a.  Set its dwDataType member to SERVICE_TRIGGER_DATA_TYPE_STRING

b.  Set its cbData member to the length of the string L"USBSTOR\\GenDisk" in bytes

c.  Set its pData member to that string.

7.  Allocate a SERVICE_TRIGGER structure,

a.  set its dwTriggerType member to SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL

b.  Set its dwAction member to SERVICE_TRIGGER_ACTION_SERVICE_START

c.  Set its pTriggerSubtype member to the address of the GUID_USBDevice GUID

d.  Set its cDataItems member to 1 and its pDataItems member to the address of the structure allocated in the previous step.

8.  Allocate a SERVICE_TRIGGER_INFO structure

9.  Set set its cTriggers member to 1 and its pTriggers member to the address of the structure allocated in the previous step.

10.  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. Complete example:

C#

LPCWSTR lpszDeviceString = L"USBSTOR\\GenDisk";

SERVICE_TRIGGER_SPECIFIC_DATA_ITEM deviceData = {0};

deviceData.dwDataType = SERVICE_TRIGGER_DATA_TYPE_STRING;

deviceData.cbData = (DWORD)((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);

}

Note: This completes the C++/CLI wrapper necessary for .NET interaction with the service trigger-start APIs.

Part 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 and need to run the Command Shell as Administrator), or using the ChangeServiceConfig2 API programmatically, which we wrapped as part of the previous task.

1.  In the RegisterService project, locate the RegisterServiceForm.cs 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:

C#

ServiceControl.SetServiceTriggerStartOnUSBArrival(ServiceName);

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#

ServiceControl.RemoveAllPrivilegesFromService(ServiceName);

Note: 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.

Part 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 the USBService.cs code file and modify the OnStart method so that 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.

2.  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).

3.  After completing the work, the service should stop using the Stop method inherited from the ServiceBase class. Complete example:

C#

//if this is a trigger start then

//use other thread to do the actual work

if (ServiceControl.IsServiceTriggerStart(ServiceName))

{

ThreadPool.QueueUserWorkItem(delegate

{

DoWork();

Stop();

});

}

else

{

_timer = new Timer(delegate

{

DoWork();

});

_timer.Change(0, 5000);

}

4.  You are now ready to test the service. Run the RegisterService project as administrator (either from an elevated Visual Studio instance or by running it elevated from the shell or command prompt).

5.  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.

6.  Inspect the service’s access token by launching Sysinternals Process Explorer, right-clicking on the UsbCopyService.exe process in the process tree and switching to the Security tab. Under the service’s privilege list, many privileges are either enabled or just present (disabled).

7.  Stop the service using the Stop button.

8.  Remove privileges from the service by clicking the Remove Privileges button.

9.  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).

10.  Stop the service using the Stop button and delete it using the Delete button.

11.  Register the service as trigger-start by clicking the Register Trigger-Start button. Do not start the service manually.

12.  Inspect the service’s trigger-start configuration by running the sc qtriggerinfo UsbCopyService command from an elevated command prompt.

13.  Insert a USB device with a ToCopy directory. The service should automatically start, copy the files and then shut itself down. (You can verify that the service is down by running the sc queryex UsbCopyService command from an elevated command prompt.)

14.  When done, delete the service by clicking the Delete button. (Ensure that the service has been deleted by running the sc queryex UsbCopyService command from an elevated command prompt.)

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 Ex1_Solution directory under the HOL root directory.

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.

Page 5 of 10