Windows服務程序的原理及實現(服務分為WIN32服務和系統服務)


今天給大家講下怎樣做一個服務程序...本來是想詳細講的,不過寫着寫着累得要命..很多

 

地方就沒詳細...不過代碼我加了點注...如果還有一些不明白的自己查下MSDN......便宜

 

環境,,VC++6.0...代碼有倆段,一段是服務程序的..另一段是安裝服務程序的...這個程序

 

的功能是開機發出滴滴聲....安裝成功后自己點啟動...下次開機就自動起動了....

load.exe的實現是比較簡單,本來想弄個匯編版本...不過真的累...就算了..這里一個服

 

務的基本框架就完成了...剩下的只是添加你自己的功能代碼...

怎樣安裝::我在C盤建一個名為sysnap的文件夾..里面放着倆個EXE..一個是sv.exe 另一

 

個是svload.exe 然后cmd到這個文件夾,輸入svload.exe則服務安裝成功...開機后就有一

 

個服務進程sv.exe...這樣我們就有了一個名為sysnap的服務..在注冊表里可以找到相關

 

信息....當然你可以把服務名改為你自己的....svload.exe的代碼很簡單..我沒有注

 

釋,,,,如果需要我回帖再注..我現在累

 

一個服務的基本框架

 

1感性認識什么是服務

2用INF文件安裝服務

3關於服務的一些基本理論知識

4一個服務的創建流程和基本組成

5詳說各個基本函數

6所用到的一些數據結構和API說明

7開始我們的第一個服務,完整代碼

8運行我們的服務,完整代碼

 

1感性認識什么是服務

在運行框輸入SERVICES.MSC..看到沒,,這些都是windows的服務..里面有一些是windows自己的,有一些是第三方服務,,比如我們的殺毒軟件大部分都有一個服務

那服務有什么用呢,..讓我們先看一下服務的定義吧

服務:是一種應用程序類型,它在后台運行。

可見如果我們把程序做成服務后也可以照樣運行起來...如果是設置成手動的,那開機后我們的服務程序就自動運行起來...所以很多木馬都搞成服務啟動,,這比在注冊表里什么RUN

要隱蔽多了

系統有倆種服務.一種叫win32服務,他運行在用戶態,對應的映像文件是.EXE或.DLL..我們這里講的就是win32服務

另外一種叫系統服務,它運行在內核態,對應的映像文件是.SYS也就是驅動程序..其實這倆個概念現在也沒必要細分了吧..區別除了運行態不同外,另外還有一個區別,就是在注冊表中除了在HKEY_LOCAL_MACHINE/SYSTE/CurrentControlSet/Services下都有一個服務名外,系統服務還多了一個設備硬健HKEY_LOCAL_MACHINESystem/CurrentControlSet/Enum子鍵,因為是驅動程序嘛,在刪除一些內核木馬時.這個建默認是無法刪除的,因為需要SYSTEM權限..不過右鍵->權限->添加->高級就可以搞定

 

2用INF文件安裝服務

現在我們就用.INF文件來把我們的EXE程序變成服務..隨開機的運行而運行,這個EXE文件我是用我前幾天在吧里發的哪個小程序,依然是輸出電腦的用戶名...在C盤建一個文件夾,命名為SYSNAP..把我們的程序sysnap.exe放進去

打開筆記本,那下面代碼寫進去,保存為sysnap.inf

[Version]

Signature="$WINDOWS NT$"

[DefaultInstall.Services]

AddService=sysnap,,My_AddService_Name

[My_AddService_Name]

DisplayName=sysnap

Description=顯示電腦用戶名

ServiceType=0x10

StartType=2

ErrorControl=0

ServiceBinary=C:/SYSNAP/sysnap.exe

 

說明一下

1、ServiceType服務類型:0x10為獨立進程服務,0x20為共享進程服務(比如svchost)

2、StartType啟動類型:0 系統引導時加載,1 OS初始化時加載,2 由SCM(服務控制管理器)自動啟動,3 手動啟動,4 禁用(注意,0和1只能用於驅動程序)

3、ErrorControl錯誤控制:0 忽略,1 繼續並警告,2 切換到LastKnownGood的設置,3藍屏

4、ServiceBinary服務程序位置

 

好了..在CMD下運行命令

 rundll32.exe setupapi,InstallHinfSection DefaultInstall 128 c:/SYSNAP/sysnap.inf 

這樣就安裝了一個名為sysnap的服務,是不是很簡單呢..當然后.INF文件安裝服務有時候是不太好,,,下面我們就通過編成來實現,,,也是主要的目的..先講一下理論吧

 

3關於服務的一些基本理論知識

