随时可以看到任务管理器中有一个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.exeWinMain函数

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;

}

打赏