gembin

          OSGi, Eclipse Equinox, ECF, Virgo, Gemini, Apache Felix, Karaf, Aires, Camel, Eclipse RCP

          HBase, Hadoop, ZooKeeper, Cassandra

          Flex4, AS3, Swiz framework, GraniteDS, BlazeDS etc.

          There is nothing that software can't fix. Unfortunately, there is also nothing that software can't completely fuck up. That gap is called talent.

          About Me

           

          創(chuàng)建SvcHost.exe調(diào)用的服務(wù)原理與實踐

          創(chuàng)建SvcHost.exe調(diào)用的服務(wù)原理與實踐

          1. 多個服務(wù)共享一個Svchost.exe進程利與弊

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


          2. Svchost原理

          Svchost本身只是作為服務(wù)宿主,并不實現(xiàn)任何服務(wù)功能,需要Svchost啟動的服務(wù)以動態(tài)鏈接庫形式實現(xiàn),在安裝這些服務(wù)時,把服務(wù)的可執(zhí)行程序指向svchost,啟動這些服務(wù)時由svchost調(diào)用相應(yīng)服務(wù)的動態(tài)鏈接庫來啟動服務(wù)。

          那么svchost如何知道某一服務(wù)是由哪個動態(tài)鏈接庫負(fù)責(zé)呢?這不是由服務(wù)的可執(zhí)行程序路徑中的參數(shù)部分提供的,而是服務(wù)在注冊表中的參數(shù)設(shè)置 的,注冊表中服務(wù)下邊有一個Parameters子鍵其中的ServiceDll表明該服務(wù)由哪個動態(tài)鏈接庫負(fù)責(zé)。并且所有這些服務(wù)動態(tài)鏈接庫都必須要導(dǎo) 出一個ServiceMain()函數(shù),用來處理服務(wù)任務(wù)。

          例如rpcss(Remote Procedure Call)在注冊表中的位置是 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\RpcSs,它的參數(shù)子鍵Parameters里有這樣一項:
          "ServiceDll"=REG_EXPAND_SZ:"%SystemRoot%\system32\rpcss.dll"
          當(dāng)啟動rpcss服務(wù)時,svchost就會調(diào)用rpcss.dll,并且執(zhí)行其ServiceMain()函數(shù)執(zhí)行具體服務(wù)。

          既然這些服務(wù)是使用共享進程方式由svchost啟動的,為什么系統(tǒng)中會有多個svchost進程呢?ms把這些服務(wù)分為幾組,同組服務(wù)共享一個svchost進程,不同組服務(wù)使用多個svchost進程,組的區(qū)別是由服務(wù)的可執(zhí)行程序后邊的參數(shù)決定的。

          例如rpcss在注冊表中 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\RpcSs 有這樣一項:
          "ImagePath"=REG_EXPAND_SZ:"%SystemRoot%\system32\svchost -k rpcss"
          因此rpcss就屬于rpcss組,這在服務(wù)管理控制臺也可以看到。

          svchost的所有組和組內(nèi)的所有服務(wù)都在注冊表的如下位置: 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負(fù)責(zé)的服務(wù)時,服務(wù)管理器如果遇到可執(zhí)行程序內(nèi)容ImagePath已經(jīng)存在于服務(wù)管理器的映象庫中,就不在啟動第2個進程svchost,而是直接啟動服務(wù)。這樣就實現(xiàn)了多個服務(wù)共享一個svchost進程。


          3. Svchost代碼

          現(xiàn)在我們基本清楚svchost的原理了,但是要自己寫一個DLL形式的服務(wù),由svchost來啟動,僅有上邊的信息還有些問題不是很清楚。比如 我們在導(dǎo)出的ServiceMain()函數(shù)中接收的參數(shù)是ANSI還是Unicode?我們是否需要調(diào)用 RegisterServiceCtrlHandler和StartServiceCtrlDispatcher來注冊服務(wù)控制及調(diào)度函數(shù)?

          這些問題要通過查看svchost代碼獲得。下邊的代碼是windows 2000+ service pack 4 的svchost反匯編片段,可以看出svchost程序還是很簡單的。

          主函數(shù)首先調(diào)用ProcCommandLine()對命令行進行分析,獲得要啟動的服務(wù)組,然后調(diào)用SvcHostOptions()查詢該服務(wù)組 的選項和服務(wù)組的所有服務(wù),并使用一個數(shù)據(jù)結(jié)構(gòu) svcTable 來保存這些服務(wù)及其服務(wù)的DLL,然后調(diào)用PrepareSvcTable() 函數(shù)創(chuàng)建SERVICE_TABLE_ENTRY 結(jié)構(gòu),把所有處理函數(shù)SERVICE_MAIN_FUNCTION 指向自己的一個函數(shù)FuncServiceMain(),最后調(diào)用API StartServiceCtrlDispatcher() 注冊這些服務(wù)的調(diào)度函數(shù)。

          ; =============================== 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為該組的所有服務(wù)都注冊了svchost中的一個處理函數(shù),因此每次啟動任何一個服務(wù)時,服務(wù)管理器SCM都會調(diào)用 FuncServiceMain() 這個函數(shù)。這個函數(shù)使用 svcTable 查詢要啟動的服務(wù)使用的DLL,調(diào)用DLL導(dǎo)出的ServiceMain()函數(shù)來啟動服務(wù),然后返回。

          ; ============================== 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已經(jīng)調(diào)用了StartServiceCtrlDispatcher來服務(wù)調(diào)度函數(shù),因此我們在實現(xiàn)DLL實現(xiàn)時就不用 了,這主要是因為一個進程只能調(diào)用一次StartServiceCtrlDispatcher API。但是需要用 RegisterServiceCtrlHandler 來注冊響應(yīng)控制請求的函數(shù)。最后我們的DLL接收的都是unicode字符串。

          由于這種服務(wù)啟動后由svchost加載,不增加新的進程,只是svchost的一個DLL,而且一般進行審計時都不會去 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost 檢查服務(wù)組是否變化,就算去檢查,也不一定能發(fā)現(xiàn)異常,因此如果添加一個這樣的DLL后門,偽裝的好,是比較隱蔽的。


          4. 安裝服務(wù)與設(shè)置
          要通過svchost調(diào)用來啟動的服務(wù),就一定要在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost下有該服務(wù)名,這可以通過如下方式來實現(xiàn):
          1) 添加一個新的服務(wù)組,在組里添加服務(wù)名
          2) 在現(xiàn)有組里添加服務(wù)名
          3) 直接使用現(xiàn)有服務(wù)組里的一個服務(wù)名,但本機沒有安裝的服務(wù)
          4) 修改現(xiàn)有服務(wù)組里的現(xiàn)有服務(wù),把它的ServiceDll指向自己

          其中前兩種可以被正常服務(wù)使用,如使用第1種方式,啟動其服務(wù)要創(chuàng)建新的svchost進程;第2種方式如果該組服務(wù)已經(jīng)運行,安裝后不能立刻啟動 服務(wù),因為svchost啟動后已經(jīng)把該組信息保存在內(nèi)存里,并調(diào)用API StartServiceCtrlDispatcher() 為該組所有服務(wù)注冊了調(diào)度處理函數(shù),新增加的服務(wù)不能再注冊調(diào)度處理函數(shù),需要重起計算機或者該組的svchost進程。而后兩種可能被后門使用,尤其是 最后一種,沒有添加服務(wù),只是改了注冊表里一項設(shè)置,從服務(wù)管理控制臺又看不出來,如果作為后門還是很隱蔽的。比如EventSystem服務(wù),缺省是指 向es.dll,如果把ServiceDll改為EventSystem.dll就很難發(fā)現(xiàn)。

          因此服務(wù)的安裝除了調(diào)用CreateService()創(chuàng)建服務(wù)之外,還需要設(shè)置服務(wù)的ServiceDll,如果使用前2種還要設(shè)置svchost的注冊表選項,在卸載時也最好刪除增加的部分。

          具體代碼參見后邊的附例(使用的是方法3)。

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


          5. DLL服務(wù)實現(xiàn)
          DLL程序的編寫比較簡單,只要實現(xiàn)一個ServiceMain()函數(shù)和一個服務(wù)控制程序,在ServiceMain()函數(shù)里用RegisterServiceCtrlHandler()注冊服務(wù)控制程序,并設(shè)置服務(wù)的運行狀態(tài)就可以了。

          另外,因為此種服務(wù)的安裝除了正常的CreateService()之外,還要進行其他設(shè)置,因此最好實現(xiàn)安裝和卸載函數(shù)。

          為了方便安裝,實現(xiàn)的代碼提供了InstallService()函數(shù)進行安裝,這個函數(shù)可以接收服務(wù)名作為參數(shù)(如果不提供參數(shù),就使用缺省的 iprip),如果要安裝的服務(wù)不在svchost的netsvcs組里安裝就會失敗;如果要安裝的服務(wù)已經(jīng)存在,安裝也會失敗;安裝成功后程序會配置服 務(wù)的ServiceDll為當(dāng)前Dll。提供的UninstallService()函數(shù),可以刪除任何函數(shù)而沒有進行任何檢查。

          為了方便使用rundll32.exe進行安裝,還提供了RundllInstallA()和RundllUninstallA()分別調(diào)用InstallService()及UninstallService()。因為rundll32.exe使用的函數(shù)原型是:
          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
          );
          對應(yīng)的命令行是rundll32 DllName,FunctionName [Arguments]

          DLL服務(wù)本身只是創(chuàng)建一個進程,該程序命令行就是啟動服務(wù)時提供的第一個參數(shù),如果未指定就使用缺省的svchostdll.exe。啟動服務(wù)時如果提供第二個參數(shù),創(chuàng)建的進程就是和桌面交互的。

          具體代碼參見后邊的附例8,源代碼和DLL文件請到http://www.binglesite.net下載。

          //main service process function
          void __stdcall ServiceMain( int argc, wchar_t* argv[] );
          //report service stat to the service control manager
          int TellSCM( DWORD dwState, DWORD dwExitCode, DWORD dwProgress );
          //service control handler, call back by service control manager
          void __stdcall ServiceHandler( DWORD dwCommand );
          //RealService just create a process
          int RealService(char *cmd, int bInteract);

          //Install this dll as a Service host by svchost.exe, service name is given by caller
          int InstallService(char *name);
          //unInstall a Service, be CARE FOR call this to delete a service
          int UninstallService(char *name);
          //Install this dll as a Service host by svchost.exe, used by RUNDLL32.EXE to call
          void CALLBACK RundllInstallA(HWND hwnd, HINSTANCE hinst, char *param, int nCmdShow);
          //unInstall a Service used by RUNDLL32.EXE to call, be CARE FOR call this to delete a service
          void CALLBACK RundllUninstallA(HWND hwnd, HINSTANCE hinst, char *param, int nCmdShow);

          //output the debug infor into log file(or stderr if a console program call me) & DbgPrint
          void OutputString( char *lpFmt, ... );


          6. 代碼使用
          C:\>tlist -s
          0 System Process
          8 System
          240 services.exe Svcs: Browser,Dhcp,dmserver,Dnscache,Eventlog,lanmanserver,lanmanworkstation, LmHosts,PlugPlay,ProtectedStorage,TrkWks,Wmi
          504 svchost.exe Svcs: RpcSs
          1360 svchost.exe Svcs: EventSystem,Netman,RasMan,SENS,TapiSrv

          C:\>rundll32 svchostdll.dll,RundllInstall abcd
          SvcHostDLL: DllMain called DLL_PROCESS_ATTACH
          you specify service name not in Svchost\netsvcs, must be one of following:
          - EventSystem
          - Ias
          - Iprip
          - Irmon
          - Netman
          - Nwsapagent
          - Rasauto
          - Rasman
          - Remoteaccess
          - SENS
          - Sharedaccess
          - Tapisrv
          - Ntmssvc
          - wzcsvc

          C:\>rundll32 svchostdll.dll,RundllInstall IPRIP
          SvcHostDLL: DllMain called DLL_PROCESS_ATTACH
          CreateService(IPRIP) SUCCESS. Config it
          Config service IPRIP ok.

          C:\>sc start iprip "cmd /k whoami" 1
          NT AUTHORITY\SYSTEM

          SvcHostDLL: ServiceMain(3, IPRIP) called
          SvcHostDLL: RealService called 'cmd /k whoami' Interact
          SvcHostDLL: CreateProcess(cmd /k whoami) to 640

          C:\>tlist -s
          0 System Process
          8 System
          240 services.exe Svcs: Browser,Dhcp,dmserver,Dnscache,Eventlog,lanmanserver,lanmanworkstation, LmHosts,PlugPlay,ProtectedStorage,TrkWks,Wmi
          504 svchost.exe Svcs: RpcSs
          640 cmd.exe Title: C:\WINNT\System32\cmd.exe
          1360 svchost.exe Svcs: EventSystem,Netman,RasMan,SENS,TapiSrv,IPRIP

          C:\>net stop iprip
          The IPRIP service was stopped successfully.

          C:\>rundll32 svchostdll.dll,RundllUninstall iprip
          DeleteService(IPRIP) SUCCESS.


          7. 參考

          Platform SDK: Tools - Rundll32
          1) Inside Win32 Services, Part 2 by: Mark Russinovich, at: http://www.winnetmag.com/Articles/Index.cfm?ArticleID=8943&pg=3
          2) Platform SDK: Tools - Rundll32, at: http://msdn.microsoft.com/library/en-us/tools/tools/rundll32.asp

          2003/8


          8. 代碼
          // SvcHostDLL.cpp : Demo for a service dll used by svchost.exe to host it.
          //
          // for detail comment see articles.
          // by bingle_at_email.com.cn
          // www.BingleSite.net
          //
          /* save following as a .def file to export function, only ServiceMain is needed.
          other used to install & uninstall service.
          or use /EXPORT: link option to export them.

          EXPORTS
          ServiceMain
          InstallService
          UninstallService
          RundllUninstallA
          RundllInstallA
          */
          /*
          To compile & link:
          cl /MD /GX /LD svchostdll.cpp /link advapi32.lib /DLL /base:0x71000000 /export:ServiceMain /EXPORT:RundllUninstallA /EXPORT:RundllInstallA /EXPORT:InstallService /EXPORT:UninstallService
          */

          //
          // Articles:
          // 1. HOWTO Create a service dll used by svchost.exe by bingle, at: http://www.BingleSite.net/article/svchost-dll-service.html
          // 2. Inside Win32 Services, Part 2 by: Mark Russinovich, at: http://www.winnetmag.com/Articles/Index.cfm?ArticleID=8943&pg=3
          // 3. Platform SDK: Tools - Rundll32, at: http://msdn.microsoft.com/library/en-us/tools/tools/rundll32.asp

          #include <stdio.h>
          #include <time.h>
          #include <assert.h>
          #include <windows.h>

          #define DEFAULT_SERVICE "IPRIP"
          #define MY_EXECUTE_NAME "SvcHostDLL.exe"

          //main service process function
          void __stdcall ServiceMain( int argc, wchar_t* argv[] );
          //report service stat to the service control manager
          int TellSCM( DWORD dwState, DWORD dwExitCode, DWORD dwProgress );
          //service control handler, call back by service control manager
          void __stdcall ServiceHandler( DWORD dwCommand );
          //RealService just create a process
          int RealService(char *cmd, int bInteract);

          //Install this dll as a Service host by svchost.exe, service name is given by caller
          int InstallService(char *name);
          //unInstall a Service, be CARE FOR call this to delete a service
          int UninstallService(char *name);
          //Install this dll as a Service host by svchost.exe, used by RUNDLL32.EXE to call
          void CALLBACK RundllInstallA(HWND hwnd, HINSTANCE hinst, char *param, int nCmdShow);
          //unInstall a Service used by RUNDLL32.EXE to call, be CARE FOR call this to delete a service
          void CALLBACK RundllUninstallA(HWND hwnd, HINSTANCE hinst, char *param, int nCmdShow);

          //output the debug infor into log file(or stderr if a console program call me) & DbgPrint
          void OutputString( char *lpFmt, ... );


          //dll module handle used to get dll path in InstallService
          HANDLE hDll = NULL;
          //Service HANDLE & STATUS used to get service state
          SERVICE_STATUS_HANDLE hSrv;
          DWORD dwCurrState;


          BOOL APIENTRY DllMain( HANDLE hModule,
          DWORD ul_reason_for_call,
          LPVOID lpReserved
          )
          {
          switch (ul_reason_for_call)
          {
          case DLL_PROCESS_ATTACH:
          hDll = hModule;
          #ifdef _DEBUG
          AllocConsole();
          OutputString("SvcHostDLL: DllMain called DLL_PROCESS_ATTACH");
          break;

          case DLL_THREAD_ATTACH:
          OutputString("SvcHostDLL: DllMain called DLL_THREAD_ATTACH");
          case DLL_THREAD_DETACH:
          OutputString("SvcHostDLL: DllMain called DLL_THREAD_DETACH");
          case DLL_PROCESS_DETACH:
          TellSCM( SERVICE_STOP_PENDING, 0, 0 );
          Sleep(1500);
          TellSCM( SERVICE_STOPPED, 0, 0 );
          OutputString("SvcHostDLL: DllMain called DLL_PROCESS_DETACH");
          #endif
          break;
          }

          return TRUE;
          }


          void __stdcall ServiceMain( int argc, wchar_t* argv[] )
          {
          // DebugBreak();
          char svcname[256];
          strncpy(svcname, (char*)argv[0], sizeof svcname); //it's should be unicode, but if it's ansi we do it well
          wcstombs(svcname, argv[0], sizeof svcname);
          OutputString("SvcHostDLL: ServiceMain(%d, %s) called", argc, svcname);

          hSrv = RegisterServiceCtrlHandler( svcname, (LPHANDLER_FUNCTION)ServiceHandler );
          if( hSrv == NULL )
          {
          OutputString("SvcHostDLL: RegisterServiceCtrlHandler %S failed", argv[0]);
          return;
          }else FreeConsole();

          TellSCM( SERVICE_START_PENDING, 0, 1 );
          TellSCM( SERVICE_RUNNING, 0, 0 );

          // call Real Service function noew
          if(argc > 1)
          strncpy(svcname, (char*)argv[1], sizeof svcname),
          wcstombs(svcname, argv[1], sizeof svcname);
          RealService(argc > 1 ? svcname : MY_EXECUTE_NAME, argc > 2 ? 1 : 0);

          do{
          Sleep(10);//not quit until receive stop command, otherwise the service will stop
          }while(dwCurrState != SERVICE_STOP_PENDING && dwCurrState != SERVICE_STOPPED);

          OutputString("SvcHostDLL: ServiceMain done");
          return;
          }

          int TellSCM( DWORD dwState, DWORD dwExitCode, DWORD dwProgress )
          {
          SERVICE_STATUS srvStatus;
          srvStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
          srvStatus.dwCurrentState = dwCurrState = dwState;
          srvStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
          srvStatus.dwWin32ExitCode = dwExitCode;
          srvStatus.dwServiceSpecificExitCode = 0;
          srvStatus.dwCheckPoint = dwProgress;
          srvStatus.dwWaitHint = 3000;
          return SetServiceStatus( hSrv, &srvStatus );
          }

          void __stdcall ServiceHandler( DWORD dwCommand )
          {
          // not really necessary because the service stops quickly
          switch( dwCommand )
          {
          case SERVICE_CONTROL_STOP:
          TellSCM( SERVICE_STOP_PENDING, 0, 1 );
          OutputString("SvcHostDLL: ServiceHandler called SERVICE_CONTROL_STOP");
          Sleep(10);
          TellSCM( SERVICE_STOPPED, 0, 0 );
          break;
          case SERVICE_CONTROL_PAUSE:
          TellSCM( SERVICE_PAUSE_PENDING, 0, 1 );
          OutputString("SvcHostDLL: ServiceHandler called SERVICE_CONTROL_PAUSE");
          TellSCM( SERVICE_PAUSED, 0, 0 );
          break;
          case SERVICE_CONTROL_CONTINUE:
          TellSCM( SERVICE_CONTINUE_PENDING, 0, 1 );
          OutputString("SvcHostDLL: ServiceHandler called SERVICE_CONTROL_CONTINUE");
          TellSCM( SERVICE_RUNNING, 0, 0 );
          break;
          case SERVICE_CONTROL_INTERROGATE:
          OutputString("SvcHostDLL: ServiceHandler called SERVICE_CONTROL_INTERROGATE");
          TellSCM( dwCurrState, 0, 0 );
          break;
          case SERVICE_CONTROL_SHUTDOWN:
          OutputString("SvcHostDLL: ServiceHandler called SERVICE_CONTROL_SHUTDOWN");
          TellSCM( SERVICE_STOPPED, 0, 0 );
          break;
          }
          }


          //RealService just create a process
          int RealService(char *cmd, int bInteract)
          {
          OutputString("SvcHostDLL: RealService called '%s' %s", cmd, bInteract ? "Interact" : "");
          STARTUPINFO si = {0};
          PROCESS_INFORMATION pi;
          si.cb = sizeof si;
          if(bInteract) si.lpDesktop = "WinSta0\\Default";
          if(!CreateProcess(NULL, cmd, NULL, NULL, false, 0, NULL, NULL, &si, &pi))
          OutputString("SvcHostDLL: CreateProcess(%s) error:%d", cmd, GetLastError());
          else OutputString("SvcHostDLL: CreateProcess(%s) to %d", cmd, pi.dwProcessId);

          return 0;
          }


          int InstallService(char *name)
          {
          // Open a handle to the SC Manager database.
          int rc = 0;
          HKEY hkRoot = HKEY_LOCAL_MACHINE, hkParam = 0;
          SC_HANDLE hscm = NULL, schService = NULL;

          try{
          char buff[500];
          char *svcname = DEFAULT_SERVICE;
          if(name && name[0]) svcname = name;

          //query svchost setting
          char *ptr, *pSvchost = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Svchost";
          rc = RegOpenKeyEx(hkRoot, pSvchost, 0, KEY_QUERY_VALUE, &hkRoot);
          if(ERROR_SUCCESS != rc)
          {
          OutputString("RegOpenKeyEx(%s) KEY_QUERY_VALUE error %d.", pSvchost, rc);
          throw "";
          }

          DWORD type, size = sizeof buff;
          rc = RegQueryValueEx(hkRoot, "netsvcs", 0, &type, (unsigned char*)buff, &size);
          RegCloseKey(hkRoot);
          SetLastError(rc);
          if(ERROR_SUCCESS != rc)
          throw "RegQueryValueEx(Svchost\\netsvcs)";

          for(ptr = buff; *ptr; ptr = strchr(ptr, 0)+1)
          if(stricmp(ptr, svcname) == 0) break;

          if(*ptr == 0)
          {
          OutputString("you specify service name not in Svchost\\netsvcs, must be one of following:");
          for(ptr = buff; *ptr; ptr = strchr(ptr, 0)+1)
          OutputString(" - %s", ptr);
          throw "";
          }

          //install service
          hscm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
          if (hscm == NULL)
          throw "OpenSCManager()";

          char *bin = "%SystemRoot%\\System32\\svchost.exe -k netsvcs";

          schService = CreateService(
          hscm, // SCManager database
          svcname, // name of service
          NULL, // service name to display
          SERVICE_ALL_ACCESS, // desired access
          SERVICE_WIN32_SHARE_PROCESS, // service type
          SERVICE_AUTO_START, // start type
          SERVICE_ERROR_NORMAL, // error control type
          bin, // service's binary
          NULL, // no load ordering group
          NULL, // no tag identifier
          NULL, // no dependencies
          NULL, // LocalSystem account
          NULL); // no password

          if (schService == NULL)
          {
          OutputString("CreateService(%s) error %d", svcname, rc = GetLastError());
          throw "";
          }
          OutputString("CreateService(%s) SUCCESS. Config it", svcname);

          CloseServiceHandle(schService);
          CloseServiceHandle(hscm);

          //config service
          hkRoot = HKEY_LOCAL_MACHINE;
          strncpy(buff, "SYSTEM\\CurrentControlSet\\Services\\", sizeof buff);
          strncat(buff, svcname, 100);
          rc = RegOpenKeyEx(hkRoot, buff, 0, KEY_ALL_ACCESS, &hkRoot);
          if(ERROR_SUCCESS != rc)
          {
          OutputString("RegOpenKeyEx(%s) KEY_SET_VALUE error %d.", svcname, rc);
          throw "";
          }

          rc = RegCreateKey(hkRoot, "Parameters", &hkParam);
          SetLastError(rc);
          if(ERROR_SUCCESS != rc)
          throw "RegCreateKey(Parameters)";

          if(!GetModuleFileName(HMODULE(hDll), buff, sizeof buff))
          throw "GetModuleFileName() get dll path";

          rc = RegSetValueEx(hkParam, "ServiceDll", 0, REG_EXPAND_SZ, (unsigned char*)buff, strlen(buff)+1);
          SetLastError(rc);
          if(ERROR_SUCCESS != rc)
          throw "RegSetValueEx(ServiceDll)";

          OutputString("Config service %s ok.", svcname);
          }catch(char *str)
          {
          if(str && str[0])
          {
          rc = GetLastError();
          OutputString("%s error %d", str, rc);
          }
          }

          RegCloseKey(hkRoot);
          RegCloseKey(hkParam);
          CloseServiceHandle(schService);
          CloseServiceHandle(hscm);

          return rc;
          }

          /*
          used to install by rundll32.exe
          Platform SDK: Tools - Rundll32
          The Run DLL utility (Rundll32.exe) included in Windows enables you to call functions exported from a 32-bit DLL. These functions must have the following syntax:
          */
          void CALLBACK RundllInstallA(
          HWND hwnd, // handle to owner window
          HINSTANCE hinst, // instance handle for the DLL
          char *param, // string the DLL will parse
          int nCmdShow // show state
          )
          {
          InstallService(param);
          }


          int UninstallService(char *name)
          {
          int rc = 0;
          SC_HANDLE schService;
          SC_HANDLE hscm;

          __try{
          hscm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
          if (hscm == NULL)
          {
          OutputString("OpenSCManager() error %d", rc = GetLastError() );
          return rc;
          }

          char *svcname = DEFAULT_SERVICE;
          if(name && name[0]) svcname = name;

          schService = OpenService(hscm, svcname, DELETE);
          if (schService == NULL)
          {
          OutputString("OpenService(%s) error %d", svcname, rc = GetLastError() );
          return rc;
          }

          if (!DeleteService(schService) )
          {
          OutputString("OpenService(%s) error %d", svcname, rc = GetLastError() );
          return rc;
          }

          OutputString("DeleteService(%s) SUCCESS.", svcname);
          }__except(1)
          {
          OutputString("Exception Catched 0x%X", GetExceptionCode());
          }

          CloseServiceHandle(schService);
          CloseServiceHandle(hscm);
          return rc;
          }

          /*
          used to uninstall by rundll32.exe
          Platform SDK: Tools - Rundll32
          The Run DLL utility (Rundll32.exe) included in Windows enables you to call functions exported from a 32-bit DLL. These functions must have the following syntax:
          */
          void CALLBACK RundllUninstallA(
          HWND hwnd, // handle to owner window
          HINSTANCE hinst, // instance handle for the DLL
          char *param, // string the DLL will parse
          int nCmdShow // show state
          )
          {
          UninstallService(param);
          }

          //output the debug infor into log file & DbgPrint
          void OutputString( char *lpFmt, ... )
          {
          char buff[1024];
          va_list arglist;
          va_start( arglist, lpFmt );
          _vsnprintf( buff, sizeof buff, lpFmt, arglist );
          va_end( arglist );

          DWORD len;
          HANDLE herr = GetStdHandle(STD_OUTPUT_HANDLE);
          if(herr != INVALID_HANDLE_VALUE)
          {
          WriteFile(herr, buff, strlen(buff), &len, NULL);
          WriteFile(herr, "\r\n", 2, &len, NULL);
          }else
          {
          FILE *fp = fopen("SvcHost.DLL.log", "a");
          if(fp)
          {
          char date[20], time[20];
          fprintf(fp, "%s %s - %s\n", _strdate(date), _strtime(time), buff);
          if(!stderr) fclose(fp);
          }
          }

          OutputDebugString(buff);
          }
          ---

          posted on 2008-09-27 14:32 gembin 閱讀(908) 評論(1)  編輯  收藏 所屬分類: 其他

          評論

          # re: 創(chuàng)建SvcHost.exe調(diào)用的服務(wù)原理與實踐 2009-01-28 13:25 單飛

          這你都有研究啊,神仙啊你。  回復(fù)  更多評論   

          導(dǎo)航

          統(tǒng)計

          常用鏈接

          留言簿(6)

          隨筆分類(440)

          隨筆檔案(378)

          文章檔案(6)

          新聞檔案(1)

          相冊

          收藏夾(9)

          Adobe

          Android

          AS3

          Blog-Links

          Build

          Design Pattern

          Eclipse

          Favorite Links

          Flickr

          Game Dev

          HBase

          Identity Management

          IT resources

          JEE

          Language

          OpenID

          OSGi

          SOA

          Version Control

          最新隨筆

          搜索

          積分與排名

          最新評論

          閱讀排行榜

          評論排行榜

          free counters
          主站蜘蛛池模板: 仪征市| 天柱县| 永州市| 崇左市| 诏安县| 温泉县| 周口市| 新巴尔虎左旗| 克拉玛依市| 棋牌| 沂水县| 临泉县| 广宁县| 左云县| 林甸县| 新泰市| 鸡西市| 滦平县| 上蔡县| 哈巴河县| 东丽区| 利川市| 蒲城县| 讷河市| 桃江县| 横峰县| 房产| 怀安县| 大兴区| 平山县| 枝江市| 乡城县| 鞍山市| 九台市| 兴隆县| 阜平县| 若羌县| 淮安市| 常熟市| 大城县| 壤塘县|