WIN32服務由三部分組成:服務應用程序、服務控制程序SCP,和服務控制管理器SCM。

服務應用程序:就是接下來我們要實現的程序,他是一個EXE文件..也可以是.DLL,這里我們是sysnap.exe

服務控制程序:控制服務應用程序的功能塊,也是服務應用程序同服務管理器(SCM)之間的橋梁

服務控制管理器:負責加載和初始化AUTO_ATRT的服務程序,SCM維護着注冊表中的服務數據庫,位於:HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services。其下的子鍵就是安裝的服務和驅動服務。每個子鍵的名稱就是服務名,當安裝的時候由服務安全程序的CreateService 函數指定

還有就不寫了..可以參考<<inside windows 2000>>他們講的已經足夠好了...,不夠里面涉及的東西很多.建議看下后面的程序...不懂的再去參考他們比較好..因為這個程序不要求你全要理解他們

 

4一個服務的創建流程一個服務的創建流程和基本組成

A編寫我們的main()函數,該函數必須在30秒內調用StartServiceCtrlDispatcher 函數..這樣我們的EXE文件就在SCM里注冊了

 

B編寫我們的ServiceMain(),ServiceMain()要立即調用RegisterServiceCtrlHandler 注冊服務控制處理函數,然后用RegisterServiceCtrlHandler返回的句柄向SCM發送狀態信息,接着開始完成實際的服務任務和工作線程,一旦線程開始,ServiceMain()就等待一個事件的發生,知道服務停止,ServiceMain()才返回

 

C編寫我們的控制處理器ServiceCtrlHandler,接受來自SCM的請求並作出反應,其實請求一般是下面幾個值(也可以自己定義)

停止服務:SERVICE_CONTROL_STOP 

暫停服務:SERVICE_CONTROL_PAUSE 

恢復被暫停的服務:SERVICE_CONTROL_CONTINUE 

返回服務的更新狀態信息:SERVICE_CONTROL_INTERROGATE

 

D編寫這個服務要實現的功能函數,也就是我們這個服務要完成什么功能,這里依然是輸出電腦用戶名

可見,一個完整的服務包括:

main():他告訴SCM 關於ServiceMain()的一些信息

ServiceMain():開始ServiceThread(),告訴SCM關於控制處理器的一些信息

ServiceCtrlHandler:接受來自SCM的請求並做出響應

InitThread():由ServiceMain()打開,執行我們的任務,,就是建立一個線程來運行我們的任務

 

5詳說各個基本函數

A main()

SCM是一個管理系統所有服務的進程,當SCM啟動某個服務時,它等待某個進程的主線程來調用StartServiceCtrlDispatcher(),這樣把調用進程的住線程轉換為控制分配器,控制分配器啟動一個新線程,新線程運行分配表里每個服務的ServiceMain()

 

B ServiceMain()

是服務的入口點.它運行在一個單獨的線程中,主要是為服務注冊控制處理器,它指示控制分配器調用ServiceCtrlHandler()來處理SCM的請求,注冊完成后將返回一個句柄

通過調用SetServiceStatus,用這個句柄和SERVICE_STATUS向SCM報告服務狀態,,,因為這樣的動作經常發生,,,所以我們把這個過程寫成一個函數ReportStatusToSCMgr()

RegisterServiceCtrlHandler(strServiceName, (LPHANDLER_FUNCTION)ServiceCtrlHandler);

接着調用ReportStatusToSCMgr()向SCM報告服務狀態 ReportStatusToSCMgr();

創建一個事件,在函數的最后將調用該事件來保持函數的運行知道SCM發出停止請求才返回 CreateEvent();

創建一個線程來運行我們的服務函數 sysnap();

 

最后ServiceThread()完成后返回ServiceMain(),ServiceMain()調用 WaitForSingleObject()

 

C ServiceCtrlHandler()

檢查SCM發送了什么請求並且做出反應...當用戶關閉系統,所有的控制處理要調用SetServiceStatus設置SERVICE_ACCEPT_SHUTDOWN控制碼去接收SERVICE_CONTROL_SHUTDOWN控制碼,如果服務需要時間去清除,它可以發送 STOP_PENDING狀態消息,連同一個等待時間,這樣,服務控制器在報告系統服務關閉之前才知道應該待多長時間,無論如何,都有一個服務控制器需要等待的時間,防止服務停留在shutdown狀態。要改變這個時間限制,可以修改HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control中的WaitToKillServiceTimeout值。 

 

switch(nControlCode)

{

case SERVICE_CONTROL_SHUTDOWN:

case SERVICE_CONTROL_STOP:

nServiceCurrentStatus=SERVICE_STOP_PENDING;

success=ReportStatusToSCMgr(SERVICE_STOP_PENDING,NO_ERROR,0,1,3000);

KillService();

return;

default:

break;

}

 

