上一篇简要介绍了如何制作一个可被svchost调用的服务,本篇介绍如何使得这个服务可以被svchost识别并调用。
svchost会到注册表的HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost项中搜索其子项,每一个子项都是一个svchost服务组,svchost项有很多键值,每个键对应一个服务组,其值是该服务组下所有的服务
所以,首先要决定我们自己的服务放在哪个服务组里,这里假设是netsvcs组。在netsvcs键值最后添加自编服务的名称即可。
随后svchost会根据服务名到注册表的HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services项下找到服务名同名的子项,子项中的一些键定义了这个服务的详情:
“description”键是服务的详细描述;“displayName”是服务的友好显示名称;ErrorControl是错误控制级别,1表示忽略错误,就是告诉操作系统尽管出错了,但还是继续加载其他服务;ImagePath指定了svchost的运行路径以及自编服务所在的服务组,一般为“%SystemRoot%\System32\svchost.exe -k {serv_grp_name}”;ObjectName指定了对象名,对于Win32服务来说,就是系统服务帐号名,比如LocalSystem;start指定了启动类型,3表示手工启动,2表示自动启动,1表示I/O驱动程序方式启动,0表示引导时随内核启动,4表示禁用;type指定了服务类型,1表示内核设备驱动程序,2表示文件系统驱动程序,0x10表示Win32服务,0x20表示该服务可以与其他服务共享进程。上图中的target是后面介绍的程序将会用到的自定义键,用于指定服务被组合到哪个服务组中。
服务名子项下还有一个名为Parameters的子项,这个子项有一个名为ServiceDLL的键,其取值为服务DLL的绝对路径。
综上所述,只要按照上述规则,在注册表中写好相应的信息,重启系统之后,就可以由svchost调用自制服务了。下面的程序可以将自编的可被svchost调用的服务装载到注册表中,亦可从注册表中清除。代码如下:
issapp.h
/*
* issapp.h
*
* Created on: 2021年5月16日
* Author: kingfox
*/
#ifndef ISSAPP_H_
#define ISSAPP_H_
#include <string>
struct ServiceConfig
{
std::string servName;
std::string descr;
std::string dispName;
uint32_t errorControl;
std::string imagePath;
std::string target;
std::string objectName;
uint32_t startMode;
uint32_t type;
std::string servDLL;
static const char *servNameKeyName;
static const char *descrKeyName;
static const char *dispNameKeyName;
static const char *errorControlKeyName;
static const char *imagePathKeyName;
static const char *targetKeyName;
static const char *objectNameKeyName;
static const char *startModeKeyName;
static const char *typeKeyName;
static const char *servDLLKeyName;
};
enum class ISSErrorCode
{
EC_SUCCESS = 0,
EC_INVALID_FILE = -1,
EC_INVALID_FORMAT = -2,
EC_INVALID_SERVICE = -3,
EC_ALREADY_INSTALLED = -4
};
class InstallServiceApplication
{
public:
InstallServiceApplication();
virtual ~InstallServiceApplication();
protected:
virtual int run(void *params) override;
void displayUsage();
long loadServiceConfig(const std::string& filename, ServiceConfig& serviceConfig);
long installService(const ServiceConfig& serviceConfig);
long uninstallService(const std::string& servName);
bool queryService(const std::string& servName);
private:
static const char __commentChar;
static const char *__keyValueDelim;
static const std::string __rootKeyPath;
static const std::string __svchostPath;
static const std::string __targetService;
char configBuffer[65536];
};
#endif /* ISSAPP_H_ */
issapp.cpp:
/*
* issapp.cpp
*
* Created on: 2021年5月16日
* Author: kingfox
*
* Purpose: install a service DLL into svchost, or uninstall a svchost service.
* usage:
* to install a svchost service: iss install serv_descr_file
* to uninstall a svchost service: iss uninstall service_name
* to query whether a svchost service is installed: iss query service_name
*/
#include <string>
#include <fstream>
#include <iostream>
#include <vector>
#include <windows.h>
#include "issapp.h"
using namespace std;
const char *ServiceConfig::servNameKeyName = "serviceName";
const char *ServiceConfig::descrKeyName = "Description";
const char *ServiceConfig::dispNameKeyName = "DisplayName";
const char *ServiceConfig::errorControlKeyName = "ErrorControl";
const char *ServiceConfig::imagePathKeyName = "ImagePath";
const char *ServiceConfig::targetKeyName = "target";
const char *ServiceConfig::objectNameKeyName = "ObjectName";
const char *ServiceConfig::startModeKeyName = "Start";
const char *ServiceConfig::typeKeyName = "Type";
const char *ServiceConfig::servDLLKeyName = "ServiceDll";
const char InstallServiceApplication::__commentChar = '#';
const char *InstallServiceApplication::__keyValueDelim = "=";
const string InstallServiceApplication::__rootKeyPath = {"SYSTEM\\CurrentControlSet\\services\\"};
const string InstallServiceApplication::__svchostPath = {"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Svchost\\"};
const string InstallServiceApplication::__targetService = {"netsvcs"};
InstallServiceApplication issApp;
InstallServiceApplication::InstallServiceApplication()
{
}
InstallServiceApplication::~InstallServiceApplication()
{
}
int InstallServiceApplication::run(void *params)
{
MainArgs *args = reinterpret_cast<MainArgs *>(params);
long errorCode = NO_ERROR;
if (args->argc == 1)
{
displayUsage();
}
else if ((string(args->argv[1]) == "install") && (args->argc == 3))
{
ServiceConfig serviceConfig;
errorCode = loadServiceConfig(args->argv[2], serviceConfig);
if (errorCode != NO_ERROR)
{
cerr << "load config from " << args->argv[2] << " error: " << errorCode << endl;
return errorCode;
}
if (queryService(serviceConfig.servName))
{
cout << "svchost service " << serviceConfig.servName << " installed. please uninstall it first." << endl;
errorCode = (long)ISSErrorCode::EC_ALREADY_INSTALLED;
}
else
{
errorCode = installService(serviceConfig);
if (errorCode == NO_ERROR)
{
cout << "install service success." << endl;
}
else
{
cerr << "write registry error: " << errorCode << endl;
}
}
}
else if ((string(args->argv[1]) == "uninstall") && (args->argc == 3))
{
if (!queryService(args->argv[2]))
{
cout << "svchost service " << args->argv[2] << " not installed" << endl;
errorCode = (long)ISSErrorCode::EC_ALREADY_INSTALLED;
}
else
{
errorCode = uninstallService(args->argv[2]);
if (errorCode == NO_ERROR)
{
cout << "uninstall service success." << endl;
}
else
{
cerr << "uninstall service failure: " << errorCode << endl;
}
}
}
else if ((string(args->argv[1]) == "query") && (args->argc == 3))
{
bool installed = queryService(args->argv[2]);
if (installed)
{
cout << "svchost service " << args->argv[2] << " installed." << endl;
}
else
{
cout << "svchost service " << args->argv[2] << " not installed." << endl;
}
}
else
{
cerr << "bad arguments." << endl;
displayUsage();
}
return errorCode;
}
void InstallServiceApplication::displayUsage()
{
cout << "to install a svchost service: iss install serv_descr_file" << endl;
cout << "to uninstall a svchost service: iss uninstall service_name" << endl;
cout << "to query whether a svchost service is installed: iss query service_name" << endl;
cout << "\tNOTE: after install a svchost service, you should restart to make it usable." << endl;
}
long InstallServiceApplication::loadServiceConfig(const string &filename, ServiceConfig &serviceConfig)
{
long errorCode = (long)ISSErrorCode::EC_SUCCESS;
string text;
ifstream ifs(filename);
if (!ifs)
{
errorCode = (long)ISSErrorCode::EC_INVALID_FILE;
}
else
{
while(!ifs.eof())
{
getline(ifs, text);
StringUtility::trim(text);
if ((text.length() > 0) && (text.at(0) != __commentChar))
{
vector<string> keyAndValue;
int count = StringUtility::split(text, keyAndValue, __keyValueDelim);
if (count != 2)
{
errorCode = (long)ISSErrorCode::EC_INVALID_FORMAT;
break;
}
if (keyAndValue[0] == ServiceConfig::servNameKeyName)
{
serviceConfig.servName.assign(keyAndValue[1]);
}
else if (keyAndValue[0] == ServiceConfig::descrKeyName)
{
serviceConfig.descr.assign(keyAndValue[1]);
}
else if (keyAndValue[0] == ServiceConfig::dispNameKeyName)
{
serviceConfig.dispName.assign(keyAndValue[1]);
}
else if (keyAndValue[0] == ServiceConfig::errorControlKeyName)
{
serviceConfig.errorControl = stoi(keyAndValue[1]);
}
else if (keyAndValue[0] == ServiceConfig::imagePathKeyName)
{
serviceConfig.imagePath.assign(keyAndValue[1]);
}
else if (keyAndValue[0] == ServiceConfig::targetKeyName)
{
serviceConfig.target.assign(keyAndValue[1]);
}
else if (keyAndValue[0] == ServiceConfig::objectNameKeyName)
{
serviceConfig.objectName.assign(keyAndValue[1]);
}
else if (keyAndValue[0] == ServiceConfig::startModeKeyName)
{
serviceConfig.startMode = stoi(keyAndValue[1]);
}
else if (keyAndValue[0] == ServiceConfig::typeKeyName)
{
serviceConfig.type = stoi(keyAndValue[1], 0, 16);
}
else if (keyAndValue[0] == ServiceConfig::servDLLKeyName)
{
serviceConfig.servDLL.assign(keyAndValue[1]);
}
else
{
errorCode = (long)ISSErrorCode::EC_INVALID_FORMAT;
break;
}
}
}
}
return errorCode;
}
long InstallServiceApplication::installService(const ServiceConfig &serviceConfig)
{
HKEY hKey;
unsigned long result;
long errorCode = RegCreateKeyEx(HKEY_LOCAL_MACHINE, (__rootKeyPath + serviceConfig.servName).c_str(), 0, nullptr, 0, KEY_ALL_ACCESS, nullptr, &hKey, &result);
if (errorCode == ERROR_SUCCESS)
{
RegSetKeyValue(hKey, nullptr, ServiceConfig::descrKeyName, REG_SZ, serviceConfig.descr.c_str(), serviceConfig.descr.length());
RegSetKeyValue(hKey, nullptr, ServiceConfig::dispNameKeyName, REG_SZ, serviceConfig.dispName.c_str(), serviceConfig.dispName.length());
RegSetKeyValue(hKey, nullptr, ServiceConfig::errorControlKeyName, REG_DWORD, &(serviceConfig.errorControl), sizeof(DWORD));
string fullImagePath = serviceConfig.imagePath + " -k " + serviceConfig.target;
RegSetKeyValue(hKey, nullptr, ServiceConfig::imagePathKeyName, REG_EXPAND_SZ, fullImagePath.c_str(), fullImagePath.length());
RegSetKeyValue(hKey, nullptr, ServiceConfig::targetKeyName, REG_SZ, serviceConfig.target.c_str(), serviceConfig.target.length());
RegSetKeyValue(hKey, nullptr, ServiceConfig::objectNameKeyName, REG_SZ, serviceConfig.objectName.c_str(), serviceConfig.objectName.length());
RegSetKeyValue(hKey, nullptr, ServiceConfig::startModeKeyName, REG_DWORD, &(serviceConfig.startMode), sizeof(DWORD));
RegSetKeyValue(hKey, nullptr, ServiceConfig::typeKeyName, REG_DWORD, &(serviceConfig.type), sizeof(DWORD));
RegCreateKeyEx(hKey, "Parameters", 0, nullptr, 0, KEY_ALL_ACCESS, nullptr, &hKey, &result);
RegSetKeyValue(hKey, nullptr, ServiceConfig::servDLLKeyName, REG_EXPAND_SZ, serviceConfig.servDLL.c_str(), serviceConfig.servDLL.length());
RegOpenKeyEx(HKEY_LOCAL_MACHINE, __svchostPath.c_str(), 0, KEY_ALL_ACCESS, &hKey);
unsigned long dataSize = sizeof configBuffer;
unsigned long dataType = REG_MULTI_SZ;
memset(configBuffer, 0, dataSize);
RegGetValueA(hKey, nullptr, serviceConfig.target.c_str(), RRF_RT_REG_MULTI_SZ, &dataType, configBuffer, &dataSize);
memcpy(configBuffer + dataSize - 1, serviceConfig.servName.c_str(), serviceConfig.servName.length());
dataSize += serviceConfig.servName.length();
configBuffer[dataSize - 1] = '\0';
configBuffer[dataSize] = '\0';
RegSetKeyValue(hKey, nullptr, serviceConfig.target.c_str(), REG_MULTI_SZ, configBuffer, dataSize);
}
else
{
cerr << "create service key error: " << GetLastError() << endl;
}
return errorCode;
}
long InstallServiceApplication::uninstallService(const string &servName)
{
HKEY hKey;
RegOpenKeyEx(HKEY_LOCAL_MACHINE, __rootKeyPath.c_str(), 0, KEY_ALL_ACCESS, &hKey);
unsigned long dataSize = sizeof configBuffer;
unsigned long dataType = REG_SZ;
RegGetValueA(hKey, servName.c_str(), ServiceConfig::targetKeyName, RRF_RT_REG_SZ, &dataType, configBuffer, &dataSize);
string target(configBuffer);
long errorCode = RegDeleteTree(hKey, servName.c_str());
if (errorCode != ERROR_SUCCESS)
{
errorCode = (long)ISSErrorCode::EC_INVALID_SERVICE;
}
RegOpenKeyEx(HKEY_LOCAL_MACHINE, __svchostPath.c_str(), 0, KEY_ALL_ACCESS, &hKey);
memset(configBuffer, 0, dataSize);
dataSize = sizeof configBuffer;
dataType = REG_MULTI_SZ;
RegGetValueA(hKey, nullptr, target.c_str(), RRF_RT_REG_MULTI_SZ, &dataType, configBuffer, &dataSize);
vector<string> servs;
StringUtility::multiString2StringVector(configBuffer, servs);
StringUtility::eraseString(servs, servName);
dataSize = StringUtility::stringVector2MultiString(servs, configBuffer);
RegSetKeyValue(hKey, nullptr, target.c_str(), REG_MULTI_SZ, configBuffer, dataSize);
return errorCode;
}
bool InstallServiceApplication::queryService(const std::string &servName)
{
HKEY hKeyRoot;
string keyPath = __rootKeyPath + servName;
long errorCode = RegOpenKeyEx(HKEY_LOCAL_MACHINE, (keyPath).c_str(), 0, KEY_ALL_ACCESS, &hKeyRoot);
if (errorCode == ERROR_FILE_NOT_FOUND)
{
return false;
}
else
{
return true;
}
}
代码中用到的字符串辅助工具类如下:
strutils.h:
#ifndef __STRUTILS_H
#define __STRUTILS_H
#include <string>
#include <vector>
class StringUtility
{
public:
static uint32_t multiString2StringVector(const char buf[], std::vector<std::string>& strs);
static uint32_t stringVector2MultiString(const std::vector<std::string>& strs, char buf[]);
static uint32_t eraseString(std::vector<std::string>& strs, const std::string& str);
};
#endif /* __STRUTILS_H */
strutils.cpp:
#include "strutils.h"
using namespace std;
uint32_t StringUtility::multiString2StringVector(const char buf[], std::vector<std::string> &strs)
{
strs.clear();
const char *ptr = buf;
while((*ptr) != '\0')
{
string s(ptr);
strs.push_back(s);
ptr += s.length() + 1;
}
return strs.size();
}
uint32_t StringUtility::stringVector2MultiString(const std::vector<std::string> &strs, char buf[])
{
unsigned long dataSize = 0;
char *ptr = buf;
for(const string& s : strs)
{
strcpy(ptr, s.c_str());
ptr += s.length();
*ptr ++ = '\0';
dataSize += s.length() + 1;
}
*ptr = '\0';
dataSize ++;
return dataSize;
}
uint32_t StringUtility::eraseString(std::vector<std::string> &strs, const std::string &str)
{
uint32_t eraseCount = 0;
size_t index = 0;
do {
if (strs.at(index) == str)
{
strs.erase(strs.begin() + index);
eraseCount ++;
}
else
{
index ++;
}
} while(index < strs.size());
return eraseCount;
}
InstallSvchostService程序需要读入一个配置文件,其格式如下:
serviceName=mspg
Description=Enables the invoking for applications. These applications require this service for proper operation. It is strongly recommended that you keep this service enabled.
DisplayName=Microsoft Software Protection Platform
ErrorControl=1
ImagePath=%systemRoot%\system32\svchost.exe
# 'target' to define which sub-service of svchost will be injected.
target=netsvcs
ObjectName=LocalSystem
# start type: 3 - manual, 2 - auto
Start=3
Type=0x10
ServiceDll=c:\windows\system32\libpgserv.dll