windows 服务 SCM学习笔记

本文介绍了Windows服务SCM(Service Control Manager)的学习笔记,包括服务的注册、启动、停止和注销等操作,强调了必须先获取SCM句柄的重要性。文章通过示例代码讲解了服务管理,并提到了枚举服务时的缓冲区大小问题。此外,还讨论了如何通过注册表查看和管理服务,并更新了CreateService函数对注册表的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

summary one

windows services 的操作核心包括:
1. 注册服务 createService
2. 启动服务 startService
3. 停止服务 ControlService(...,SERVICE_CONTROL_STOP,....)
4. 注销服务 deleteService

只有注册了服务,系统中才能够查看到服务。注册后的服务,无论是否停止、运行、暂停,都会在services中显示。也就是说,关闭服务,需要注销服务。

平时用到的services.msc查看到的都是SERVICE_WIN32 类型的服务,看不到SERVICE_DRIVER 驱动型的服务。

枚举服务不能显示全部服务,必须在win32服务和驱动服务中做出单一选择。

ControlService函数不包括所有的控制,比如没有宏SERVICE_CONTROL_START 用来启动服务

对服务的操作(枚举,创建,停止,启动)都必须先获得SC控制管理器的句柄,删除服务操作是唯一例外。

注意的基本点

shown in WinNT.h
win32型服务宏定义
#define SERVICE_WIN32                  (SERVICE_WIN32_OWN_PROCESS | \
                                        SERVICE_WIN32_SHARE_PROCESS)
驱动型服务宏定义
#define SERVICE_DRIVER                 (SERVICE_KERNEL_DRIVER | \
                                        SERVICE_FILE_SYSTEM_DRIVER | \
                                        SERVICE_RECOGNIZER_DRIVER)

枚举服务时用到的数据结构 shown in WinSvc.h
//
// Service Status Structures
//
typedef struct _SERVICE_STATUS {
    DWORD   dwServiceType;   ****
    DWORD   dwCurrentState;  ****
    DWORD   dwControlsAccepted;
    DWORD   dwWin32ExitCode;
    DWORD   dwServiceSpecificExitCode;
    DWORD   dwCheckPoint;
    DWORD   dwWaitHint;
} SERVICE_STATUS, *LPSERVICE_STATUS;

//
// Service Status Enumeration Structure
//
// ENUM_SERVICE_STATUS
typedef struct _ENUM_SERVICE_STATUSW {
    LPWSTR            lpServiceName; ***
    LPWSTR            lpDisplayName; ***
    SERVICE_STATUS    ServiceStatus; ***
} ENUM_SERVICE_STATUSW, *LPENUM_SERVICE_STATUSW;
#ifdef UNICODE
typedef ENUM_SERVICE_STATUSW ENUM_SERVICE_STATUS;

常见错误(通过GetLastError()获得) shown in WinError.h
//
// MessageId: ERROR_INSUFFICIENT_BUFFER
//
// MessageText:
//
// The data area passed to a system call is too small.
//
#define ERROR_INSUFFICIENT_BUFFER        122L    // dderror

//
// MessageId: ERROR_SERVICE_EXISTS
//
// MessageText:
//
// The specified service already exists.
//
#define ERROR_SERVICE_EXISTS             1073L

demo

VS2008sp1 ON XPx86 SP3
Windows Console Application
Debug Beta

// EnumService.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <windows.h>

/*
 *  type: SERVICE_WIN32   xor SERVICE_DRIVER  ONLY ONE.
 */
VOID EnumService(DWORD type)
{
    SC_HANDLE hscm = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
    if (hscm == NULL)
    {
        printf("OpenSC Manager error! \n");
        exit(1);
    }
    //DWORD dwBufSize =68;
    DWORD dwBufSize =sizeof(ENUM_SERVICE_STATUS)*512;
    DWORD dwByteNeeded,dwServiceReturned,lpResumeHandle= NULL;
//  ENUM_SERVICE_STATUS SerStatus[6] ={0};
    ENUM_SERVICE_STATUS SerStatus[512] ={0};


    BOOL bRet = EnumServicesStatus(hscm,type,SERVICE_STATE_ALL,SerStatus,\
                                dwBufSize,&dwByteNeeded,&dwServiceReturned,&lpResumeHandle);
    if (FALSE == bRet)
    {
        printf("EnumService error!\n");
        printf("Find Serices count:%d\t",dwServiceReturned);
        printf("Need Buffer_length: %d\n",dwByteNeeded/sizeof(ENUM_SERVICE_STATUS));
    }
    //ERROR_INSUFFICIENT_BUFFER

    for (DWORD i = 0 ; i <dwServiceReturned ; i++)
    {

        if (wcscmp(SerStatus[i].lpServiceName,L"helloworld"))
        {
            continue;
        }
        printf("%i ",i);
        printf("%S  ",SerStatus[i].lpServiceName);
        printf("%S",SerStatus[i].lpDisplayName);
        switch (SerStatus[i].ServiceStatus.dwCurrentState)
        {
        case SERVICE_PAUSED:
            printf(" paused!\n");
            break;
        case SERVICE_STOPPED:
            printf(" stopped!\n");
            break;
        case SERVICE_RUNNING:
            printf(" running!\n");
            break;
        default:
            printf("Status: %08x",SerStatus[i].ServiceStatus.dwCurrentState);
        }
    }
    CloseServiceHandle(hscm);
}

