SVCHOST程式解讀

hzh_hu發表於2005-11-19

大家都要知道Svchost.exe,是系統必不可少的一個程式,很多服務都會多多少少用到它,
但是我想大家也知道,由於它本身特殊性,高明的"駭客們"肯定是不會放過的,前段時間的Svchost.exe木馬風波,大家應該是記憶猶新吧,而且現在還是有很多機器裡都藏有此木馬,因為它偽裝和系統程式Svchost.exe一樣,所以很多人分不清,那個是程式,那個是木馬....
好的,還是讓我們詳盡瞭解一下Svchost.exe程式吧

1.多個服務共享一個 Svchost.exe程式利與弊
windows 系統服務分為獨立程式和共享程式兩種,在windows NT時只有伺服器管理器SCM(Services.exe)有多個共享服務,隨著系統內建服務的增加,在windows 2000中ms又把很多服務做成共享方式,由svchost.exe啟動。windows 2000一般有2個svchost程式,一個是RPCSS(Remote Procedure Call)服務程式,另外一個則是由很多服務共享的一個svchost.exe。而在windows XP中,則一般有4個以上的svchost.exe服務程式,windows 2003 server中則更多,可以看出把更多的系統內建服務以共享程式方式由svchost啟動是ms的一個趨勢。這樣做在一定程度上減少了系統資源的消耗,不過也帶來一定的不穩定因素,因為任何一個共享程式的服務因為錯誤退出程式就會導致整個程式中的所有服務都退出。另外就是有一點安全隱患,首先要介紹一下svchost.exe的實現機制。


2. Svchost原理
Svchost本身只是作為服務宿主,並不實現任何服務功能,需要Svchost啟動的服務以動態連結庫形式實現,在安裝這些服務時,把服務的可執行程式指向svchost,啟動這些服務時由svchost呼叫相應服務的動態連結庫來啟動服務。

那麼svchost如何知道某一服務是由哪個動態連結庫負責呢?這不是由服務的可執行程式路徑中的引數部分提供的,而是服務在登錄檔中的引數設定的,登錄檔中服務下邊有一個Parameters子鍵其中的ServiceDll表明該服務由哪個動態連結庫負責。並且所有這些服務動態連結庫都必須要匯出一個ServiceMain()函式,用來處理服務任務。

例如rpcss(Remote Procedure Call)在登錄檔中的位置是 HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesRpcSs,它的引數子鍵Parameters裡有這樣一項:
"ServiceDll"=REG_EXPAND_SZ:"%SystemRoot%system32 pcss.dll"
當啟動rpcss服務時,svchost就會呼叫rpcss.dll,並且執行其ServiceMain()函式執行具體服務。

既然這些服務是使用共享程式方式由svchost啟動的,為什麼系統中會有多個svchost程式呢?ms把這些服務分為幾組,同組服務共享一個svchost程式,不同組服務使用多個svchost程式,組的區別是由服務的可執行程式後邊的引數決定的。

例如rpcss在登錄檔中 HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesRpcSs 有這樣一項:
"ImagePath"=REG_EXPAND_SZ:"%SystemRoot%system32svchost -k rpcss"
因此rpcss就屬於rpcss組,這在服務管理控制檯也可以看到。

svchost的所有組和組內的所有服務都在登錄檔的如下位置: HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionSvchost,例如windows 2000共有4組rpcss、netsvcs、wugroup、BITSgroup,其中最多的就是netsvcs=REG_MULTI_SZ:EventSystem.Ias.Iprip.Irmon.Netman.

Nwsapagent.Rasauto.Rasman.Remoteaccess.SENS.Sharedaccess.Tapisrv.Ntmssvc.wzcsvc..

在啟動一個svchost.exe負責的服務時,服務管理器如果遇到可執行程式內容ImagePath已經存在於服務管理器的映象庫中,就不在啟動第2個程式svchost,而是直接啟動服務。這樣就實現了多個服務共享一個svchost程式。