6所用到的一些數據結構和API說明

本來想寫,,算了.自己查MSDN///累....下面代碼我會加點注射

 

下面就直接代碼吧...可以自己編譯

 

sv.exe的代碼

 

#include <stdio.h>

#include <windows.h>

#include <winsvc.h>

//定義一些全局變量和函數

void ServiceMain(DWORD argc, LPTSTR *argv); 

void ServiceCtrlHandler(DWORD dwControlCode);

//SCM報告服務狀態信息

BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode,

 DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint,

 DWORD dwWaitHint);

BOOL InitThread(); //創建線程來運行我們的任務

DWORD sysnap(LPDWORD param); //我們這個服務所要完成的任務

HANDLE hServiceThread; 

void KillService(); 

 

char *strServiceName = "sysnap"; ////標識服務的內部名

 

SERVICE_STATUS_HANDLE nServiceStatusHandle; //存儲調用RegisterServiceCtrlHandler返回的句柄

HANDLE killServiceEvent;

BOOL nServiceRunning;

DWORD nServiceCurrentStatus;

 

void main(int argc, char* argv[])

{

// SERVICE_TABLE_ENTRY 結構類型的數組,他包含了調用進程所提供的每個服務的入口函數和字符串名。表中的最后一個元素必須為 NULL,指明入口表結束

SERVICE_TABLE_ENTRY servicetable[]=

{

{strServiceName,(LPSERVICE_MAIN_FUNCTION)ServiceMain},

{NULL,NULL}

};

BOOL success;

 // StartServiceCtrlDispatcher 函數負責把程序主線程連接到服務控制管理程序

success=StartServiceCtrlDispatcher(servicetable);

if(!success)

{

printf("fialed!");

}

}

 

void ServiceMain(DWORD argc, LPTSTR *argv)

{

BOOL success;

//把ServiceCtrlHandler注冊為服務控制器,接受來自SCM的請求並做出處理,

nServiceStatusHandle=RegisterServiceCtrlHandler(strServiceName,

(LPHANDLER_FUNCTION)ServiceCtrlHandler);

//判斷是否注冊成功,否則返回

if(!nServiceStatusHandle)

{

return;

}

//注冊成功后向SCM報告服務狀態信息,因為服務還沒初始化完成,所以當前服務狀態為SERVICE_START_PENDING

success=ReportStatusToSCMgr(SERVICE_START_PENDING,NO_ERROR,0,1,3000);

if(!success)

{

return;

}

//創建一個事件,在函數的最后將用該事件來保持函數的運行直到SCM發出停止請求才返回

killServiceEvent=CreateEvent(0,TRUE,FALSE,0);

if(killServiceEvent==NULL)

{

return;

}

//向SCM報告服務狀態信息

success=ReportStatusToSCMgr(SERVICE_START_PENDING,NO_ERROR,0,2,1000);

if(!success)

{

return;

}

//InitThread()創建一個線程來運行我們的sysnap()函數

success=InitThread();

if(!success)

{

return;

}

//我們的服務開始運行任務了,當前狀態設置為SERVICE_RUNNING

nServiceCurrentStatus=SERVICE_RUNNING;

success=ReportStatusToSCMgr(SERVICE_RUNNING,NO_ERROR,0,0,0);

if(!success)

{

return;

}

//sysnap()函數運行完了之后返回ServiceMain(),ServiceMain()調用WaitForSingleObject,因為服務被停止之前ServiceMain()不會結束

WaitForSingleObject(killServiceEvent,INFINITE);

CloseHandle(killServiceEvent);

}

 

 

//向SCM報告服務狀態信息,可以說是更新信息吧,它接受的參數都是SERVICE_STATUS結構成員

BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode,

 DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint,

 DWORD dwWaitHint)

