基于USBDeview的自制USB设备监管系统实现(4)——Guard Process

10 篇文章 0 订阅
3 篇文章 0 订阅

Guard Process是一个守护进程,目的是为了防止用户有意或无意间终止常驻的usbdeview进程。本文中,这个守护进程命名为USB Monitor Service(以下简称“ums”),做为Windows系统服务运行。

ums会定时扫描系统进程,检查usbdeivew进程是否处于运行中。若发现当前进程列表中没有usbdeview,则会自主运行usbdeview.exe。以下是ums的完整源代码:

/**
  * USB Monitor Service是个一Windows系统服务程序,这个程序定时检查usbdeview.exe进程是否处于
  * 运行状态,如果未发现此进程,则启动该进程。
  * 可以用sc create binPath= ums.exe命令来讲USB Monitor Service注册为系统服务,并用sc start ums
  * 来启动该服务。
  */
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <tlhelp32.h>
#include <wtsapi32.h>
#include <winnt.h>
#include <winbase.h>
#include <userenv.h>

#define __USE_CREATE_PROCESS__

#define __MAX_MSG_LENGTH__ 256

const char __configFilename[] = "ums.ini";
const char __sleepPeriodKey[] = "SLEEPPERIOD";
const char __logFileKey[] = "LOGFILE";
const char __serviceNameKey[] = "SERVICENAME";
const char __monitorNameKey[] = "MONITORNAME";
const char __monitorFilenameKey[] = "MONITORFILENAME";

typedef struct __tagServiceConfig
{
   DWORD sleepPeriod;
   char logFile[MAX_PATH + 1];
   char serviceName[__MAX_MSG_LENGTH__];
   char monitorName[__MAX_MSG_LENGTH__];
   char monitorFilename[MAX_PATH + 1];
} ServiceConfig;

ServiceConfig serviceConfig =
{
   5000,
   ".\\ums.log",
   "USB Monitor Service",
   "usb monitor",
   "usbdeview.exe"
};

unsigned int count = 0;
SERVICE_STATUS serviceStatus;
SERVICE_STATUS_HANDLE servStatusHandle;
char modulePath[MAX_PATH + 1] = "\0";
char fullPath[MAX_PATH + 1] = "\0";
char invokeCommand[MAX_PATH + 1] = "\0";
char errMsg[__MAX_MSG_LENGTH__] = "\0";

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

int writeLog(const char logMsg[]);
int initService();
void servMain(int argc, char *argv[]);
void controlHandler(DWORD request);
BOOL searchProcess(const char procName[]);
void getPath(char fullPath[], char path[]);
unsigned long invokeProcess(char cmd[]);
int loadConfig(const char configFile[], ServiceConfig *config);
void strupr(char str[]);
// mingw的h文件中并未声明WTSGetActiveConsoleSessionId函数,因此需要手工声明。
// WTSGetActiveConsoleSessionId仅能用于C++。
WINBASEAPI DWORD WINAPI WTSGetActiveConsoleSessionId(VOID);

#ifdef __cplusplus
}
#endif // __cplusplus

int main(int argc, char *argv[])
{
   GetModuleFileName(NULL, fullPath, MAX_PATH);
   getPath(fullPath, modulePath);
   sprintf(fullPath, "%s\%s", modulePath, __configFilename);
   loadConfig(fullPath, &serviceConfig);

   SERVICE_TABLE_ENTRY serviceTables[2];
   serviceTables[0].lpServiceName = (LPSTR)serviceConfig.serviceName;
   serviceTables[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)servMain;
   serviceTables[1].lpServiceName = NULL;
   serviceTables[1].lpServiceProc = NULL;
   StartServiceCtrlDispatcher(serviceTables);

   return 0;
}

int writeLog(const char logMsg[])
{
   int error = 1;
   sprintf(fullPath, "%s\%s", modulePath, serviceConfig.logFile);
   FILE *ofp = fopen(fullPath, "a");
   time_t currTime = time(NULL);
   struct tm *logTime = localtime(&currTime);
   if (ofp != NULL)
   {
      fprintf(ofp, "%04d-%02d-%02d %02d:%02d:%02d%c",
           logTime->tm_year + 1900, logTime->tm_mon + 1, logTime->tm_mday,
           logTime->tm_hour, logTime->tm_min, logTime->tm_sec, '|');
      fprintf(ofp, "%s\n", logMsg);
      fclose(ofp);
      error = 0;
   }

   return error;
}