3. Svchost程式碼
現在我們基本清楚svchost的原理了,但是要自己寫一個DLL形式的服務,由svchost來啟動,僅有上邊的資訊還有些問題不是很清楚。比如我們在匯出的ServiceMain()函式中接收的引數是ANSI還是Unicode?我們是否需要呼叫RegisterServiceCtrlHandler和StartServiceCtrlDispatcher來註冊服務控制及排程函式?

這些問題要透過檢視svchost程式碼獲得。下邊的程式碼是windows 2000+ service pack 4 的svchost反彙編片段,可以看出svchost程式還是很簡單的。

主函式首先呼叫ProcCommandLine()對命令列進行分析,獲得要啟動的服務組,然後呼叫SvcHostOptions()查詢該服務組的選項和服務組的所有服務,並使用一個資料結構 svcTable 來儲存這些服務及其服務的DLL,然後呼叫PrepareSvcTable() 函式建立 SERVICE_TABLE_ENTRY 結構,把所有處理函式SERVICE_MAIN_FUNCTION 指向自己的一個函式FuncServiceMain(),最後呼叫API StartServiceCtrlDispatcher() 註冊這些服務的排程函式。

; =============================== Main Funcion =======================================
.text:010010B8 public start
.text:010010B8 start proc near
.text:010010B8 push esi
.text:010010B9 push edi
.text:010010BA push offset sub_1001EBA ; lpTopLevelExceptionFilter
.text:010010BF xor edi, edi
.text:010010C1 call ds:SetUnhandledExceptionFilter
.text:010010C7 push 1 ; uMode
.text:010010C9 call ds:SetErrorMode
.text:010010CF call ds:GetProcessHeap
.text:010010D5 push eax
.text:010010D6 call sub_1001142
.text:010010DB mov eax, offset dword_1003018
.text:010010E0 push offset unk_1003000 ; lpCriticalSection
.text:010010E5 mov dword_100301C, eax
.text:010010EA mov dword_1003018, eax
.text:010010EF call ds:InitializeCriticalSection
.text:010010F5 call ds:GetCommandLineW
.text:010010FB push eax ; lpString
.text:010010FC call ProcCommandLine
.text:01001101 mov esi, eax
.text:01001103 test esi, esi
.text:01001105 jz short lab_doservice
.text:01001107 push esi
.text:01001108 call SvcHostOptions
.text:0100110D call PrepareSvcTable
.text:01001112 mov edi, eax ; SERVICE_TABLE_ENTRY returned
.text:01001114 test edi, edi
.text:01001116 jz short loc_1001128
.text:01001118 mov eax, [esi+10h]
.text:0100111B test eax, eax
.text:0100111D jz short loc_1001128
.text:0100111F push dword ptr [esi+14h] ; dwCapabilities
.text:01001122 push eax ; int
.text:01001123 call InitializeSecurity
.text:01001128
.text:01001128 loc_1001128: ; CODE XREF: start+5Ej
.text:01001128 ; start+65j
.text:01001128 push esi ; lpMem
.text:01001129 call HeapFreeMem
.text:0100112E
.text:0100112E lab_doservice: ; CODE XREF: start+4Dj
.text:0100112E test edi, edi
.text:01001130 jz ExitProgram
.text:01001136 push edi ; lpServiceStartTable
.text:01001137 call ds:StartServiceCtrlDispatcherW
.text:0100113D jmp ExitProgram
.text:0100113D start endp
; =============================== Main Funcion end ===========================================

