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的服务给停掉或者卸载掉,……等等。