[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;

}

 

posted @ 2018-12-18 02:06  jadeshu  阅读(520)  评论(0编辑  收藏  举报