網(wǎng)絡(luò)安全編程:服務(wù)控制管理器實(shí)例
幾乎每一種操作系統(tǒng)都有一種在系統(tǒng)啟動(dòng)時(shí)啟動(dòng)的進(jìn)程機(jī)制,這種機(jī)制不會(huì)依賴于用戶的交互。在Windows下,類似的機(jī)制被稱為服務(wù)或Windows服務(wù)。服務(wù)是一種程序類型,它在后臺(tái)運(yùn)行,服務(wù)程序通??梢栽诒镜睾屯ㄟ^網(wǎng)絡(luò)為用戶提供一些功能,服務(wù)在操作系統(tǒng)啟動(dòng)時(shí)就會(huì)隨之啟動(dòng)的程序。服務(wù)程序可能是EXE程序,具有其單獨(dú)的進(jìn)程,也有可能是DLL文件依附于某個(gè)進(jìn)程(比如svchost.exe),更有可能是SYS文件而處于系統(tǒng)的內(nèi)核之中。由于服務(wù)所處的核心地位、啟動(dòng)方式等因素,它也是反病毒軟件與惡意軟件的“兵家必爭(zhēng)之地”。對(duì)于研究系統(tǒng)安全來說則非常重要。
01 如何查看系統(tǒng)服務(wù)
在Windows下,有很多服務(wù)是跟隨操作系統(tǒng)一起啟動(dòng)的,具體有哪些服務(wù)是跟隨操作系統(tǒng)一起啟動(dòng)的呢?如何查看呢?其實(shí)非常簡(jiǎn)單。在“我的電腦”上單擊鼠標(biāo)右鍵,然后在彈出的菜單上選擇“管理”,打開“計(jì)算機(jī)管理”工具,單擊左面樹形列表的“服務(wù)和應(yīng)用程序”會(huì)打開子列表,選擇“服務(wù)”,則在右側(cè)出現(xiàn)服務(wù)項(xiàng)列表。較為簡(jiǎn)單的方法是直接在“運(yùn)行”窗口中輸入“services.msc”,打開服務(wù)管理器。服務(wù)管理器主要用于顯示系統(tǒng)中已經(jīng)存在的應(yīng)用程序服務(wù),顯示對(duì)服務(wù)的描述,還可以控制服務(wù)的啟動(dòng)狀態(tài)和啟動(dòng)方式。服務(wù)管理器如圖1所示。
圖1 Windows下的服務(wù)管理程序
在圖1顯示的服務(wù)列表中,只能查看Win32應(yīng)用程序的服務(wù),無法查看關(guān)于驅(qū)動(dòng)程序的服務(wù)。可以借助于其他一些工具來查看驅(qū)動(dòng)程序級(jí)別的服務(wù),圖2使用SREng來查看驅(qū)動(dòng)程序相關(guān)的服務(wù)列表。
圖2 使用SREng查看驅(qū)動(dòng)程序服務(wù)列表
接下來會(huì)編寫一個(gè)類似的程序,既可以查看應(yīng)用程序服務(wù)列表,也可以查看驅(qū)動(dòng)程序服務(wù)列表。編寫完成后的程序界面如圖3所示。
圖3 服務(wù)管理程序界面
編寫的服務(wù)管理程序既可以查看“Win32服務(wù)應(yīng)用程序”,也可以查看“驅(qū)動(dòng)服務(wù)程序”,并且可以對(duì)它們的運(yùn)行狀態(tài)進(jìn)行簡(jiǎn)單的控制。這里開發(fā)的服務(wù)控制管理器使用MFC的對(duì)話框,其中用到了CListCtrl控件?,F(xiàn)在就開始打造一個(gè)屬于自己的服務(wù)控制管理器。
02 服務(wù)控制管理器的實(shí)現(xiàn)
服務(wù)控制管理器的開發(fā)過程與注冊(cè)表啟動(dòng)管理器的開發(fā)過程比較類似,主要也是枚舉服務(wù)并顯示到列表控件中。對(duì)服務(wù)狀態(tài)的控制,是通過服務(wù)相關(guān)的API函數(shù)來完成的。首先來編寫代碼,希望大家能夠掌握服務(wù)相關(guān)的API函數(shù)。在代碼的后面,會(huì)對(duì)開發(fā)服務(wù)管理器涉及的API進(jìn)行相應(yīng)的解釋。
學(xué)習(xí)API函數(shù)的使用,MSDN是最好的老師,詳細(xì)、透徹、權(quán)威。在編程的道路上,要不斷通過閱讀別人的代碼來提高自己的編程能力,就需要自己來掌握陌生的API函數(shù),那時(shí)一定要想起查閱MSDN。
1. 服務(wù)的類型
服務(wù)控制管理器的界面都已經(jīng)熟悉了,界面的布局可以按照自己的方式進(jìn)行調(diào)整。在枚舉服務(wù)的時(shí)候,將“Win32應(yīng)用程序服務(wù)”和“驅(qū)動(dòng)程序服務(wù)”分開枚舉,這樣有助于對(duì)各種服務(wù)的了解。
枚舉這兩類服務(wù)的主要差別在于調(diào)用EnumServicesStatus()函數(shù)時(shí)為其傳遞的第二個(gè)參數(shù)。如果枚舉“Win32應(yīng)用程序服務(wù)”,那么傳遞的參數(shù)為SERVICE_WIN32;如果枚舉“驅(qū)動(dòng)程序服務(wù)”,那么傳遞的參數(shù)為SERVICE_DRIVER。這兩個(gè)參數(shù)其實(shí)是系統(tǒng)定義的宏,該宏定義在WinNt.h頭文件中,具體定義如下:
- #define SERVICE_DRIVER (SERVICE_KERNEL_DRIVER | \
- SERVICE_FILE_SYSTEM_DRIVER | \
- SERVICE_RECOGNIZER_DRIVER)
- #define SERVICE_WIN32 (SERVICE_WIN32_OWN_PROCESS | \
- SERVICE_WIN32_SHARE_PROCESS)
SERVICE_DRIVER 和 SERVICE_WIN32 是其他宏的組合,而那些宏又有具體的值。下面解釋一下其他宏的含義。
SERVICE_DRIVER 宏由 3 個(gè)宏組成,具體如下:
- #define SERVICE_KERNEL_DRIVER 0x00000001 // 設(shè)備驅(qū)動(dòng)程序
- #define SERVICE_FILE_SYSTEM_DRIVER 0x00000002 // 內(nèi)核模式文件系統(tǒng)驅(qū)動(dòng)程序
- #define SERVICE_RECOGNIZER_DRIVER 0x00000008 // 文件系統(tǒng)識(shí)別器驅(qū)動(dòng)程序
SERVICE_WIN32 宏由兩個(gè)宏組成,具體如下:
- #define SERVICE_WIN32_OWN_PROCESS 0x00000010 // 獨(dú)占一個(gè)進(jìn)程的服務(wù)
- #define SERVICE_WIN32_SHARE_PROCESS 0x00000020 // 與其他服務(wù)共享一個(gè)進(jìn)程的服務(wù)
如果想要枚舉全部類型的服務(wù),那么使用SERVICE_TYPE_ALL宏即可,該宏的定義如下:
- #define SERVICE_TYPE_ALL (SERVICE_WIN32 | \
- SERVICE_ADAPTER | \
- SERVICE_DRIVER | \
- SERVICE_INTERACTIVE_PROCESS)
2. 服務(wù)的枚舉函數(shù)
服務(wù)的枚舉所使用的API函數(shù)是EnumServicesStatus(),該函數(shù)中需要指定枚舉的類型分別是SERVICE_DRIVER和SERVICE_WIN32。
具體來看服務(wù)枚舉的函數(shù),代碼如下:
- VOID CManageServicesDlg::ShowServiceList(DWORD dwServiceType)
- {
- m_ServiceList.DeleteAllItems();
- // 打開服務(wù)管理器
- SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
- if ( NULL == hSCM )
- {
- AfxMessageBox("OpenSCManger Error!");
- return ;
- }
- DWORD ServiceCount = 0;
- DWORD dwSize = 0;
- LPENUM_SERVICE_STATUS lpInfo;
- // 第一次調(diào)用
- BOOL bRet = EnumServicesStatus(hSCM,
- dwServiceType, SERVICE_STATE_ALL,
- NULL, 0, &dwSize,
- &ServiceCount, NULL);
- // 由于沒有給定接收服務(wù)列表的緩沖區(qū),這里必定會(huì)調(diào)用失敗
- // 失敗的原因是 ERROR_MORE_DATA
- // 說明需要更大的緩沖區(qū)來保存數(shù)據(jù)
- if ( !bRet && GetLastError() == ERROR_MORE_DATA )
- {
- // 分配緩沖區(qū),保存服務(wù)列表
- lpInfo = (LPENUM_SERVICE_STATUS)(new BYTE[dwSize]);
- bRet = EnumServicesStatus(hSCM,
- dwServiceType, SERVICE_STATE_ALL,
- (LPENUM_SERVICE_STATUS)lpInfo,
- dwSize, &dwSize,
- &ServiceCount, NULL);
- if ( !bRet )
- {
- CloseServiceHandle(hSCM);
- return ;
- }
- //逐個(gè)獲取數(shù)據(jù),添加至列表控件
- for ( DWORD i = 0; i < ServiceCount; i ++)
- {
- CString str;
- m_ServiceList.InsertItem(i, lpInfo[i].lpServiceName);
- m_ServiceList.SetItemText(i, 1, lpInfo[i].lpDisplayName);
- switch ( lpInfo[i].ServiceStatus.dwCurrentState )
- {
- case SERVICE_PAUSED:
- {
- m_ServiceList.SetItemText(i, 2, "暫停");
- break;
- }
- case SERVICE_STOPPED:
- {
- m_ServiceList.SetItemText(i, 2, "停止");
- break;
- }
- case SERVICE_RUNNING:
- {
- m_ServiceList.SetItemText(i, 2, "運(yùn)行");
- break;
- }
- default:
- {
- m_ServiceList.SetItemText(i, 2, "其他");
- }
- }
- }
- // 釋放申請(qǐng)的空間
- delete lpInfo;
- }
- // 關(guān)閉服務(wù)管理器句柄
- CloseServiceHandle(hSCM);
- }
該函數(shù)有一個(gè)參數(shù),用來指明枚舉類型是“Win32應(yīng)用程序服務(wù)”,還是“驅(qū)動(dòng)程序服務(wù)”。該函數(shù)的默認(rèn)參數(shù)為“Win32應(yīng)用程序服務(wù)”,該函數(shù)的定義如下:
- VOID ShowServiceList(DWORD dwServiceType = SERVICE_WIN32);
3. 枚舉服務(wù)相關(guān)API函數(shù)解釋
(1)打開和關(guān)閉服務(wù)管理器
打開服務(wù)管理器的函數(shù)定義如下:
- SC_HANDLE OpenSCManager(
- LPCTSTR lpMachineName, // computer name
- LPCTSTR lpDatabaseName, // SCM database name
- DWORD dwDesiredAccess // access type
- );
參數(shù)說明如下。
lpMachineName:指向欲打開服務(wù)控制管理器數(shù)據(jù)庫的目標(biāo)主機(jī)名,本機(jī)則設(shè)置為 NULL。
lpDatabaseName:指向目標(biāo)主機(jī) SCM 數(shù)據(jù)庫名字的字符串。
dwDesiredAccess:指定對(duì) SCM 數(shù)據(jù)庫的訪問權(quán)限。
該函數(shù)調(diào)用成功,返回一個(gè) SCM 句柄,否則返回 NULL。
SCM是服務(wù)控制管理器的意思,它是系統(tǒng)服務(wù)的一個(gè)組成部分,跟開發(fā)的軟件不是一個(gè)概念。
關(guān)閉服務(wù)句柄的函數(shù)定義如下:
- BOOL CloseServiceHandle(
- SC_HANDLE hSCObject // handle to service or SCM object
- );
該函數(shù)用來關(guān)閉由OpenSCManager()和OpenService()打開的句柄。
(2)服務(wù)的枚舉函數(shù)
枚舉服務(wù)的函數(shù)定義如下:
- BOOL EnumServicesStatus(
- SC_HANDLE hSCManager, // handle to SCM database
- DWORD dwServiceType, // service type
- DWORD dwServiceState, // service state
- LPENUM_SERVICE_STATUS lpServices, // status buffer
- DWORD cbBufSize, // size of status buffer
- LPDWORD pcbBytesNeeded, // buffer size needed
- LPDWORD lpServicesReturned, // number of entries returned
- LPDWORD lpResumeHandle // next entry
- );
參數(shù)說明如下。
hSCManager:OpenSCManager()函數(shù)返回的句柄。
dwServiceType:指定枚舉的服務(wù)類型,也就是自定義函數(shù)的參數(shù)。
dwServiceState:枚舉指定狀態(tài)的服務(wù)。
lpServices:指向 ENUM_SERVICE_STATUS 類型的指針。
cbBufSize:指定緩沖區(qū)的大小。
pcbBytesNeeded:返回實(shí)際使用的內(nèi)存空間大小。
lpServicesReturned:返回枚舉服務(wù)的個(gè)數(shù)。
lpResumeHandle:返回枚舉是否成功。
ENUM_SERVICE_STATUS 結(jié)構(gòu)體的定義如下:
- typedef struct _ENUM_SERVICE_STATUS {
- LPTSTR lpServiceName;
- LPTSTR lpDisplayName;
- SERVICE_STATUS ServiceStatus;
- } ENUM_SERVICE_STATUS, *LPENUM_SERVICE_STATUS;
SERVICE_STATUS 結(jié)構(gòu)體的定義如下:
- typedef struct _SERVICE_STATUS {
- DWORD dwServiceType;
- DWORD dwCurrentState;
- DWORD dwControlsAccepted;
- DWORD dwWin32ExitCode;
- DWORD dwServiceSpecificExitCode;
- DWORD dwCheckPoint;
- DWORD dwWaitHint;
- } SERVICE_STATUS, *LPSERVICE_STATUS;
代碼中兩次調(diào)用EnumServicesStatus()函數(shù)。第1次沒有傳遞第4個(gè)和第5個(gè)參數(shù),使得函數(shù)返回FALSE,用GetLastError()得到錯(cuò)誤的原因?yàn)镋RROR_MORE_DATA。這時(shí),第6個(gè)參數(shù)pcbBytesNeeded返回實(shí)際需要使用的內(nèi)存大小,這樣可以通過new動(dòng)態(tài)申請(qǐng)所需的堆空間。以這種方式來獲取實(shí)際所需緩沖區(qū)大小的情況是比較多的,請(qǐng)大家一定要理解。
4. 服務(wù)的啟動(dòng)與停止
對(duì)服務(wù)的操作只介紹兩種,一種是啟動(dòng)服務(wù),另一種是停止服務(wù),也就是改變服務(wù)的狀態(tài)。經(jīng)常會(huì)用到停止服務(wù)的操作,因?yàn)橄到y(tǒng)中有很多用不到的服務(wù),但是它仍然會(huì)隨著系統(tǒng)的啟動(dòng)而啟動(dòng),這樣既會(huì)影響到系統(tǒng)的啟動(dòng)速度,也會(huì)占用寶貴的系統(tǒng)資源。因此,沒有用的系統(tǒng)服務(wù)最好將其停止(其實(shí)真正停止服務(wù)是改變它的啟動(dòng)狀態(tài),而不是這里介紹的運(yùn)行狀態(tài))。
啟動(dòng)服務(wù)的代碼如下:
- void CManageServicesDlg::OnBtnStart()
- {
- // TODO: Add your control notification handler code here
- // 選中服務(wù)的索引
- POSITION Pos = m_ServiceList.GetFirstSelectedItemPosition();
- int nSelect = -1;
- while ( Pos )
- {
- nSelect = m_ServiceList.GetNextSelectedItem(Pos);
- }
- if ( -1 == nSelect )
- {
- AfxMessageBox("請(qǐng)選擇要啟動(dòng)的服務(wù)");
- return ;
- }
- // 獲取選中服務(wù)的服務(wù)名
- char szServiceName[MAXBYTE] = { 0 };
- m_ServiceList.GetItemText(nSelect, 0, szServiceName, MAXBYTE);
- SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
- if ( NULL == hSCM )
- {
- AfxMessageBox("OpenSCManager Error");
- return ;
- }
- // 打開指定的服務(wù)
- SC_HANDLE hSCService = OpenService(hSCM, szServiceName, SERVICE_ALL_ACCESS);
- // 啟動(dòng)服務(wù)
- BOOL bRet = StartService(hSCService, 0, NULL);
- if ( bRet == TRUE )
- {
- m_ServiceList.SetItemText(nSelect, 2, "運(yùn)行");
- }
- else
- {
- AfxMessageBox("啟動(dòng)失敗!");
- }
- CloseServiceHandle(hSCService);
- CloseServiceHandle(hSCM);
- }
停止服務(wù)的代碼如下:
- void CManageServicesDlg::OnBtnStop()
- {
- // TODO: Add your control notification handler code here
- // 選中服務(wù)的索引
- // 此部分操作與啟動(dòng)服務(wù)相同,為節(jié)省篇幅,此處省略
- // ……
- SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
- if ( NULL == hSCM )
- {
- AfxMessageBox("OpenSCManager Error");
- return ;
- }
- // 打開指定的服務(wù)
- SC_HANDLE hSCService = OpenService(hSCM, szServiceName, SERVICE_ALL_ACCESS);
- SERVICE_STATUS ServiceStatus;
- // 停止服務(wù)
- BOOL bRet = ControlService(hSCService, SERVICE_CONTROL_STOP, &ServiceStatus);
- if ( bRet == TRUE )
- {
- m_ServiceList.SetItemText(nSelect, 2, "停止");
- }
- else
- {
- AfxMessageBox("停止失敗!");
- }
- CloseServiceHandle(hSCService);
- CloseServiceHandle(hSCM);
- }
5. 啟動(dòng)與停止服務(wù)相關(guān)API函數(shù)解釋
打開指定服務(wù)的函數(shù)定義如下:
- SC_HANDLE OpenService(
- SC_HANDLE hSCManager, // handle to SCM database
- LPCTSTR lpServiceName, // service name
- DWORD dwDesiredAccess // access
- );
參數(shù)說明如下。
hSCManager:指定由 OpenSCManager()函數(shù)打開的服務(wù)句柄。
lpServiceName:指定要打開的服務(wù)的名稱。
dwDesiredAccess:對(duì)要打開服務(wù)的訪問權(quán)限,這里為了方便,指定為 SC_MANAGER_ALL_ACCESS。
啟動(dòng)服務(wù)的函數(shù)定義如下:
- BOOL StartService(
- SC_HANDLE hService, // handle to service
- DWORD dwNumServiceArgs, // number of arguments
- LPCTSTR *lpServiceArgVectors // array of arguments
- );
參數(shù)說明如下。
hService:指定要啟動(dòng)服務(wù)的句柄,該句柄由 OpenService()返回。
dwNumServiceArgs:指向啟動(dòng)服務(wù)所需的參數(shù)個(gè)數(shù)。
lpServiceArgVectors:指向啟動(dòng)服務(wù)的參數(shù)。
停止服務(wù)的函數(shù)定義如下:
- BOOL ControlService(
- SC_HANDLE hService, // handle to service
- DWORD dwControl, // control code
- LPSERVICE_STATUS lpServiceStatus // status information
- );
參數(shù)說明如下。
hService:指定一個(gè)由 OpenService()打開的服務(wù)句柄。
dwControl:指定要發(fā)送的控制碼。
lpServiceStatus:返回服務(wù)的狀態(tài)。
ControlService()可以對(duì)服務(wù)進(jìn)行多種控制操作,每種控制操作對(duì)應(yīng)一種控制碼。當(dāng)要停止服務(wù)時(shí),使用的控制碼為 SERVICE_CONTROL_STOP。一定要注意,停止服務(wù)不要想當(dāng)然的使用 StopService()這樣的函數(shù),因?yàn)闆]有這個(gè) API 函數(shù)。