BOOL StopService(LPWCH stop_name)
{
    SERVICE_STATUS ServiceStatus;
    SC_HANDLE hSCM = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
    if (hSCM == NULL)
    {
        printf("Open SCManager Error!");
        exit(1);
    }
    SC_HANDLE hSCService = OpenService(hSCM,stop_name,SERVICE_ALL_ACCESS);
    BOOL bRet = ControlService(hSCService,SERVICE_CONTROL_STOP,&ServiceStatus);
    CloseServiceHandle(hSCService);
    CloseServiceHandle(hSCM);
    if (bRet ==TRUE)
    {
        return TRUE;
    }
    return FALSE;


}

BOOL myStartService(LPWCH start_name)
{
    SERVICE_STATUS ServiceStatus;
    SC_HANDLE hSCM = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
    if (hSCM == NULL)
    {
        printf("Open SCManager Error!");
        exit(1);
    }
    SC_HANDLE hSCService = OpenService(hSCM,start_name,SERVICE_ALL_ACCESS);
    BOOL bRet = StartService(hSCService,NULL,NULL);
    CloseServiceHandle(hSCM);
    CloseServiceHandle(hSCService);
    if (bRet == TRUE)
    {
        return TRUE;
    }

    return FALSE;

}




SC_HANDLE RegisterService()
{
// FROM MSDN
//  SC_HANDLE WINAPI CreateService(
//      _In_      SC_HANDLE hSCManager,        //SHOULD OpenSCManager already.
//      _In_      LPCTSTR   lpServiceName,
//      _In_opt_  LPCTSTR   lpDisplayName,
//      _In_      DWORD     dwDesiredAccess,
//      _In_      DWORD     dwServiceType,     //kernel? or win32?
//      _In_      DWORD     dwStartType,       // how to start
//      _In_      DWORD     dwErrorControl,
//      _In_opt_  LPCTSTR   lpBinaryPathName,  ****//可作为恶意代码检测点,即服务文件的位置
//      _In_opt_  LPCTSTR   lpLoadOrderGroup,
//      _Out_opt_ LPDWORD   lpdwTagId,
//      _In_opt_  LPCTSTR   lpDependencies,
//      _In_opt_  LPCTSTR   lpServiceStartName,
//      _In_opt_  LPCTSTR   lpPassword
//      );
    SERVICE_STATUS ServiceStatus;
    SC_HANDLE hSCM = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
    if (hSCM == NULL)
    {
        printf("Open SCManager Error!");
        exit(1);
    }
    LPCTSTR lpServiceName = L"helloworld";
    LPCTSTR lpDisplayName = L"bingo";
    //DWORD dwDesiredAccess = SC_MANAGER_ALL_ACCESS ;
    DWORD dwDesiredAccess = SERVICE_ALL_ACCESS ;

    DWORD dwServiceType = SERVICE_KERNEL_DRIVER;
    DWORD dwStartType = SERVICE_DEMAND_START;
    DWORD dwErrorControl =SERVICE_ERROR_NORMAL;
    LPCTSTR lpBinaryPathName = L"C:\\drivers\\objchk_wxp_x86\\i386\\helloworld.sys";
    LPCTSTR lpLoadOrderGroup = NULL;
    LPDWORD lpdwTagId       =NULL;
    LPCTSTR   lpDependencies = NULL;
    LPCTSTR   lpServiceStartName =NULL;
    LPCTSTR   lpPassword        = NULL;
    SC_HANDLE bRet = CreateService(hSCM,lpServiceName,lpDisplayName,dwDesiredAccess,\
                dwServiceType,dwStartType,\
                dwErrorControl,lpBinaryPathName,lpLoadOrderGroup,lpdwTagId,\
                lpDependencies,lpServiceStartName,lpPassword);
    CloseServiceHandle(hSCM);
    if (bRet!=NULL)
    {
        return bRet;
    }
    printf("%d",GetLastError());

        return NULL;
}
BOOL unRegisterService(SC_HANDLE handle)
{
    int Bret= DeleteService(handle);
    printf("result:%d\n",Bret);
    return TRUE;
}

