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

           

          創建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 ========================================


          由于svchost已經調用了StartServiceCtrlDispatcher來服務調度函數,因此我們在實現DLL實現時就不用 了,這主要是因為一個進程只能調用一次StartServiceCtrlDispatcher API。但是需要用 RegisterServiceCtrlHandler 來注冊響應控制請求的函數。最后我們的DLL接收的都是unicode字符串。

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


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

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

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

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

          注: 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。啟動服務時如果提供第二個參數,創建的進程就是和桌面交互的。

          具體代碼參見后邊的附例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 閱讀(891) 評論(1)  編輯  收藏 所屬分類: 其他

          評論

          # re: 創建SvcHost.exe調用的服務原理與實踐 2009-01-28 13:25 單飛

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

          導航

          統計

          常用鏈接

          留言簿(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
          主站蜘蛛池模板: 昆明市| 田东县| 香河县| 临城县| 石柱| 玉环县| 丰镇市| 丰原市| 黄山市| 龙陵县| 昭平县| 开远市| 阿克陶县| 京山县| 西丰县| 肥东县| 淮安市| 渝北区| 三亚市| 九龙县| 洪雅县| 柳江县| 济南市| 嘉祥县| 昌平区| 昌都县| 商河县| 综艺| 祥云县| 延吉市| 葵青区| 屏南县| 义乌市| 腾冲县| 秀山| 象山县| 南澳县| 陇川县| 白朗县| 石河子市| 当涂县|