{

BOOL success;

SERVICE_STATUS nServiceStatus; //定義一個SERVICE_STATUS類型結構nServiceStatus

nServiceStatus.dwServiceType=SERVICE_WIN32_OWN_PROCESS; //表示我們的服務是獨占一個進程的服務

nServiceStatus.dwCurrentState=dwCurrentState; //當前服務狀態

//

if(dwCurrentState==SERVICE_START_PENDING) 

{

nServiceStatus.dwControlsAccepted=0; //服務的初始化沒有完成

}

else

{

nServiceStatus.dwControlsAccepted=SERVICE_ACCEPT_STOP //通知 SCM 服務接受哪個域。這里允許 STOP 和SHUTDOWN 請求

|SERVICE_ACCEPT_SHUTDOWN;

}

//dwServiceSpecificExitCode在你終止服務並報告退出細節時很有用。初始化服務時並不退出,因此值為 0

if(dwServiceSpecificExitCode==0)

{

nServiceStatus.dwWin32ExitCode=dwWin32ExitCode;

}

else

{

nServiceStatus.dwWin32ExitCode=ERROR_SERVICE_SPECIFIC_ERROR;

}

nServiceStatus.dwServiceSpecificExitCode=dwServiceSpecificExitCode;

//

nServiceStatus.dwCheckPoint=dwCheckPoint;

nServiceStatus.dwWaitHint=dwWaitHint;

 //設置好nServiceStatus后,向SCM報告服務狀態

 success=SetServiceStatus(nServiceStatusHandle,&nServiceStatus);

 

if(!success)

{

KillService();

return success;

}

else

return success;

}

 

BOOL InitThread()

{

DWORD id;

hServiceThread=CreateThread(0,0,

(LPTHREAD_START_ROUTINE)sysnap,

0,0,&id);

if(hServiceThread==0)

{

return false;

}

else

{

nServiceRunning=true;

return true;

}

}

 

DWORD sysnap(LPDWORD param)

{

 while(nServiceRunning)

{

Beep(450,150);

Sleep(4000);

}

 

return 0;

}

 

void KillService()

{

nServiceRunning=false;

SetEvent(killServiceEvent);

ReportStatusToSCMgr(SERVICE_STOPPED,NO_ERROR,0,0,0);

}

 

void ServiceCtrlHandler(DWORD dwControlCode)

{

BOOL success;

switch(dwControlCode)

{

case SERVICE_CONTROL_SHUTDOWN: 

case SERVICE_CONTROL_STOP:

nServiceCurrentStatus=SERVICE_STOP_PENDING;

success=ReportStatusToSCMgr(SERVICE_STOP_PENDING,NO_ERROR,0,1,3000);//先更新服務狀態為SERVICDE_STOP_PENDING,再停止服務

KillService();

return;

default:

break;

}

ReportStatusToSCMgr(nServiceCurrentStatus,NO_ERROR,0,0,0);

}

 

svload.exe的代碼

#include <stdio.h>

#include <windows.h>

#include <winsvc.h>

 

int main(int argc, char* argv[])

{

 char* showInfo="sysnap's first Windows service";      //注意寬字符的轉換(L)

 char* showName="sysnap";

 char* sv_Path="C://sysnap//sv.exe";

 

SC_HANDLE Hsysnap;

 SC_HANDLE hSCManager;

 

hSCManager=OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);

if(!hSCManager)

{

printf("failed");

return 1;

}

 

Hsysnap=CreateService(hSCManager,TEXT(showName),

TEXT(showInfo),

SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS,SERVICE_DEMAND_START,

SERVICE_ERROR_NORMAL,

sv_Path,

0,0,0,0,0);

if(!Hsysnap)

{

CloseServiceHandle(hSCManager);

printf("failed");

return 1;

}

CloseServiceHandle(Hsysnap);

CloseServiceHandle(hSCManager);

return 0;

}

 

倆個EXE文件一共334K....如果不想編譯我可以把他們發到你郵葙

 

 

哈哈..謝謝給個精品..當時在寫的時候認為很清晰..不過現在自己再看一遍..是比較長..我把他的整體弄上來,,,看了才不灰亂 



void main() 

調用StartServiceCtrlDispatcher(),把控制交給控制分配器,控制分配器新建一個線程來運行ServiceMain..也就是真正進入服務 


void ServiceMain(DWORD argc, LPTSTR *argv) 


RegisterServiceCtrlHandler注冊為服務控制器,接受來自SCM的請求並做出處理 


創建一個事件,在函數的最后將用該事件來保持函數的運行直到SCM發出停止請求 才返回 

創建一個線程來運行我們的函數 


函數運行完了之后返回ServiceMain(),ServiceMain()調用  




BOOL ReportStatusToSCMgr() 

 填充SERVICE_STATUS 成員..並把它做為參數傳給etServiceStatus()向SCM報告服務狀 態  



BOOL InitThread() 

創建運行sysnap()的線程 


DWORD sysnap(LPDWORD param) 

 我們要做的工作 


void KillService() 

停止服務 


void ServiceCtrlHandler(DWORD dwControlCode) 

接受來自SCM的請求並做出反應..這里要自己實現幾個函數; 
}

總之記住看的時候以main(),ServiceMain()和ServiceCtrlHandler()為中心..其他函數都是比較簡單的

http://blog.csdn.net/jiangxinyu/article/details/5265673


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM