[16]Windows内核情景分析 --- 服务管理
随时可以看到任务管理器中有一个services.exe进程,这个就是系统的服务控制管理进程,简称SCM
这个进程专门用来管理服务(启动、停止、删除、配置等操作)
系统中所有注册的服务都登记在\HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services键下,这个键专门叫做‘服务键’,服务键下面的每个子键代表一个服务,记录了各个服务的信息。
每个服务可以是独立的服务,也可以位于某个服务组内。用户不仅可以注册服务,还可以注册服务组,并指定服务与服务组之间的隶属关系。\HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\ServiceGroupOrder键下有一个值List,类型是多字符串,记录了系统中注册的所有服务组(蕴含了他们之间的相对加载顺序)。系统加载启动服务时,首先是以服务组为加载顺序进行加载的,各个服务组的加载先后顺序不同,然后同一服务组内的各个服务也是有加载顺序的。
下面是各种启动类型:
#define SERVICE_BOOT_START 0 //系统引导时启动
#define SERVICE_SYSTEM_START 1 //系统初始哈时启动
#define SERVICE_AUTO_START 2 //AutoStart启动类型,即系统初始化完毕后,由SCM开机时自动启动
#define SERVICE_DEMAND_START 3 //按需启动
#define SERVICE_DISABLED 4 //禁用
系统在初始化完毕后,启动services.exe时,会检查注册表中那些登记为AutoStart启动类型的服务,一一予以启动。
我们看services.exe的WinMain函数
int WINAPI
wWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPWSTR lpCmdLine,int nShowCmd)
{
HANDLE hScmStartEvent;
HANDLE hEvent;
DWORD dwError;
if (!ScmCreateStartEvent(&hScmStartEvent))
ExitThread(0);
//读取系统中注册的所有服务组和服务,记录到各自的全局链表中
dwError = ScmCreateServiceDatabase();
if (dwError != ERROR_SUCCESS)
ExitThread(0);
ScmGetBootAndSystemDriverState();//标记出全局链表中那些已经启动了的驱动类型服务
ScmStartRpcServer();
RegisterServicesProcess(GetCurrentProcessId());
SetEvent(hScmStartEvent);
SetConsoleCtrlHandler(ShutdownHandlerRoutine, TRUE);
ScmWaitForLsass();
AcquireLoadDriverPrivilege();
ScmAutoStartServices();//关键。启动所有那些注册为AutoStart启动类型的服务
hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (hEvent)
WaitForSingleObject(hEvent, INFINITE);//services.exe进程永远不会结束
CloseHandle(hScmStartEvent);
ExitThread(0);
return 0;
}
DWORD ScmCreateServiceDatabase(VOID)
{
WCHAR szSubKey[MAX_PATH];
HKEY hServicesKey;
HKEY hServiceKey;
DWORD dwSubKey;
DWORD dwSubKeyLength;
FILETIME ftLastChanged;
DWORD dwError;
dwError = ScmCreateGroupList();//读取注册表中所有注册的服务组,加入全局服务组链表
if (dwError != ERROR_SUCCESS)
return dwError;
InitializeListHead(&ServiceListHead);
RtlInitializeResource(&DatabaseLock);
dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,L"System\\CurrentControlSet\\Services",
0,KEY_READ,&hServicesKey);
if (dwError != ERROR_SUCCESS)
return dwError;
dwSubKey = 0;
for (;;)
{
dwSubKeyLength = MAX_PATH;
//枚举系统中注册的所有服务(按注册顺序)
dwError = RegEnumKeyExW(hServicesKey,dwSubKey,szSubKey,&dwSubKeyLength,NULL,NULL,
NULL,&ftLastChanged);
if (dwError == ERROR_SUCCESS && szSubKey[0] != L'{') //跳过com服务
{
dwError = RegOpenKeyExW(hServicesKey,szSubKey,0,KEY_READ,&hServiceKey);
if (dwError == ERROR_SUCCESS)
{
dwError = CreateServiceListEntry(szSubKey,hServiceKey);//加入全局服务链表
RegCloseKey(hServiceKey);
}
}
if (dwError != ERROR_SUCCESS)
break;
dwSubKey++;
}
RegCloseKey(hServicesKey);
WaitForLSA();
ScmDeleteMarkedServices();//删除那些标记为‘已删除’的服务
return ERROR_SUCCESS;
}
DWORD ScmCreateGroupList(VOID)
{
RTL_QUERY_REGISTRY_TABLE QueryTable[2];
NTSTATUS Status;
InitializeListHead(&GroupListHead);
InitializeListHead(&UnknownGroupListHead);
RtlZeroMemory(&QueryTable,sizeof(QueryTable));
QueryTable[0].Name = L"List";
QueryTable[0].QueryRoutine = CreateGroupListRoutine;
//查询ServiceGroupOrder键中的List值,对list中的每个组执行CreateGroupListRoutine函数
Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,L"ServiceGroupOrder",//这个键
QueryTable,NULL,NULL);
return RtlNtStatusToDosError(Status);
}
NTSTATUS WINAPI
CreateGroupListRoutine(PWSTR ValueName,//值名,即list
ULONG ValueType,
PVOID ValueData,//即list中的每个组名
ULONG ValueLength,
PVOID Context,
PVOID EntryContext)
{
PSERVICE_GROUP Group;
RTL_QUERY_REGISTRY_TABLE QueryTable[2];
NTSTATUS Status;
if (ValueType == REG_SZ)
{
//分配一个服务组描述符
Group = (PSERVICE_GROUP)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
sizeof(SERVICE_GROUP) + ((wcslen((const wchar_t*) ValueData) + 1) * sizeof(WCHAR)));
wcscpy(Group->szGroupName, (const wchar_t*) ValueData);
Group->lpGroupName = Group->szGroupName;
Group->dwRefCount = (DWORD)-1;
RtlZeroMemory(&QueryTable, sizeof(QueryTable));
QueryTable[0].Name = (PWSTR)ValueData;//组名
QueryTable[0].QueryRoutine = CreateGroupOrderListRoutine;
//查询GroupOrderList键中对应组名的信息,执行CreateGroupOrderListRoutine函数
Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,L"GroupOrderList",QueryTable,
(PVOID)Group,NULL);//返回组描述符
InsertTailList(&GroupListHead,&Group->GroupListEntry);//将组描述符挂入全局服务组链表
}
return STATUS_SUCCESS;
}
NTSTATUS
CreateGroupOrderListRoutine(PWSTR ValueName,//即组名
ULONG ValueType,
PVOID ValueData,//值的数据,即tag总数|tag|tag…|tag格式
ULONG ValueLength,
PVOID Context,//返回该组包含的服务tag个数和各个服务的tag
PVOID EntryContext)
{
PSERVICE_GROUP Group;
if (ValueType == REG_BINARY &&
ValueData != NULL &&
ValueLength >= sizeof(DWORD) &&
ValueLength >= (*(PULONG)ValueData + 1) * sizeof(DWORD))
{
Group = (PSERVICE_GROUP)Context;
Group->TagCount = ((PULONG)ValueData)[0];
if (Group->TagCount > 0)
{
if (ValueLength >= (Group->TagCount + 1) * sizeof(DWORD))//多此一举
{
//即该组中每个服务的tag
Group->TagArray = (PULONG)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
Group->TagCount * sizeof(DWORD));
RtlCopyMemory(Group->TagArray, ValueData + 1,Group->TagCount * sizeof(DWORD));
}
else
{
Group->TagCount = 0;
return STATUS_UNSUCCESSFUL;
}
}
}
return STATUS_SUCCESS;
}
//获取指定服务的信息,构造一个服务描述符,将服务加入全局服务链表中
DWORD CreateServiceListEntry(LPCWSTR lpServiceName,HKEY hServiceKey)
{
PSERVICE lpService = NULL;
LPWSTR lpDisplayName = NULL;
LPWSTR lpGroup = NULL;
DWORD dwSize;
DWORD dwError;
DWORD dwServiceType;
DWORD dwStartType;
DWORD dwErrorControl;
DWORD dwTagId;
if (*lpServiceName == L'{')
return ERROR_SUCCESS;
dwSize = sizeof(DWORD);
dwError = RegQueryValueExW(hServiceKey,L"Type",NULL,NULL, (LPBYTE)&dwServiceType,
&dwSize);
if (dwError != ERROR_SUCCESS)
return ERROR_SUCCESS;
//没有进程的服务不必加入全局链表(但驱动型服务除外)
if (((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_OWN_PROCESS) &&
((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_SHARE_PROCESS) &&
(dwServiceType != SERVICE_KERNEL_DRIVER) &&
(dwServiceType != SERVICE_FILE_SYSTEM_DRIVER))
return ERROR_SUCCESS;
dwSize = sizeof(DWORD);
dwError = RegQueryValueExW(hServiceKey,L"Start",NULL,NULL, (LPBYTE)&dwStartType,&dwSize);
if (dwError != ERROR_SUCCESS)
return ERROR_SUCCESS;
dwSize = sizeof(DWORD);
dwError = RegQueryValueExW(hServiceKey,L"ErrorControl",NULL,NULL,
(LPBYTE)&dwErrorControl,&dwSize);
if (dwError != ERROR_SUCCESS)
return ERROR_SUCCESS;
//查询该服务的tag id
dwError = RegQueryValueExW(hServiceKey,L"Tag",NULL,NULL, (LPBYTE)&dwTagId,&dwSize);
if (dwError != ERROR_SUCCESS)
dwTagId = 0;
//查询该服务所属的组
dwError = ScmReadString(hServiceKey,L"Group",&lpGroup);
if (dwError != ERROR_SUCCESS)
lpGroup = NULL;
dwError = ScmReadString(hServiceKey,L"DisplayName",&lpDisplayName);
if (dwError != ERROR_SUCCESS)
lpDisplayName = NULL;
//创建一个服务描述符
dwError = ScmCreateNewServiceRecord(lpServiceName, &lpService);
if (dwError != ERROR_SUCCESS)
goto done;
lpService->Status.dwServiceType = dwServiceType;
lpService->dwStartType = dwStartType;
lpService->dwErrorControl = dwErrorControl;
lpService->dwTag = dwTagId;
if (lpGroup != NULL)
{
dwError = ScmSetServiceGroup(lpService, lpGroup);//记录它所属的服务组
if (dwError != ERROR_SUCCESS)
goto done;
}
if (lpDisplayName != NULL)
{
lpService->lpDisplayName = lpDisplayName;
lpDisplayName = NULL;
}
if (ScmIsDeleteFlagSet(hServiceKey))
lpService->bDeleted = TRUE;
done:;
if (lpGroup != NULL)
HeapFree(GetProcessHeap(), 0, lpGroup);
if (lpDisplayName != NULL)
HeapFree(GetProcessHeap(), 0, lpDisplayName);
return dwError;
}
当构造好了服务组链表和服务链表后,下面的函数用于标记出那些已经启动了的驱动服务
VOID ScmGetBootAndSystemDriverState(VOID)
{
PLIST_ENTRY ServiceEntry;
PSERVICE CurrentService;
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
if (CurrentService->dwStartType == SERVICE_BOOT_START ||
CurrentService->dwStartType == SERVICE_SYSTEM_START)
{
ScmCheckDriver(CurrentService);//检查这两种早期启动型服务
}
ServiceEntry = ServiceEntry->Flink;
}
}
NTSTATUS ScmCheckDriver(PSERVICE Service) //标记成所有已经启动了的驱动型服务
{
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING DirName;
HANDLE DirHandle;
NTSTATUS Status;
POBJECT_DIRECTORY_INFORMATION DirInfo;
ULONG BufferLength;
ULONG DataLength;
ULONG Index;
if (Service->Status.dwServiceType == SERVICE_KERNEL_DRIVER)
RtlInitUnicodeString(&DirName,L"\\Driver");
else
RtlInitUnicodeString(&DirName,L"\\FileSystem");
InitializeObjectAttributes(&ObjectAttributes,&DirName,0,NULL,NULL);
Status = NtOpenDirectoryObject(&DirHandle, DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
&ObjectAttributes);
if (!NT_SUCCESS(Status))
{
return Status;
}
BufferLength = sizeof(OBJECT_DIRECTORY_INFORMATION) + 2 * MAX_PATH * sizeof(WCHAR);
DirInfo = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,BufferLength);
Index = 0;
while (TRUE)
{
Status = NtQueryDirectoryObject(DirHandle,DirInfo,BufferLength,TRUE,FALSE,&Index,
&DataLength);
if (Status == STATUS_NO_MORE_ENTRIES)
break;
if (!NT_SUCCESS(Status))
break;
//if 该服务的驱动对象已在对象目录中,也即if该服务已经启动
if (_wcsicmp(Service->lpServiceName, DirInfo->Name.Buffer) == 0)
{
Service->Status.dwCurrentState = SERVICE_RUNNING;//标记
if (Service->lpGroup != NULL)
Service->lpGroup->ServicesRunning = TRUE;//标记那个组有服务启动了
break;
}
}
HeapFree(GetProcessHeap(),0,DirInfo);
NtClose(DirHandle);
return STATUS_SUCCESS;
}
下面是最关键的了:我们看下所有AutoStart启动类型的服务是按什么顺序启动的
VOID ScmAutoStartServices(VOID) //启动所有AutoStart启动类型的服务
{
PLIST_ENTRY GroupEntry;
PLIST_ENTRY ServiceEntry;
PSERVICE_GROUP CurrentGroup;
PSERVICE CurrentService;
ULONG i;
ServiceEntry = ServiceListHead.Flink;
//先全部标记为‘未启动’
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
CurrentService->ServiceVisited = FALSE;
ServiceEntry = ServiceEntry->Flink;
}
// 1、先按服务组之间的相对顺序,启动所有服务组中的服务
GroupEntry = GroupListHead.Flink;
while (GroupEntry != &GroupListHead)
{
CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
for (i = 0; i < CurrentGroup->TagCount; i++)
{
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
if ((CurrentService->lpGroup == CurrentGroup) &&
(CurrentService->dwStartType == SERVICE_AUTO_START) &&
(CurrentService->ServiceVisited == FALSE) &&
(CurrentService->dwTag == CurrentGroup->TagArray[i]))
{
CurrentService->ServiceVisited = TRUE;
ScmStartService(CurrentService, 0, NULL);
}
ServiceEntry = ServiceEntry->Flink;
}
}
// 2、启动处于服务组中,但没有tag或者tag无效的那些服务
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
if ((CurrentService->lpGroup == CurrentGroup) &&
(CurrentService->dwStartType == SERVICE_AUTO_START) &&
(CurrentService->ServiceVisited == FALSE))
{
CurrentService->ServiceVisited = TRUE;
ScmStartService(CurrentService, 0, NULL);
}
ServiceEntry = ServiceEntry->Flink;
}
GroupEntry = GroupEntry->Flink;
}
//3、启动那些有服务组,但服务组本身不存在的服务
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
if ((CurrentService->lpGroup != NULL) &&
(CurrentService->dwStartType == SERVICE_AUTO_START) &&
(CurrentService->ServiceVisited == FALSE))
{
CurrentService->ServiceVisited = TRUE;
ScmStartService(CurrentService, 0, NULL);
}
ServiceEntry = ServiceEntry->Flink;
}
// 4、启动所有独立的服务(不在任何服务组中)
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
if ((CurrentService->lpGroup == NULL) &&
(CurrentService->dwStartType == SERVICE_AUTO_START) &&
(CurrentService->ServiceVisited == FALSE))
{
CurrentService->ServiceVisited = TRUE;
ScmStartService(CurrentService, 0, NULL);
}
ServiceEntry = ServiceEntry->Flink;
}
//完毕
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
CurrentService->ServiceVisited = FALSE;
ServiceEntry = ServiceEntry->Flink;
}
}
可以看出,各个服务组之间相对的加载顺序、组内服务之间的相对加载顺序都记录在注册表。
有组的服务优先比没组的服务优先启动,没组的那些服务,则按注册表中的登记的顺序启动。
DWORD ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv)
{
PSERVICE_GROUP Group = Service->lpGroup;
DWORD dwError = ERROR_SUCCESS;
Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
if (Service->Status.dwServiceType & SERVICE_DRIVER)//驱动类型服务
{
dwError = ScmLoadDriver(Service);//用这个函数加载驱动,启动服务
if (dwError == ERROR_SUCCESS)
{
Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
Service->Status.dwCurrentState = SERVICE_RUNNING;
}
}
Else 。。。
return dwError;
}
DWORD ScmLoadDriver(PSERVICE lpService)
{
WCHAR szDriverPath[MAX_PATH];
UNICODE_STRING DriverPath;
NTSTATUS Status;
DWORD dwError = ERROR_SUCCESS;
wcscpy(szDriverPath,L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
wcscat(szDriverPath, lpService->lpServiceName);
RtlInitUnicodeString(&DriverPath, szDriverPath);
//看到没。通过SCM启动的服务,都使用这个函数加载驱动,而这个函数我们前面看过,只能加载NT式驱动,不能加载PNP驱动,或者即使是个PNP驱动,通过这个函数加载的,也会变成老式的NT式驱动,不支持即插即用。
Status = NtLoadDriver(&DriverPath);
if (!NT_SUCCESS(Status))
dwError = RtlNtStatusToDosError(Status);
return dwError;
}