由於svchost為該組的所有服務都註冊了svchost中的一個處理函式,因此每次啟動任何一個服務時,服務管理器SCM都會呼叫FuncServiceMain() 這個函式。這個函式使用 svcTable 查詢要啟動的服務使用的DLL,呼叫DLL匯出的ServiceMain()函式來啟動服務,然後返回。
; ============================== FuncServiceMain() ===========================================
.text:01001504 FuncServiceMain proc near ; DATA XREF: PrepareSvcTable+44o
.text:01001504
.text:01001504 arg_0 = dword ptr 8
.text:01001504 arg_4 = dword ptr 0Ch
.text:01001504
.text:01001504 push ecx
.text:01001505 mov eax, [esp+arg_4]
.text:01001509 push ebx
.text:0100150A push ebp
.text:0100150B push esi
.text:0100150C mov ebx, offset unk_1003000
.text:01001511 push edi
.text:01001512 mov edi, [eax]
.text:01001514 push ebx
.text:01001515 xor ebp, ebp
.text:01001517 call ds:EnterCriticalSection
.text:0100151D xor esi, esi
.text:0100151F cmp dwGroupSize, esi
.text:01001525 jbe short loc_1001566
.text:01001527 and [esp+10h], esi
.text:0100152B
.text:0100152B loc_100152B: ; CODE XREF: FuncServiceMain+4Aj
.text:0100152B mov eax, svcTable
.text:01001530 mov ecx, [esp+10h]
.text:01001534 push dword ptr [eax+ecx]
.text:01001537 push edi
.text:01001538 call ds:lstrcmpiW
.text:0100153E test eax, eax
.text:01001540 jz short StartThis
.text:01001542 add dword ptr [esp+10h], 0Ch
.text:01001547 inc esi
.text:01001548 cmp esi, dwGroupSize
.text:0100154E jb short loc_100152B
.text:01001550 jmp short loc_1001566
.text:01001552 ; =================================================
.text:01001552
.text:01001552 StartThis: ; CODE XREF: FuncServiceMain+3Cj
.text:01001552 mov ecx, svcTable
.text:01001558 lea eax, [esi+esi*2]
.text:0100155B lea eax, [ecx+eax*4]
.text:0100155E push eax
.text:0100155F call GetDLLServiceMain
.text:01001564 mov ebp, eax ; dll ServiceMain Function address
.text:01001566
.text:01001566 loc_1001566: ; CODE XREF: FuncServiceMain+21j
.text:01001566 ; FuncServiceMain+4Cj
.text:01001566 push ebx
.text:01001567 call ds:LeaveCriticalSection
.text:0100156D test ebp, ebp
.text:0100156F jz short loc_100157B
.text:01001571 push [esp+10h+arg_4]
.text:01001575 push [esp+14h+arg_0]
.text:01001579 call ebp
.text:0100157B
.text:0100157B loc_100157B: ; CODE XREF: FuncServiceMain+6Bj
.text:0100157B pop edi
.text:0100157C pop esi
.text:0100157D pop ebp
.text:0100157E pop ebx
.text:0100157F pop ecx
.text:01001580 retn 8
.text:01001580 FuncServiceMain endp ; sp = -8
; ============================== FuncServiceMain() end ========================================

由於svchost已經呼叫了StartServiceCtrlDispatcher來服務排程函式,因此我們在實現DLL實現時就不用了,這主要是因為一個程式只能呼叫一次StartServiceCtrlDispatcher API。但是需要用 RegisterServiceCtrlHandler 來註冊響應控制請求的函式。最後我們的DLL接收的都是unicode字串。

由於這種服務啟動後由svchost載入,不增加新的程式,只是svchost的一個DLL,而且一般進行審計時都不會去HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionSvchost 檢查服務組是否變化,就算去檢查,也不一定能發現異常,因此如果新增一個這樣的DLL後門,偽裝的好,是比較隱蔽的。

4. 安裝服務與設定
要透過svchost呼叫來啟動的服務,就一定要在HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionSvchost下有該服務名,這可以透過如下方式來實現:
1) 新增一個新的服務組,在組裡新增服務名
2) 在現有組裡新增服務名
3) 直接使用現有服務組裡的一個服務名,但本機沒有安裝的服務
4) 修改現有服務組裡的現有服務,把它的ServiceDll指向自己

其中前兩種可以被正常服務使用,如使用第1種方式,啟動其服務要建立新的svchost程式;第2種方式如果該組服務已經執行,安裝後不能立刻啟動服務,因為svchost啟動後已經把該組資訊儲存在記憶體裡,並呼叫API StartServiceCtrlDispatcher() 為該組所有服務註冊了排程處理函式,新增加的服務不能再註冊排程處理函式,需要重起計算機或者該組的svchost程式。而後兩種可能被後門使用,尤其是最後一種,沒有新增服務,只是改了登錄檔裡一項設定,從服務管理控制檯又看不出來,如果作為後門還是很隱蔽的。比如EventSystem服務,預設是指向es.dll,如果把ServiceDll改為EventSystem.dll就很難發現。