int initService()
{
   int result = writeLog("Service Started.");
   return result;
}

void servMain(int argc, char* argv[])
{
   unsigned long count = 0;
   char msgBuf[64];

   serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
   serviceStatus.dwCurrentState = SERVICE_START_PENDING;
   serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
   serviceStatus.dwWin32ExitCode = 0;
   serviceStatus.dwServiceSpecificExitCode = 0;
   serviceStatus.dwCheckPoint = 0;
   serviceStatus.dwWaitHint = 0;

   servStatusHandle = RegisterServiceCtrlHandler(serviceConfig.serviceName, (LPHANDLER_FUNCTION)controlHandler);
   if (servStatusHandle == (SERVICE_STATUS_HANDLE)0)
   {  // Registering Control Handler failed
      return;
   }

   int error = initService();
   if (error)
   {
      serviceStatus.dwCurrentState = SERVICE_STOPPED;
      serviceStatus.dwWin32ExitCode = -1;
      SetServiceStatus(servStatusHandle, &serviceStatus);

      return;
   }

   serviceStatus.dwCurrentState = SERVICE_RUNNING;
   SetServiceStatus(servStatusHandle, &serviceStatus);

   while(serviceStatus.dwCurrentState == SERVICE_RUNNING)
   {
      count ++;
      BOOL procExist = searchProcess(serviceConfig.monitorFilename);
      if (!procExist)
      {
         sprintf(msgBuf, "%08lu, %s not exists.", count, serviceConfig.monitorName);
         writeLog(msgBuf);
         sprintf(invokeCommand, "%s%s", modulePath, serviceConfig.monitorFilename);
         writeLog(invokeCommand);
         invokeProcess(invokeCommand);
      }
      Sleep(serviceConfig.sleepPeriod);
   }
}

void controlHandler(DWORD request)
{
   switch(request)
   {
      case SERVICE_CONTROL_STOP:
         writeLog("Service Stopped.");
         serviceStatus.dwCurrentState = SERVICE_STOPPED;
         serviceStatus.dwWin32ExitCode = 0;
         SetServiceStatus(servStatusHandle, &serviceStatus);
         break;

      case SERVICE_CONTROL_SHUTDOWN:
         writeLog("Service Stopped.");
         serviceStatus.dwCurrentState = SERVICE_STOPPED;
         serviceStatus.dwWin32ExitCode = 0;
         SetServiceStatus(servStatusHandle, &serviceStatus);
         break;

      default:
         SetServiceStatus(servStatusHandle, &serviceStatus);
         break;
   }

   return;
}

BOOL searchProcess(const char procName[])
{
   BOOL walkCode, found = FALSE;
   PROCESSENTRY32 pe;
   // writeLog("get processes snapshot.");
   HANDLE snapshotHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
   if (snapshotHandle == INVALID_HANDLE_VALUE)
   {
      writeLog("can't get process snapshot.");
      return FALSE;
   }

   pe.dwSize = sizeof(pe);
   walkCode = Process32First(snapshotHandle, &pe);
   while(walkCode)
   {
      int equal = strcmp(pe.szExeFile, serviceConfig.monitorFilename);
      if (equal == 0)
      {
         found = TRUE;
         break;
      }
      walkCode = Process32Next(snapshotHandle, &pe);
   }
   CloseHandle(snapshotHandle);

   return found;
}

void getPath(char fullPath[], char path[])
{
   if (path != NULL)
   {
      char *occur = strrchr(fullPath, '\\');
      memset(path, 0, strlen(fullPath));
      strncpy(path, fullPath, strlen(fullPath) - strlen(occur) + 1);
   }
}

