Windows Service

Kail-KM
|2016. 6. 21. 08:59
Introdution

윈도우 운영체제에 있어 우리가 흔히 직접 실행할 수 있는 프로그램 이외에 우리가 직접 실행하지 않아도 실행되는 프로세스가 있다. 이 중 서비스는 보통 시스템의 시작과 함께 시작되어 윈도우의 핵심 프로세스의 기능을 수행한다. 중요한 만큼 사용자의 개입을 최소화하기 위해 백그라운드에서 동작한다. 프로세스에 대한 제어권을 우리가 갖지 않고 윈도우의 서비스 제어 관리자가 가지고 있기 때문에 일반적인 방법으로 서비스를 분석할 수 없다. 이러한 요인들로 인해 악성코드 제작자는 서비스를 통해 백그라운드라는 점, 사용자 개입의 최소화 등의 이점을 누릴 수 있다. 따라서 이번 문서에서는 서비스가 어떻게 동작하는지, 그리고 이를 분석하기 위해서는 어떠한 방식을 사용해야 하는지에 대하여 알아보자.


Service Control Manager(SCM)

Windows에서 서비스를 실행할 때 중요한 역할을 수행하는 것이 SCM이다. SCM은 Boot시에 시작되는 시스템 프로세스의 일종으로, 서비스를 제공하는 프로그램의 등록, 시작, 종료, 삭제 등과 같은 모든 관리를 담당한다.  일반적으로 아래의 그림과 같이 우리는 Controller를 통해 SCM에게 서비스의 실행, 중지 등을 요청하여 서비스 프로그램을 시작하고 종료할 수 있다. 이렇듯 사용자나 다른 프로그램이 Service를 조작할 경우에는 반드시 SCM을 통해야만 한다. 이를 반대로 말하면 서비스 프로그램은 SCM에서 제어할 수 있는 장치를 넣어둔 프로그램이라 할 수 있다.

서비스 프로그램은 SCM에서 제어할 수 있는 장치를 가지고 있다는 것은 서비스 프로그램의 내부에 SCM에 제어를 요청하거나 연결을 요청하는 API가 존재하고 있다는 것이다. 만약 해당 API들이 없다면 서비스 프로그램은 단순히 일반 프로그램과 다를 바가 없다. 아래는 서비스 프로그램의 간략화된 main()으로 일반적인 프로그램의 main()과는 약간 다른 코드를 갖고 있다. 일반적인 프로그램의 경우 Main()에서부터 메인 스레드를 통해 독자적으로 처리하면 되지만, 이와는 다르게 서비스 프로그램의 경우에는  Main 함수에서 프로그램과 SCM을 연결해주는 StartServiceCtrlDispatcher API를 호출해주어야 한다. 여기서 연결이란 서비스의 진입점을 SCM에게 등록해주는 것으로, 해당 API를 통해 SCM은 서비스의 제어를 가지게 되고 서비스가 정지할 때까지 제어를 돌려주지 않는다.


해당 API의 인자로 지정된 services는 배열의 형태를 가지고 있다. 아래의 코드에서 "TestSvc"는 서비스의 이름을 지정해주는 것이고, TestSvcMain은 해당 서비스 프로그램이 SCM에게 실행을 요청할 내용의 코드이다. 이는 다시 말해 서비스 전용 메인 함수로 SCM은 새롭게 스레드를 생성해서 서비스 메인의 함수를 호출한다. 따라서 우리가 이후에 분석을 할 때에 중점적으로 확인해야 할 부분이 바로 TestSvcMain이 위치한 곳이 된다.

#include <Windows.h>
#include <stdio.h>

int main()
{
    SERVICE_TABLE_ENTRY services[] = {{"TestSvc", TestSvcMain}, {NULL, NULL}};
    if(!StartServiceCtrlDispatcher(services))
    {
        printf("서비스 연결 실패");
        return -1;
    }
    return 0;
}


ServiceMain & Control Handler

서비스 프로그램의 전용 메인 함수(위 예의 TestSvcMain)가 SCM과 연결되어 프로세스가 생성되었지만, SERVICE_START_PENDING 상태가 된다. 이는 정식 서비스로 동작하는 것은 아니며 이를 동작시키기 위해선 자신의 서비스 상태를 지정해주어야 한다. 서비스의 상태를 제어하기 위해 서비스의 메인 함수에서는 RegisterServiceCtrlHandler API를 호출해야 한다. 이는 SCM에 제어 핸들러를 등록하기 위한 API로, SCM은 서비스를 제어할 필요가 있을 때 이 핸들러를 호출하는 방법으로 서비스에 통지한다. 이것이 등록되어야 SCM이 서비스 프로그램을 제어할 수 있다. 

SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandler( 
    _In_ LPCTSTR            lpServiceName,  
    _In_ LPHANDLER_FUNCTION lpHandlerProc
);

제어 핸들러를 등록하고 SetServiceStatus API를 통해 서비스가 시작 상태가 된 것을 SCM에 통지해야 한다. 시작된 것을 알리고자 할 때는 SERVICE_RUNNING을 인자로 해당 API를 호출하면 된다. 이러한 과정은 서비스 프로세스가 생성되고 1초 이내에 완료될 것을 기대학 때문에 그 이상 시간이 걸릴 경우 미리 설정해주어야 한다. 만약 지정한 시간이 지나도 상태가 갱신되지 않을 경우 SCM은 초기화에 실패했다고 판단하고 서비스를 종료한다. 시간 내에 SetServiceStatus를 통해 SERVICE_RUNNIGN을 지정해주었다면 비로소 정상적인 서비스 프로세스로 동작할 수 있게 된다.

BOOL WINAPI SetServiceStatus(  
    In_ SERVICE_STATUS_HANDLE hServiceStatus,  
    In_ LPSERVICE_STATUS      lpServiceStatus
);



Reference


https://ko.wikipedia.org/wiki/윈도우_서비스

[API로 배우는 Windows 구조와 원리]

[리버싱 핵심원리]



'O / S > Window' 카테고리의 다른 글

Windows Event Message  (0) 2016.07.13
Windows Multi Task  (0) 2016.07.08
Windows Boot Process (Vista 이상ver 부팅 과정)  (0) 2016.04.13
CSIDL 값  (0) 2016.02.20
Write Protection - Registry Setting  (0) 2016.01.17