因此服務的安裝除了呼叫CreateService()建立服務之外,還需要設定服務的ServiceDll,如果使用前2種還要設定svchost的登錄檔選項,在解除安裝時也最好刪除增加的部分。

注: ImagePath 和ServiceDll 是ExpandString不是普通字串。因此如果使用.reg檔案安裝時要注意。

5. DLL服務實現
DLL程式的編寫比較簡單,只要實現一個ServiceMain()函式和一個服務控制程式,在ServiceMain()函式里用RegisterServiceCtrlHandler()註冊服務控制程式,並設定服務的執行狀態就可以了。

另外,因為此種服務的安裝除了正常的CreateService()之外,還要進行其他設定,因此最好實現安裝和解除安裝函式。

為了方便安裝,實現的程式碼提供了InstallService()函式進行安裝,這個函式可以接收服務名作為引數(如果不提供引數,就使用預設的iprip),如果要安裝的服務不在svchost的netsvcs組裡安裝就會失敗;如果要安裝的服務已經存在,安裝也會失敗;安裝成功後程式會配置服務的ServiceDll為當前Dll。提供的UninstallService()函式,可以刪除任何函式而沒有進行任何檢查。

為了方便使用rundll32.exe進行安裝,還提供了RundllInstallA()和RundllUninstallA()分別呼叫InstallService()及UninstallService()。因為rundll32.exe使用的函式原型是:
void CALLBACK FunctionName(
HWND hwnd, // handle to owner window
HINSTANCE hinst, // instance handle for the DLL
LPTSTR lpCmdLine, // string the DLL will parse
int nCmdShow // show state
);
對應的命令列是rundll32 DllName,FunctionName [Arguments]

DLL服務本身只是建立一個程式,該程式命令列就是啟動服務時提供的第一個引數,如果未指定就使用預設的svchostdll.exe。啟動服務時如果提供第二個引數,建立的程式就是和桌面互動的。大家都要知道Svchost.exe,是系統必不可少的一個程式,很多服務都會多多少少用到它,
但是我想大家也知道,由於它本身特殊性,高明的"駭客們"肯定是不會放過的,前段時間的Svchost.exe木馬風波,大家應該是記憶猶新吧,而且現在還是有很多機器裡都藏有此木馬,因為它偽裝和系統程式Svchost.exe一樣,所以很多人分不清,那個是程式,那個是木馬....
好的,還是讓我們詳盡瞭解一下Svchost.exe程式吧

1.多個服務共享一個 Svchost.exe程式利與弊
windows 系統服務分為獨立程式和共享程式兩種,在windows NT時只有伺服器管理器SCM(Services.exe)有多個共享服務,隨著系統內建服務的增加,在windows 2000中ms又把很多服務做成共享方式,由svchost.exe啟動。windows 2000一般有2個svchost程式,一個是RPCSS(Remote Procedure Call)服務程式,另外一個則是由很多服務共享的一個svchost.exe。而在windows XP中,則一般有4個以上的svchost.exe服務程式,windows 2003 server中則更多,可以看出把更多的系統內建服務以共享程式方式由svchost啟動是ms的一個趨勢。這樣做在一定程度上減少了系統資源的消耗,不過也帶來一定的不穩定因素,因為任何一個共享程式的服務因為錯誤退出程式就會導致整個程式中的所有服務都退出。另外就是有一點安全隱患,首先要介紹一下svchost.exe的實現機制。

2. Svchost原理
Svchost本身只是作為服務宿主,並不實現任何服務功能,需要Svchost啟動的服務以動態連結庫形式實現,在安裝這些服務時,把服務的可執行程式指向svchost,啟動這些服務時由svchost呼叫相應服務的動態連結庫來啟動服務。

那麼svchost如何知道某一服務是由哪個動態連結庫負責呢?這不是由服務的可執行程式路徑中的引數部分提供的,而是服務在登錄檔中的引數設定的,登錄檔中服務下邊有一個Parameters子鍵其中的ServiceDll表明該服務由哪個動態連結庫負責。並且所有這些服務動態連結庫都必須要匯出一個ServiceMain()函式,用來處理服務任務。