unsigned long invokeProcess(char cmd[])
{
   unsigned long errorCode = 0;
   HANDLE hTokenThis = NULL;
   HANDLE hTokenDup = NULL;
   BOOL bResult = FALSE;
   HANDLE hThisProcess = GetCurrentProcess();
   bResult = OpenProcessToken(hThisProcess, TOKEN_ALL_ACCESS, &hTokenThis);
   if (!bResult)
   {
      errorCode = GetLastError();
      return errorCode;
   }

   bResult = DuplicateTokenEx(hTokenThis, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hTokenDup);
   if (!bResult)
   {
      errorCode = GetLastError();
      return errorCode;
   }

   DWORD dwSessionId = WTSGetActiveConsoleSessionId();
   bResult = SetTokenInformation(hTokenDup, TokenSessionId, &dwSessionId, sizeof(DWORD));
   if (!bResult)
   {
      errorCode = GetLastError();
      return errorCode;
   }

   STARTUPINFO si;
   PROCESS_INFORMATION pi;
   ZeroMemory(&si, sizeof(STARTUPINFO));
   ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
   si.cb = sizeof(STARTUPINFO);
   si.dwFlags = STARTF_USESHOWWINDOW;
   si.wShowWindow = SW_SHOWNORMAL;

   LPVOID pEnv = NULL;
   DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT;

   bResult = CreateEnvironmentBlock(&pEnv, hTokenDup, FALSE);
   if (!bResult)
   {
      errorCode = GetLastError();
      return errorCode;
   }

   bResult = CreateProcessAsUser(hTokenDup, NULL, cmd, NULL, NULL, FALSE, dwCreationFlag, pEnv, NULL, &si, &pi);
   if (!bResult)
   {
      errorCode = GetLastError();
      return errorCode;
   }
   return errorCode;
}

int loadConfig(const char configFile[], ServiceConfig *config)
{
   int loadCode = 0;
   const char splitter = '=';

   char *valuePtr;
   char key[32];
   char buffer[__MAX_MSG_LENGTH__];
   FILE *cfgFp = fopen(configFile, "r");
   if (cfgFp != NULL)
   {
      loadCode = 0;
      do {
         fscanf(cfgFp, "%s", buffer);
         valuePtr = strchr(buffer, splitter);
         if (valuePtr == NULL)
         {
            loadCode = -2;
            break;
         }
         *valuePtr = '\0';
         valuePtr ++;
         // strncpy(key, buffer, strlen(buffer) - strlen(valuePtr) + 1);
         strcpy(key, buffer);
         strupr(key);
         if (!strcmp(key, __sleepPeriodKey))
         {
            config->sleepPeriod = strtoul(valuePtr, NULL, 10);
         }
         else if (!strcmp(key, __logFileKey))
         {
            strcpy(config->logFile, valuePtr);
         }
         else if (!strcmp(key, __serviceNameKey))
         {
            strcpy(config->serviceName, valuePtr);
         }
         else if (!strcmp(key, __monitorNameKey))
         {
            strcpy(config->monitorName, valuePtr);
         }
         else if (!strcmp(key, __monitorFilenameKey))
         {
            strcpy(config->monitorFilename, valuePtr);
         }
         else
         {
            loadCode = -2;
            break;
         }
      } while(!feof(cfgFp));
      fclose(cfgFp);
   }
   else
   {
      loadCode = -1;
      sprintf(buffer, "load config file %s error: %d", __configFilename, loadCode);
      writeLog(buffer);
   }

   return loadCode;

}

void strupr(char str[])
{
   int length = strlen(str);
   for(int index = 0; index < length; index ++)
   {
      if ((str[index] >= 'a') && (str[index] <= 'z'))
      {
         str[index] -= 0x20;
      }
   }
}

这里使用了Windows Service程序的编写方法,main函数只是用于注册服务信息,真正的服务入口是servMain函数。servMain函数将controlHandler注册为Windows服务事件处理程序,然后循环检查当前系统进程列表中是否包含usbdeview.exe进程。如果发现未包含usbdeview.exe进程,则在日志文件中写入一条异常记录,并调用invokeProcess函数试图运行usbdeview工具。

由于usm是作为系统服务运行,无法直接操作桌面,所以如果invokeProcess简单地调用Windows提供的创建进程API CreateProcess来创建usbdeview.exe的话,你会发现这个进程确实被创建了,但是却无法显示其GUI界面。所以,invoke必须调用CreateProcessAsUser接口来创建usbdeview进程。这也是为什么invokeProcess那么啰嗦的原因。

usm目前的实现还是比较简单的,往后还可以添加更多的保护能力,比如校验usbdeview.exe和udl.exe是否为原装的(免得被PC终端用户偷偷替换掉),想办法别别让用户把ums的服务给停掉或者卸载掉,……等等。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值