int _tmain(int argc, _TCHAR* argv[])
{
    //EnumService(SERVICE_KERNEL_DRIVER);
    LPWCH stop_name=L"helloworld";
    //StopService(stop_name);
    //EnumService(SERVICE_KERNEL_DRIVER);
    //myStartService(stop_name);
    //EnumService(SERVICE_KERNEL_DRIVER);
    //RegisterService();
    printf("EnumServices:\n");
    EnumService(SERVICE_KERNEL_DRIVER);

    printf("after createsevice:\n");

    SC_HANDLE create_handle=RegisterService();
    EnumService(SERVICE_KERNEL_DRIVER);

    myStartService(stop_name);
    printf("after startservice:\n");
    EnumService(SERVICE_KERNEL_DRIVER);

    StopService(stop_name);
    printf("after stopservice:\n");
    EnumService(SERVICE_KERNEL_DRIVER);

    printf("after deleteservice:\n");
    unRegisterService(create_handle);
    CloseServiceHandle(create_handle); //if delete,what happend?
    EnumService(SERVICE_KERNEL_DRIVER);
    return 0;
}

输出

C:\VC6\MyProjects\EnumService\Debug>EnumService.exe
EnumServices:     **未注册
after createsevice:  **注册,还未启动
172 helloworld  bingo stopped!
after startservice:  **启动
172 helloworld  bingo running!
after stopservice:   **停止
172 helloworld  bingo stopped!
after deleteservice: **注销
result:1      **NONULL表示成功注销,此时SCManager已经无法得到该服务

summary two

枚举服务:1.OpenSCManager,获得服务管理器句柄,2.EnumServiceStatus
注册服务:1.OpenSCManager,2.CreateService。注册服务是最麻烦的,如同CreateProcess一般,因为要注册的消息参数非常多。
启动服务:1.OpenSCManager,2,OpenService(service_name),获得服务句柄。3,Startservice (该服务必须已注册)
停止服务:1.OpenSCManager,2.OpenService(service_name),3ControlService. (同样要求已注册且已启动)
注销服务:必须有该服务句柄,然后deleteService

可以看到,基本上都需要与服务管理器交互获得服务管理器句柄,并且很多情况下需要通过服务名获得服务句柄,然后再执行操作。
CreateService 根据参数不同而确定服务是否已经启动,因此不能说创建服务,则服务已经启动。
记住要关闭句柄,否则会出现很多异常。比如最后注销服务的时候,如果不关闭句柄,根据MSDN提示,则该服务还没有从SCM中移除。已测试。

在枚举服务的时候有很有意思的事情出现:

    //DWORD dwBufSize =68; //68= sizeof(ENUM_SERVICE_STATUS)+sizeof(L'the first service_name')*2
    DWORD dwBufSize =sizeof(ENUM_SERVICE_STATUS)*512;
    DWORD dwByteNeeded,dwServiceReturned,lpResumeHandle= NULL;
//  ENUM_SERVICE_STATUS SerStatus[6] ={0};   //will show only one service if dwBufSize = 68
                                            //与第一条服务字符串名长度有关,请自行调整
    ENUM_SERVICE_STATUS SerStatus[512] ={0};

上述代码中dwBufSize 定义了缓冲区长度,这个缓冲区用来存放Service_name和Display_name ,因此需要非常大的缓冲区,如果较小,则接收失败。在官方文档中有提及到,为了确定长度,官方给了dwByteNeeded,用于返回若全部服务返回时,还需要将dwBufSize 扩充的大小 ,dwServiceReturned 记录了返回的服务个数。从内存调试中可以得知,缓冲区同样从SerStatus 的地址开始,大小为dwBufSize 。枚举到的服务名从&SerStatus+dwBufSize 处往低地址处填充。因此可以通过增大SerStatus 的大小,同时将dwBufSize改写成数组大小*sizeof(enum_service_status)(使用这个结构体,有利于查找buf位置)来提供更大的缓冲区。假如真的系统有512个服务,那么SerStatus 的数组一定要远远大于512个,因为其尾部都会被缓冲区给填充。至于多大,感觉自己算的还是不够精细。假如太小,很容易出异常,今天还以为是自己找到0day了,结果半天发现微软已经记录了此异常.T_T.

其他

1.微软已经提供了SC命令 来查看服务信息,比services.msc 好的是,这里可以看到的不止是service_win32 类型,而且更加详细。