例如rpcss(Remote Procedure Call)在登錄檔中的位置是 HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesRpcSs,它的引數子鍵Parameters裡有這樣一項:
"ServiceDll"=REG_EXPAND_SZ:"%SystemRoot%system32 pcss.dll"
當啟動rpcss服務時,svchost就會呼叫rpcss.dll,並且執行其ServiceMain()函式執行具體服務。

既然這些服務是使用共享程式方式由svchost啟動的,為什麼系統中會有多個svchost程式呢?ms把這些服務分為幾組,同組服務共享一個svchost程式,不同組服務使用多個svchost程式,組的區別是由服務的可執行程式後邊的引數決定的。

例如rpcss在登錄檔中 HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesRpcSs 有這樣一項:
"ImagePath"=REG_EXPAND_SZ:"%SystemRoot%system32svchost -k rpcss"
因此rpcss就屬於rpcss組,這在服務管理控制檯也可以看到。

svchost的所有組和組內的所有服務都在登錄檔的如下位置: HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionSvchost,例如windows 2000共有4組rpcss、netsvcs、wugroup、BITSgroup,其中最多的就是netsvcs=REG_MULTI_SZ:EventSystem.Ias.Iprip.Irmon.Netman.Nwsapagent

.Rasauto.Rasman.Remoteaccess.SENS.Sharedaccess.Tapisrv.Ntmssvc.wzcsvc..

在啟動一個svchost.exe負責的服務時,服務管理器如果遇到可執行程式內容ImagePath已經存在於服務管理器的映象庫中,就不在啟動第2個程式svchost,而是直接啟動服務。這樣就實現了多個服務共享一個svchost程式。


3. Svchost程式碼
現在我們基本清楚svchost的原理了,但是要自己寫一個DLL形式的服務,由svchost來啟動,僅有上邊的資訊還有些問題不是很清楚。比如我們在匯出的ServiceMain()函式中接收的引數是ANSI還是Unicode?我們是否需要呼叫RegisterServiceCtrlHandler和StartServiceCtrlDispatcher來註冊服務控制及排程函式?

這些問題要透過檢視svchost程式碼獲得。下邊的程式碼是windows 2000+ service pack 4 的svchost反彙編片段,可以看出svchost程式還是很簡單的。

主函式首先呼叫ProcCommandLine()對命令列進行分析,獲得要啟動的服務組,然後呼叫SvcHostOptions()查詢該服務組的選項和服務組的所有服務,並使用一個資料結構 svcTable 來儲存這些服務及其服務的DLL,然後呼叫PrepareSvcTable() 函式建立SERVICE_TABLE_ENTRY 結構,把所有處理函式SERVICE_MAIN_FUNCTION 指向自己的一個函式FuncServiceMain(),最後呼叫API StartServiceCtrlDispatcher() 註冊這些服務的排程函式。

; =============================== Main Funcion ===========================================
.text:010010B8 public start
.text:010010B8 start proc near
.text:010010B8 push esi
.text:010010B9 push edi
.text:010010BA push offset sub_1001EBA ; lpTopLevelExceptionFilter
.text:010010BF xor edi, edi
.text:010010C1 call ds:SetUnhandledExceptionFilter
.text:010010C7 push 1 ; uMode
.text:010010C9 call ds:SetErrorMode
.text:010010CF call ds:GetProcessHeap
.text:010010D5 push eax
.text:010010D6 call sub_1001142
.text:010010DB mov eax, offset dword_1003018
.text:010010E0 push offset unk_1003000 ; lpCriticalSection
.text:010010E5 mov dword_100301C, eax
.text:010010EA mov dword_1003018, eax
.text:010010EF call ds:InitializeCriticalSection
.text:010010F5 call ds:GetCommandLineW
.text:010010FB push eax ; lpString
.text:010010FC call ProcCommandLine
.text:01001101 mov esi, eax
.text:01001103 test esi, esi
.text:01001105 jz short lab_doservice
.text:01001107 push esi
.text:01001108 call SvcHostOptions
.text:0100110D call PrepareSvcTable
.text:01001112 mov edi, eax ; SERVICE_TABLE_ENTRY returned
.text:01001114 test edi, edi
.text:01001116 jz short loc_1001128
.text:01001118 mov eax, [esi+10h]
.text:0100111B test eax, eax
.text:0100111D jz short loc_1001128
.text:0100111F push dword ptr [esi+14h] ; dwCapabilities
.text:01001122 push eax ; int
.text:01001123 call InitializeSecurity
.text:01001128
.text:01001128 loc_1001128: ; CODE XREF: start+5Ej
.text:01001128 ; start+65j
.text:01001128 push esi ; lpMem
.text:01001129 call HeapFreeMem
.text:0100112E
.text:0100112E lab_doservice: ; CODE XREF: start+4Dj
.text:0100112E test edi, edi
.text:01001130 jz ExitProgram
.text:01001136 push edi ; lpServiceStartTable
.text:01001137 call ds:StartServiceCtrlDispatcherW
.text:0100113D jmp ExitProgram
.text:0100113D start endp
; =============================== Main Funcion end ===========================================

