創(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,TapiSrvC:/>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 - wzcsvcC:/>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/SYSTEMSvcHostDLL: ServiceMain(3, IPRIP) called SvcHostDLL: RealService called 'cmd /k whoami' Interact SvcHostDLL: CreateProcess(cmd /k whoami) to 640C:/>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,IPRIPC:/>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.asp2003/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"http://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); } --- |
新聞熱點
疑難解答