C:\VC6\MyProjects\EnumService\Debug>sc qc "helloworld"
[SC] GetServiceConfig SUCCESS

SERVICE_NAME: helloworld
        TYPE               : 1   KERNEL_DRIVER
        START_TYPE         : 3   DEMAND_START
        ERROR_CONTROL      : 1   NORMAL
        BINARY_PATH_NAME   : \??\C:\drivers\objchk_wxp_x86\i386\helloworld.sys

        LOAD_ORDER_GROUP   :
        TAG                : 0
        DISPLAY_NAME       : helloworld
        DEPENDENCIES       :
        SERVICE_START_NAME : 
//这个参数很重要,查了下表示以谁的身份启动的此服务(the name of the account under which the service should run.)

2.前几天在看rootkit ,用到了OSR的Driver Loader。效果如图所示。
这里写图片描述

今天做完这个实验后,发现其实做的就是我们之前做的这些。
希望在恶意代码分析上可以用到今天的这些知识,下次打算把dll 用服务方式打开,加上boot 启动类型,实现高级注入。

–update2 5.13/2015

CreateService函数创建一个服务对象并将其安装在SCM数据库中,同时在注册表以下位置中创建同名键值:[HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services]

此项会随系统自动启动。就说嘛,create了一次之后忘了没有delete,那么下次系统启动后SCM中还有存根。原来是这么一回事。博客中之前不知道,这两个函数同时会对注册表进行操作,切记,切记。

–update5.14/2015

  • SCM数据库存在于注册表中。枚举的时候访问的是对应的注册表中的项。存放在注册表中的这些服务,开机后就会创建(注意是创建,而不是启动)。
  • OSR DRIVER LOADER 中的Register Service 做的其实就是CreateService ,这时候就会往注册表中写入启动项。
  • OSR DRIVER LOADER 中的Unregister Service 做的其实就是deleteService ,这时候就会从注册表中删除此项。

AutoRun.exe 做测试,会发现当注册服务之后,会多出此项。
这里写图片描述

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services 下存放了很多服务,这些就是SCM数据库中的数据项。
双击进入之后会看到ssdtHook服务 的信息,这些信息其实就是当时CreateService 中填写的参数。
这里写图片描述

项名称:             HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\SSDTHook
类别名:         <无类别>
最近写入时间:    2015-5-14 - 12:32
值  0
  名称:            Type
  类型:            REG_DWORD
  数据:            0x1

值  1
  名称:            Start
  类型:            REG_DWORD
  数据:            0x3

值  2
  名称:            ErrorControl
  类型:            REG_DWORD
  数据:            0x1

值  3
  名称:            ImagePath
  类型:            REG_EXPAND_SZ
  数据:            \??\C:\VC6\MyProjects\SSDTHook\SSDTHook\Debug\SSDTHook.sys

值  4
  名称:            DisplayName
  类型:            REG_SZ
  数据:            SSDTHook


项名称:             HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\SSDTHook\Security
类别名:         <无类别>
最近写入时间:    2015-5-14 - 12:32
值  0
  名称:            Security
  类型:            REG_BINARY
  数据:            
00000000   01 00 14 80 90 00 00 00 - 9c 00 00 00 14 00 00 00  ................
00000010   30 00 00 00 02 00 1c 00 - 01 00 00 00 02 80 14 00  0...............
00000020   ff 01 0f 00 01 01 00 00 - 00 00 00 01 00 00 00 00  ÿ...............
00000030   02 00 60 00 04 00 00 00 - 00 00 14 00 fd 01 02 00  ..`.........ý...
00000040   01 01 00 00 00 00 00 05 - 12 00 00 00 00 00 18 00  ................
00000050   ff 01 0f 00 01 02 00 00 - 00 00 00 05 20 00 00 00  ÿ........... ...
00000060   20 02 00 00 00 00 14 00 - 8d 01 02 00 01 01 00 00   ...............
00000070   00 00 00 05 0b 00 00 00 - 00 00 18 00 fd 01 02 00  ............ý...
00000080   01 02 00 00 00 00 00 05 - 20 00 00 00 23 02 00 00  ........ ...#...
00000090   01 01 00 00 00 00 00 05 - 12 00 00 00 01 01 00 00  ................
000000a0   00 00 00 05 12 00 00 00 -                          ........

在已经用osr driver loader 注册了之后,autorun 中就会有此项。用autorun 删除了此项之后,点击osr_driver_loader 中的start service 就会报错。提示“指定的服务并未以已安装的服务存在”。 说明删除做的正是deleteService 。删除之后,会发现autorunregister 中都已经没有了此项存在。这时候就不会开机创建此服务了。
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值