由於svchost為該組的所有服務都註冊了svchost中的一個處理函式,因此每次啟動任何一個服務時,服務管理器SCM都會呼叫FuncServiceMain() 這個函式。這個函式使用 svcTable 查詢要啟動的服務使用的DLL,呼叫DLL匯出的ServiceMain()函式來啟動服務,然後返回。
; ============================== FuncServiceMain() ===========================================
.text:01001504 FuncServiceMain proc near ; DATA XREF: PrepareSvcTable+44o
.text:01001504
.text:01001504 arg_0 = dword ptr 8
.text:01001504 arg_4 = dword ptr 0Ch
.text:01001504
.text:01001504 push ecx
.text:01001505 mov eax, [esp+arg_4]
.text:01001509 push ebx
.text:0100150A push ebp
.text:0100150B push esi
.text:0100150C mov ebx, offset unk_1003000
.text:01001511 push edi
.text:01001512 mov edi, [eax]
.text:01001514 push ebx
.text:01001515 xor ebp, ebp
.text:01001517 call ds:EnterCriticalSection
.text:0100151D xor esi, esi
.text:0100151F cmp dwGroupSize, esi
.text:01001525 jbe short loc_1001566
.text:01001527 and [esp+10h], esi
.text:0100152B
.text:0100152B loc_100152B: ; CODE XREF: FuncServiceMain+4Aj
.text:0100152B mov eax, svcTable
.text:01001530 mov ecx, [esp+10h]
.text:01001534 push dword ptr [eax+ecx]
.text:01001537 push edi
.text:01001538 call ds:lstrcmpiW
.text:0100153E test eax, eax
.text:01001540 jz short StartThis
.text:01001542 add dword ptr [esp+10h], 0Ch
.text:01001547 inc esi
.text:01001548 cmp esi, dwGroupSize
.text:0100154E jb short loc_100152B
.text:01001550 jmp short loc_1001566
.text:01001552 ; =================================================
.text:01001552
.text:01001552 StartThis: ; CODE XREF: FuncServiceMain+3Cj
.text:01001552 mov ecx, svcTable
.text:01001558 lea eax, [esi+esi*2]
.text:0100155B lea eax, [ecx+eax*4]
.text:0100155E push eax
.text:0100155F call GetDLLServiceMain
.text:01001564 mov ebp, eax ; dll ServiceMain Function address
.text:01001566
.text:01001566 loc_1001566: ; CODE XREF: FuncServiceMain+21j
.text:01001566 ; FuncServiceMain+4Cj
.text:01001566 push ebx
.text:01001567 call ds:LeaveCriticalSection
.text:0100156D test ebp, ebp
.text:0100156F jz short loc_100157B
.text:01001571 push [esp+10h+arg_4]
.text:01001575 push [esp+14h+arg_0]
.text:01001579 call ebp
.text:0100157B
.text:0100157B loc_100157B: ; CODE XREF: FuncServiceMain+6Bj
.text:0100157B pop edi
.text:0100157C pop esi
.text:0100157D pop ebp
.text:0100157E pop ebx
.text:0100157F pop ecx
.text:01001580 retn 8
.text:01001580 FuncServiceMain endp ; sp = -8
; ============================== FuncServiceMain() end ========================================

