查看單個文章
舊 2006-05-20, 06:06 PM   #1
psac
榮譽會員
 
psac 的頭像
榮譽勳章
UID - 3662
在線等級: 級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時
註冊日期: 2002-12-07
住址: 木柵市立動物園
文章: 17381
現金: 5253 金幣
資產: 33853 金幣
預設 新增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_MACHINE\SYSTEM\CurrentControlSet\Services\RpcSs,它的參數子鍵Parameters裡有這樣一項:

"ServiceDll"=REG_EXPAND_SZ:"%SystemRoot%\system32\rpcss.dll"

當啟動rpcss服務時,svchost就會使用rpcss.dll,並且執行其ServiceMain()函數執行具體服務。

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

例如rpcss在註冊表中 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\RpcSs 有這樣一項:

"ImagePath"=REG_EXPAND_SZ:"%SystemRoot%\system32\svchost -k rpcss"

因此rpcss就屬於rpcss組,這在服務管理控制台也可以看到。

svchost的所有組和組內的所有服務都在註冊表的如下位置: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost,例如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 ========================================
__________________
http://bbsimg.qianlong.com/upload/01/08/29/68/1082968_1136014649812.gif
psac 目前離線  
送花文章: 3, 收花文章: 1631 篇, 收花: 3205 次
向 psac 送花的會員:
justgamela (2007-09-06)
感謝您發表一篇好文章