Tuesday, May 11, 2010

SCM – EnumServicesStatus

SCM – EnumServicesStatus

EnumServiceStatus enumerates NT services in the service control manager (SCM) database. By using this Win32 API, we can get all serivce list in the machine. Here is a typical example for Win32 service enumeration.
SC_HANDLE sc = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE|GENERIC_READ);
    if ( sc == NULL )
    {
        wprintf(L"Unable to open SCM...\n");
        return 0;
    }

    LPENUM_SERVICE_STATUS lpEnumService = 0;
    DWORD dwBuffSize = 0, dwBytesNeeded = 0, dwServicesReturned = 0, dwResumeHandle = 0;

    BOOL bResult = EnumServicesStatus(
        sc,
        SERVICE_WIN32,
        SERVICE_STATE_ALL,
        lpEnumService,
        dwBuffSize,
        &dwBytesNeeded,
        &dwServicesReturned,
        &dwResumeHandle
    );

    if ( bResult != FALSE )
    {
        CloseServiceHandle(sc);
        return 0;
    }

    lpEnumService = (LPENUM_SERVICE_STATUS) LocalAlloc(LPTR, dwBytesNeeded);  
    if ( lpEnumService == 0 )
    {
        CloseServiceHandle(sc);
        return 0;
    }

    bResult = EnumServicesStatus(
        sc,
        SERVICE_WIN32,
        SERVICE_STATE_ALL,
        lpEnumService,
        dwBytesNeeded,
        &dwBytesNeeded,
        &dwServicesReturned,
        &dwResumeHandle
    );

    LPENUM_SERVICE_STATUS lpCurrentEnum = lpEnumService;

    for ( DWORD i = 0; i < dwServicesReturned; i++ )
    {
        wprintf(L"%s\n", lpCurrentEnum->lpServiceName);
        ++lpCurrentEnum;      
    }

    wprintf(L"Total: %d\n", dwServicesReturned);

    LocalFree(lpEnumService);
    CloseServiceHandle(sc);
One interesting thing to mention is EnumServicesStatus() API can return all or partial service list depending on user security permission. Let’s say, user A has access permission to 20 services out of total 200 services. If the code above runs with user A account, EnumServicesStatus only returns 20 services without returning error.

How can we know whether an user has access permission for any specific service? There are many ways of doing it. One of them is checking security descriptor of the service process using kernel debugger. Another handy way is using Service Control utility (sc.exe in resource kit). For example, below SC command is to show security descriptor of default SQL instance.

C > sc sdshow MSSQLSERVER

D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)

The output is text format of security descriptor, known as SDDL (Security Descriptor Definition Language). MSDN explains all the details of this format. Basically the example is saying LocalSystem (SY), Builtin Admin (BA), Interactive User (IU), Logged as service user (SU) can access MSSQLSERVER service (please note that each user principal has different ACL but all user principals allow query). By checking security descriptor for each service, we can figure out why some services cannot be enumerated while others can.

By the same token, the following WMI query (in Powershell) will also return the all or partial service list depending on user's access token. It’s connecting to remoteServer to get Win32 service names.

gwmi -query "SELECT * FROM Win32_Service" -computer remoteServer | select Name

If it returns partial results, chances are the user might not have enough permission so you might want to use SC tool for further investigation.

No comments:

Post a Comment