由於svchost已經呼叫了StartServiceCtrlDispatcher來服務排程函式,因此我們在實現DLL實現時就不用了,這主要是因為一個程式只能呼叫一次StartServiceCtrlDispatcher API。但是需要用 RegisterServiceCtrlHandler 來註冊響應控制請求的函式。最後我們的DLL接收的都是unicode字串。

由於這種服務啟動後由svchost載入,不增加新的程式,只是svchost的一個DLL,而且一般進行審計時都不會去HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionSvchost 檢查服務組是否變化,就算去檢查,也不一定能發現異常,因此如果新增一個這樣的DLL後門,偽裝的好,是比較隱蔽的。

4. 安裝服務與設定
要透過svchost呼叫來啟動的服務,就一定要在HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionSvchost下有該服務名,這可以透過如下方式來實現:
1) 新增一個新的服務組,在組裡新增服務名
2) 在現有組裡新增服務名
3) 直接使用現有服務組裡的一個服務名,但本機沒有安裝的服務
4) 修改現有服務組裡的現有服務,把它的ServiceDll指向自己

其中前兩種可以被正常服務使用,如使用第1種方式,啟動其服務要建立新的svchost程式;第2種方式如果該組服務已經執行,安裝後不能立刻啟動服務,因為svchost啟動後已經把該組資訊儲存在記憶體裡,並呼叫API StartServiceCtrlDispatcher() 為該組所有服務註冊了排程處理函式,新增加的服務不能再註冊排程處理函式,需要重起計算機或者該組的svchost程式。而後兩種可能被後門使用,尤其是最後一種,沒有新增服務,只是改了登錄檔裡一項設定,從服務管理控制檯又看不出來,如果作為後門還是很隱蔽的。比如EventSystem服務,預設是指向es.dll,如果把ServiceDll改為EventSystem.dll就很難發現。

因此服務的安裝除了呼叫CreateService()建立服務之外,還需要設定服務的ServiceDll,如果使用前2種還要設定svchost的登錄檔選項,在解除安裝時也最好刪除增加的部分。

注: ImagePath 和ServiceDll 是ExpandString不是普通字串。因此如果使用.reg檔案安裝時要注意。

5. DLL服務實現
DLL程式的編寫比較簡單,只要實現一個ServiceMain()函式和一個服務控制程式,在ServiceMain()函式里用RegisterServiceCtrlHandler()註冊服務控制程式,並設定服務的執行狀態就可以了。

另外,因為此種服務的安裝除了正常的CreateService()之外,還要進行其他設定,因此最好實現安裝和解除安裝函式。

為了方便安裝,實現的程式碼提供了InstallService()函式進行安裝,這個函式可以接收服務名作為引數(如果不提供引數,就使用預設的iprip),如果要安裝的服務不在svchost的netsvcs組裡安裝就會失敗;如果要安裝的服務已經存在,安裝也會失敗;安裝成功後程式會配置服務的ServiceDll為當前Dll。提供的UninstallService()函式,可以刪除任何函式而沒有進行任何檢查。

為了方便使用rundll32.exe進行安裝,還提供了RundllInstallA()和RundllUninstallA()分別呼叫InstallService()及UninstallService()。因為rundll32.exe使用的函式原型是:
void CALLBACK FunctionName(
HWND hwnd, // handle to owner window
HINSTANCE hinst, // instance handle for the DLL
LPTSTR lpCmdLine, // string the DLL will parse
int nCmdShow // show state
);
對應的命令列是rundll32 DllName,FunctionName [Arguments]

DLL服務本身只是建立一個程式,該程式命令列就是啟動服務時提供的第一個引數,如果未指定就使用預設的svchostdll.exe。啟動服務時如果提供第二個引數,建立的程式就是和桌面互動的。

[@more@]

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/95530/viewspace-810347/,如需轉載,請註明出處,否則將追究法律責任。

相關文章