Windows Service is a program executing in the background which is
started automatically when Windows boots and runs as long as System
is up and running. Service is designed to perform a specific
function and generally does not interact with user.Compared to
normal processes, Services normally run with
'System' privilege
which allow them to perform higher privilege operations. Windows
Service Manager (Services.exe) is responsible for managing all
aspects of Services. Services can be managed using the built in
'Service Management Console' which can be launched through
'Administrative Tools' in Control Panel.
As services runs with highest privilege, most Rootkits implement
services to keep control over the entire system.Though services
operate in the background they can be seen through the 'Task
Manager' or 'Service Management Console'. So it is important for the
Rootkits to hide their services to prevent its detection.
How Rootkits Hide Services
Many Rootkits such as Hacker Defender, Vanquish use the Services not
only to manage its operations but to remain under the cover. As services
are visible to normal user, Rootkits use multiple techniques to hide
their services.
One of the common way to list windows services is using EnumServicesStatus
API function. So Rootkits generally hook this function and return the
results by filtering out its services. As a result any Anti-Rootkit
application which uses this method will fail to discover such Rootkit
services.
Another way to list all services is by enumerating 'Services'
registry key. Windows keeps the database of all installed services in
the registry at following location
.
So application can manually enumerate through all the entries within
this registry key to discover all the services. To hide from this
technique, Rootkits hook the registry enumeration API functions and
return the modified values thus deceiving the detection tools.
Detailed information about hiding registry entries and their
detection is mentioned in the article
[1]"Hidden Registry Detection".
Hidden Rootkit Services Detection Methods
As mentioned above, Rootkits use advanced techniques to hide their
services from being detected by various Applications. Here are some of
the methods which can be effectively used to detect most of such Rootkit
services.
Hidden Service Detection using 'Hook Bypass Method through
Memory Mapping DLL'
Hidden
Service Detection by 'Enumerating Child Processes of Services.exe'
Hidden Service Detection by 'Enumerating Processes
with NtControlPipe'
Hidden Service Detection by
'Enumerating
Services Registry Key'
Hidden Services
Detection using Hook Bypass Method through Memory Mapping DLL
This method rely on the fact that API hooking can be bypassed by
directly mapping the DLL as an image rather than loading it through
LoadLibrary function. This way any attempt by Rootkits to hook
EnumServiceStatus function can be prevented by directly mapping the
Advapi32.dll through CreateFileMapping API function. Then the
EnumServiceStatus function within the mapped image is located and called
directly thus bypassing the hooks leading to discovery of hidden Rootkit
services.
However this is not the new method and it was
initially used by IceSword
(Anti-Rootkit tool). Later it was exposed by EiNSTeiN_ in his
[2]'Hidden Service
Detection' article.
Here is the complete code example which illustrates this
technique.
typedef BOOL (WINAPI *LPFUN_ENUMSERVICESSTATUSEX) (
SC_HANDLE hSCManager,
SC_ENUM_TYPE InfoLevel,
DWORD dwServiceType,
DWORD dwServiceState,
LPBYTE lpServices,
DWORD cbBufSize,
LPDWORD pcbBytesNeeded,
LPDWORD lpServicesReturned,
LPDWORD lpResumeHandle,
LPCTSTR pszGroupName
);
BOOL DetectHiddenServicesUsingHookBypassMethod
(SC_HANDLE scManager)
{
char WinSysDir[MAX_PATH];
char advapiPath[MAX_PATH*2];
GetSystemDirectory(WinSysDir, MAX_PATH);
sprintf_s(advapiPath, MAX_PATH*2,
"%s\\advapi32.dll", WinSysDir);
HANDLE hFile = CreateFile(advapiPath,
GENERIC_READ | GENERIC_EXECUTE ,
FILE_SHARE_READ,
0,
OPEN_EXISTING,
0,
0);
if( hFile == INVALID_HANDLE_VALUE )
{
printf("%s : Failed to open %s, Error=0x%.8x",
__FUNCTION__, advapiPath, GetLastError());
return FALSE;
}
HANDLE hMappedFile = CreateFileMapping(hFile, NULL,
PAGE_EXECUTE_READ |SEC_IMAGE, 0, 0, NULL);
if( hMappedFile == NULL )
{
printf("%s : Failed to create file
mapping for advapi32.dll", __FUNCTION__);
CloseHandle(hFile);
return FALSE;
}
CloseHandle(hFile);
LPVOID addrMappedDll = MapViewOfFile(hMappedFile,
FILE_MAP_READ | FILE_MAP_EXECUTE , 0, 0, 0);
if( addrMappedDll == NULL )
{
printf("%s : Failed to map view
of flle advapi32.dll", __FUNCTION__);
CloseHandle(hMappedFile);
return FALSE;
}
CloseHandle(hMappedFile);
LPFUN_ENUMSERVICESSTATUSEX MappedEnumServicesStatusEx = NULL;
LPVOID addrDllBase = GetModuleHandle("advapi32.dll");
MappedEnumServicesStatusEx = ( (DWORD)addrMappedDll +
((DWORD)&EnumServicesStatusEx - (DWORD)addrDllBase) );
printf("Org Advapi32 base address is %p", addrDllBase);
printf("Org EnumServiceStatus address is %p",
&EnumServicesStatusEx);
printf("Mapped Advapi32 address is %p", addrMappedDll);
printf("Mapped enumservicesstatusEx address is %p",
MappedEnumServicesStatusEx);
// Now enumerate services using
// mapped EnumServicesStatusEx function
DWORD bytesNeeded ;
DWORD countServices ;
DWORD resumeHandle = 0;
DWORD lastErrorCode;
int totalCount=0;
int curIndex=-1;
BOOL retvalue;
DWORD buffSize = sizeof(ENUM_SERVICE_STATUS_PROCESS) * 500;
ENUM_SERVICE_STATUS_PROCESS *enumServices = NULL;
while( 1 )
{
enumServices =
( ENUM_SERVICE_STATUS_PROCESS *) malloc( buffSize );
if( enumServices == NULL )
{
printf("\n %s :
Failed to allocated buffer", __FUNCTION__);
break ;
}
retvalue = MappedEnumServicesStatusEx( scManager ,
SC_ENUM_PROCESS_INFO ,
SERVICE_WIN32 ,
SERVICE_STATE_ALL ,
enumServices ,
buffSize ,
&bytesNeeded ,
&countServices,
&resumeHandle,
NULL
);
// Error has occured ...
// not related to more data...so return
lastErrorCode = GetLastError();
if( retvalue == FALSE && lastErrorCode != ERROR_MORE_DATA )
{
free( enumServices );
break;
}
for(int i = 0; i < countServices; i++ )
{
// Check if this is hidden service
// by comparing with earlier results
if( IsHiddenService(enumServices[i].lpServiceName) )
{
//We have found hidden service
printf("\n Found hidden service %s ",
enumServices[i].lpServiceName);
}
}
free( enumServices );
// check if there are more services to be retrieved ...?
if( (retvalue == FALSE) &&
(lastErrorCode == ERROR_MORE_DATA) )
buffSize = bytesNeeded ;
else
break;
}
UnmapViewOfFile(addrMappedDll);
return TRUE;
}
This method can effectively detect the Rootkits such as
HackerDefender which hooks EnumServiceStatus function to hide its
services. Though there is no currently known Rootkits which hide against
this method, it is possible to bypass this detection mechanism by
hooking file mapping functions and then extending API hooks to any
mapped DLL image also.
Hidden Services Detection by Enumerating Child Processes of Services.exe
All Windows service processes are created by 'Service Manager
Process' i.e. Services.exe. This fact can be used to list all the child
processes created by Services.exe, thus possibly detecting all running
services. Since the normal process enumeration function can be hooked by
Rootkits, the best way to implement this technique is to directly use
NtQuerySystemInformation function to enumerate all processes and then
separating out the child processes created by Services.exe. Then this
list can be compared with the normal list to find all hidden services.
Below is the sample code which uses this technique to uncover all
running services.
BOOL IsChildProcess(int pidParent, int pidChild, HANDLE hprocess)
{
NTSTATUS status = STATUS_SUCCESS;
PROCESS_BASIC_INFORMATION procInfo;
status = NtQueryInformationProcess(hprocess,
ProcessBasicInformation,
&procInfo,
sizeof(procInfo),
NULL
);
if( status == STATUS_SUCCESS )
{
if( procInfo.ParentProcessId == pidParent )
return TRUE;
}
return FALSE;
}
void DetectHiddenServices(int pidService)
{
NTSTATUS status = STATUS_SUCCESS;
DWORD dwSize = 800000;
void *bufHandleTable = malloc(dwSize);;
status = NtQuerySystemInformation
(SystemHandleInformation, bufferHandleTable, dwSize, 0);
if( status != STATUS_SUCCESS )
{
//failed
return;
}
HANDLE hprocess;
SYSTEM_HANDLE_INFORMATION *HandleInfo =
(SYSTEM_HANDLE_INFORMATION *) bufHandleTable;
for(int i=0; i< HandleInfo->NumberOfHandles; i++)
{
int pid = HandleInfo->Handles[i].UniqueProcessId;
//If this process is services.exe then ignore
if( pidService == pid )
continue;
hprocess =
OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
false,
pid);
if( hprocess == NULL )
continue;
if( IsChildProcess(pidServices, pid, hprocess) )
{
printf("Found service process %d", pid);
}
CloseHandle(hprocess);
}
}
Although this technique can be easily thwarted by hooking the
NtQuerySystemInformation in the Kernel, it presents simple way to detect
services hidden by userland Rootkits.
Hidden Services Detection by Enumerating Processes with 'NtControlPipe' Handle
This method relies on the fact that every Windows service interacts with 'Service Manager Process'
(services.exe) through named pipe. This named pipe has the name
'NtControlPipe**' where ** indicates the start order of that service. So
by enumerating all the processes having handle 'NtControlPipe**' one can
list all running services. Then this list can be compared with normal
services discovered through EnumServiceStatus function to uncover the
hidden service processes.
Here is the sample code below which uses this technique.
NTSTATUS status = STATUS_SUCCESS;
DWORD dwSize = 800000;
void *bufHandleTable = malloc(dwSize);;
status = NtQuerySystemInformation(SystemHandleInformation,
bufferHandleTable, dwSize, 0);
if( status != STATUS_SUCCESS )
{
//failed
return;
}
HANDLE hprocess,hCopy;
IO_STATUS_BLOCK iostatus;
int pid;
BOOL bResult;
SYSTEM_HANDLE_INFORMATION *HandleInfo =
(SYSTEM_HANDLE_INFORMATION *) bufHandleTable;
PFILE_NAME_INFORMATION pNameInfo =
(PFILE_NAME_INFORMATION)malloc( MAX_PATH * 2 * 2);
int dwInfoSize = MAX_PATH * 2 * 2;
for(int i=0; i< HandleInfo->NumberOfHandles; i++)
{
int pid = HandleInfo->Handles[i].UniqueProcessId;
//If this process is services.exe then ignore
if( pidService == pid )
continue;
//Check for only file handles
//For XP HANDLE_TYPE_FILE is 28
if( HandleInfo->Handles[i].ObjectTypeIndex == HANDLE_TYPE_FILE)
{
hprocess =
OpenProcess(PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION,
false,
pid
);
if( hprocess == NULL )
continue;
bResult = DuplicateHandle(hprocess,
(HANDLE)HandleInfo->Handles[i].Handle,
GetCurrentProcess(),
&hCopy,
MAXIMUM_ALLOWED,
FALSE,
0);
if( bResult == FALSE )
continue;
status = NtQueryInformationFile(hCopy,
&iostatus,
pNameInfo,
dwInfoSize,
FileNameInformation);
if( status == STATUS_SUCCESS )
{
// check if this handle is named
// pipe with name 'ntcontrolpipe'
wcslwr(pNameInfo->FileName);
if( wcsstr( pNameInfo->FileName, L"ntcontrolpipe") )
{
//Found the service, now check if its hidden service
if( IsHiddenService(pid) )
{
printf("Found hidden service %d", pid);
}
}
}
CloseHandle(hCopy);
CloseHandle(hprocess);
}
} //End of for loop
But this code hangs when NtQueryInformationFile tries to get the name of
named pipe. However it works greatly when executed inside the kernel.
Even the [3] Process Explorer from Sysinternals uses the driver to
enumerate process handles. It may be possible to successfully implement
this method in the user land itself through some innovative way.
Hidden Services Detection by Enumerating Services Registry Key
As mentioned earlier, Windows stores all installed services under the
following registry key,
Normally Rootkits
hides their service entries under this key by using variety of registry
hiding technique. As a result any attempt to discover these hidden
services using standard registry functions will fail. However these
hidden keys can be successfully uncovered as explained in the article
[1]'Hidden Registry Detection'.
Though many Rootkits uses combination of methods to hide their
services, there are some (for example Vanquish Rootkit) which uses only
this technique to hide its services. As a result such services are shown
even in normal query through EnumServiceStatus function. So in order to
effectively mark it as Rootkit, one has to use this method of detection
as well.
Conclusion
This article described variety of techniques to detect the hidden
Rootkit services. Even though most of the times one method is enough to
uncover the hidden services, its good idea to combine multiple
techniques to make the detection process more robust and successful.
Though the 'Hidden Rootkit Services' detection techniques presented
here are effective ones, often they are not enough
considering the evolving trend of emerging Rootkits. In that context
this article only serves as basic footsteps towards more sophisticated
detection methods.