In the last 3 posts,We discussed the svchost functions which are called once per an svchost instance.They are BuildCommandOptions,BuildServiceArray and BuildServiceTable.
And we also discussed three important data structures .
1)INSTANCE_PARAMS
2)_SERVICE_ARRAY_ELEMENT
3)_SERVICE_TABLE_ELEMENT
if you still remember , till now no members of the structure _SERVICE_ARRAY_ELEMENT except srv_name is filled.
So what about these other members??and when do they get filled???
struct _SERVICE_ARRAY_ELEMENT
{
wchar_t* srv_name;
_SRV_DLL_INFO* srv_dll_info;
char* SvcMainName;
unsigned long Count;
FUNCPTR d;
};
Lets take these members one by one
1)_SERVICE_ARRAY_ELEMENT::srv_name ,it is the service name in ServiceNames.
so , it is always pointing inside ServiceNames.
2)_SERVICE_ARRAY_ELEMENT::srv_dll_info
it is a pointer to another structure of different type. this structure holds info about the service DLL.
struct _SRV_DLL_INFO
{
_SRV_DLL_INFO* pNext;
_SRV_DLL_INFO* pPrev;
HMODULE hDll;
wchar_t* dllName;
_SERVICE_ARRAY_ELEMENT* pServiceArray;
};
this structure itself is a node in a "Circular Doubly linked List".
_SRV_DLL_INFO::pNext is a pointer to the next node.
_SRV_DLL_INFO::pPrev is a pointer to the previous node.
_SRV_DLL_INFO::hDll is the loaded Service DLL handle.
_SRV_DLL_INFO::dllName is the Service DLL complete filename.
_SRV_DLL_INFO::pServiceArray is pointer to the corresponding _SERVICE_ARRAY_ELEMENT.
So the final view will be like this...
In the figure above,only Audiosrv and Browser are started,So only Audiosrv and Browser have Nodes inserted into this circular doubly linked list.BITS is not started yet so it has no node.
N.B two global variables of type _SRV_DLL_INFO* exist to act as the Linked list Head and tail.
DllList acts as the head and DllList2 acts as the tail.
N.B For every Service loaded , A new node is inserted into this circular doubly linked list and its
_SERVICE_ARRAY_ELEMENT::srv_dll_info is null.
N.B If a svchost service is started,structure _SERVICE_ARRAY_ELEMENT points at structure _SRV_DLL_INFO and vice versa.
3)_SERVICE_ARRAY_ELEMENT::SvcMainName
N.B Each svchost Service DLL must export at least one function for some specific tasks.In most cases this function is exported under the name "ServiceMain".You can use other names as long as you configure the registry properly.
_SERVICE_ARRAY_ELEMENT::SvcMainName is a pointer to the name of the corresponding svchost service DLL ServiceMain.if it is zero, "ServiceMain is assumed".
it is of type char* not wchar_t* (for compatibilty with function GetProcAddress).
N.B ServiceMain is different from DllMain.
DllMain is called just after svchost calls LoadLibraryEx but ServiceMain is called by ServiceStarter(we will talk about Service Starter later,Just some patience).
N.B There is another function that many svchost service DLLs export,it is SvchostPushServiceGlobals(we will talk about its functionality later in this post).
Unlike ServiceMain ,its name cant be configured through the registry.It is always with this name.
The figure above shows the exported functions by wzcsvc "Wireless zero configuration" service.
WZCSvcMain and SvchostPushServiceGlobals.
4)_SERVICE_ARRAY_ELEMENT::Count this member is incremented,by function ServiceStarter or function _SvcRegisterStopCallback, And decremented by function UnloadServiceDLL.
5)_SERVICE_ARRAY_ELEMENT::d ,this is a pointer to the service specific stop callback function.This callback is called when a specific service object is signaled.
You may ask me how svchost knows about this callback??Is it exported by the service DLL??
No,the service Dll does not export it but it exports another function SvchostPushServiceGlobals.
This function lets svchost provide the service with some data structure called shared globals table.
struct SHARED_GLOBALS_TABLE
{
unsigned char* pNullSid;
unsigned char* pWorldSid;
unsigned char* pLocalSid;
unsigned char* pNetworkSid;
unsigned char* pLocalSystemSid;
unsigned char* pLocalServiceSid;
unsigned char* pNetworkServiceSid;
unsigned char* pBuiltinDomainSid;
unsigned char* pAuthenticatedUserSid;
unsigned char* pAnonymousLogonSid;
unsigned char* pAliasAdminsSid;
unsigned char* pAliasUsersSid;
unsigned char* pAliasGuestsSid;
unsigned char* pAliasPowerUsersSid;
unsigned char* pAliasAccountOpsSid;
unsigned char* pAliasSystemOpsSid;
unsigned char* pAliasPrintOpsSid;
unsigned char* pAliasBackupOpsSid;
//--
FPTR3 pRpcStartServer;
FPTR4 pRpcStopServer;
FPTR4 pRpcStopServerEx;
//--
FPTR5 pNetBiosOpen;
FPTR5 pNetBiosClose;
FPTR6 pNetBiosReset;
//----
FPTR7 pRegisterStopCallback;
};
//here we only care about the last member of this structure SHARED_GLOBALS_TABLE::pRegisterStopCallback.
Once SvchostPushServiceGlobals is called with this table as its argument,The service will be aware of a function in svchost that will register its own stop callback and store its address in the service _SERVICE_ARRAY_ELEMENT::d.
So here is the scenario:_
1)svchost builds the shared globals table.
2)svchost calls the service DLL function SvchostPushServiceGlobals,to provide it with this table.
3)The service calls SHARED_GLOBALS_TABLE::pRegisterStopCallback which (a)registers a wait on a service specific object.(b) increments _SERVICE_ARRAY_ELEMENT::Count (c) stores the service specific callback into _SERVICE_ARRAY_ELEMENT::d
4)Once this object (svchost knows nothing about this object except its handle) is handled,svchost function _SvchostStopCallback is called.which (a)calls the service specific callback (b)unloads yhe service DLL.
A question arises here , why all these steps for stopping an svchost service??
I asked myself this question??
The answer was simply "DllUnloadOnStop" How?
Some services need to unload its Dll on stop, the others dont.
The svchost Service which doesn't need to unload its Dll ,doesn't need to care about these steps.
The svchost service which needs to unload its Dll ,simply can't do this by itself(Cuz no DLL calls FreeLibrary to free itself from the current process Address space).
Till now ,we are still preparing for function ServiceStarter which i consider the backbone for svchost .
Any suggestions or ideas are welcome
And we also discussed three important data structures .
1)INSTANCE_PARAMS
2)_SERVICE_ARRAY_ELEMENT
3)_SERVICE_TABLE_ELEMENT
if you still remember , till now no members of the structure _SERVICE_ARRAY_ELEMENT except srv_name is filled.
So what about these other members??and when do they get filled???
struct _SERVICE_ARRAY_ELEMENT
{
wchar_t* srv_name;
_SRV_DLL_INFO* srv_dll_info;
char* SvcMainName;
unsigned long Count;
FUNCPTR d;
};
Lets take these members one by one
1)_SERVICE_ARRAY_ELEMENT::srv_name ,it is the service name in ServiceNames.
so , it is always pointing inside ServiceNames.
2)_SERVICE_ARRAY_ELEMENT::srv_dll_info
it is a pointer to another structure of different type. this structure holds info about the service DLL.
struct _SRV_DLL_INFO
{
_SRV_DLL_INFO* pNext;
_SRV_DLL_INFO* pPrev;
HMODULE hDll;
wchar_t* dllName;
_SERVICE_ARRAY_ELEMENT* pServiceArray;
};
this structure itself is a node in a "Circular Doubly linked List".
_SRV_DLL_INFO::pNext is a pointer to the next node.
_SRV_DLL_INFO::pPrev is a pointer to the previous node.
_SRV_DLL_INFO::hDll is the loaded Service DLL handle.
_SRV_DLL_INFO::dllName is the Service DLL complete filename.
_SRV_DLL_INFO::pServiceArray is pointer to the corresponding _SERVICE_ARRAY_ELEMENT.
So the final view will be like this...
In the figure above,only Audiosrv and Browser are started,So only Audiosrv and Browser have Nodes inserted into this circular doubly linked list.BITS is not started yet so it has no node.
N.B two global variables of type _SRV_DLL_INFO* exist to act as the Linked list Head and tail.
DllList acts as the head and DllList2 acts as the tail.
N.B For every Service loaded , A new node is inserted into this circular doubly linked list and its
_SERVICE_ARRAY_ELEMENT::srv_dll_info is null.
N.B If a svchost service is started,structure _SERVICE_ARRAY_ELEMENT points at structure _SRV_DLL_INFO and vice versa.
3)_SERVICE_ARRAY_ELEMENT::SvcMainName
N.B Each svchost Service DLL must export at least one function for some specific tasks.In most cases this function is exported under the name "ServiceMain".You can use other names as long as you configure the registry properly.
_SERVICE_ARRAY_ELEMENT::SvcMainName is a pointer to the name of the corresponding svchost service DLL ServiceMain.if it is zero, "ServiceMain is assumed".
it is of type char* not wchar_t* (for compatibilty with function GetProcAddress).
N.B ServiceMain is different from DllMain.
DllMain is called just after svchost calls LoadLibraryEx but ServiceMain is called by ServiceStarter(we will talk about Service Starter later,Just some patience).
N.B There is another function that many svchost service DLLs export,it is SvchostPushServiceGlobals(we will talk about its functionality later in this post).
Unlike ServiceMain ,its name cant be configured through the registry.It is always with this name.
The figure above shows the exported functions by wzcsvc "Wireless zero configuration" service.
WZCSvcMain and SvchostPushServiceGlobals.
4)_SERVICE_ARRAY_ELEMENT::Count this member is incremented,by function ServiceStarter or function _SvcRegisterStopCallback, And decremented by function UnloadServiceDLL.
5)_SERVICE_ARRAY_ELEMENT::d ,this is a pointer to the service specific stop callback function.This callback is called when a specific service object is signaled.
You may ask me how svchost knows about this callback??Is it exported by the service DLL??
No,the service Dll does not export it but it exports another function SvchostPushServiceGlobals.
This function lets svchost provide the service with some data structure called shared globals table.
struct SHARED_GLOBALS_TABLE
{
unsigned char* pNullSid;
unsigned char* pWorldSid;
unsigned char* pLocalSid;
unsigned char* pNetworkSid;
unsigned char* pLocalSystemSid;
unsigned char* pLocalServiceSid;
unsigned char* pNetworkServiceSid;
unsigned char* pBuiltinDomainSid;
unsigned char* pAuthenticatedUserSid;
unsigned char* pAnonymousLogonSid;
unsigned char* pAliasAdminsSid;
unsigned char* pAliasUsersSid;
unsigned char* pAliasGuestsSid;
unsigned char* pAliasPowerUsersSid;
unsigned char* pAliasAccountOpsSid;
unsigned char* pAliasSystemOpsSid;
unsigned char* pAliasPrintOpsSid;
unsigned char* pAliasBackupOpsSid;
//--
FPTR3 pRpcStartServer;
FPTR4 pRpcStopServer;
FPTR4 pRpcStopServerEx;
//--
FPTR5 pNetBiosOpen;
FPTR5 pNetBiosClose;
FPTR6 pNetBiosReset;
//----
FPTR7 pRegisterStopCallback;
};
//here we only care about the last member of this structure SHARED_GLOBALS_TABLE::pRegisterStopCallback.
Once SvchostPushServiceGlobals is called with this table as its argument,The service will be aware of a function in svchost that will register its own stop callback and store its address in the service _SERVICE_ARRAY_ELEMENT::d.
So here is the scenario:_
1)svchost builds the shared globals table.
2)svchost calls the service DLL function SvchostPushServiceGlobals,to provide it with this table.
3)The service calls SHARED_GLOBALS_TABLE::pRegisterStopCallback which (a)registers a wait on a service specific object.(b) increments _SERVICE_ARRAY_ELEMENT::Count (c) stores the service specific callback into _SERVICE_ARRAY_ELEMENT::d
4)Once this object (svchost knows nothing about this object except its handle) is handled,svchost function _SvchostStopCallback is called.which (a)calls the service specific callback (b)unloads yhe service DLL.
A question arises here , why all these steps for stopping an svchost service??
I asked myself this question??
The answer was simply "DllUnloadOnStop" How?
Some services need to unload its Dll on stop, the others dont.
The svchost Service which doesn't need to unload its Dll ,doesn't need to care about these steps.
The svchost service which needs to unload its Dll ,simply can't do this by itself(Cuz no DLL calls FreeLibrary to free itself from the current process Address space).
Till now ,we are still preparing for function ServiceStarter which i consider the backbone for svchost .
Any suggestions or ideas are welcome
No comments:
Post a Comment