mirror of
https://github.com/BeneficialCode/WinArk.git
synced 2026-10-18 21:51:35 +08:00
375 lines
9.6 KiB
C++
375 lines
9.6 KiB
C++
// WinArkSvc.cpp : This file contains the 'main' function. Program execution begins and ends there.
|
|
//
|
|
#include "pch.h"
|
|
#include "ServiceCommon.h"
|
|
|
|
#pragma comment(lib,"wtsapi32")
|
|
|
|
int Error(const char* msg, DWORD error = ::GetLastError());
|
|
void WINAPI ServiceMain(DWORD dwNumServicesArgs, LPTSTR* args);
|
|
DWORD WINAPI ServiceHandler(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext);
|
|
void SetStatus(DWORD status);
|
|
void HandleMessage(const AlarmMessage& msg);
|
|
void NTAPI OnTimerExpired(
|
|
_Inout_ PTP_CALLBACK_INSTANCE Instance,
|
|
_Inout_opt_ PVOID Context,
|
|
_Inout_ PTP_TIMER Timer);
|
|
int HandleCommandLine(int argc, const char* argv[]);
|
|
bool IsRunningElevated();
|
|
|
|
// service control
|
|
int InstallService();
|
|
int StartWinArkService();
|
|
int StopService();
|
|
int UninstallService();
|
|
|
|
|
|
SERVICE_STATUS_HANDLE g_hService;
|
|
SERVICE_STATUS g_Status;
|
|
HANDLE g_hStopEvent;
|
|
HANDLE g_hMainsolt;
|
|
PTP_TIMER g_Timer;
|
|
|
|
int Error(const char* msg, DWORD error) {
|
|
printf("%s (%u)\n", msg, error);
|
|
return 1;
|
|
}
|
|
|
|
int main(int argc,const char* argv[]){
|
|
if (argc > 1)
|
|
return HandleCommandLine(argc, argv);
|
|
|
|
// Note that the service name is typed as LPTSTR, meaning it should not be part of readonly
|
|
// memory, and that¡¯s why the name is first set to a variable on the stack(which is
|
|
// always read / write), before passing it to the structure.
|
|
WCHAR name[] = L"winark";
|
|
|
|
const SERVICE_TABLE_ENTRY table[] = {
|
|
{name,ServiceMain},
|
|
{nullptr,nullptr}
|
|
};
|
|
|
|
if (!::StartServiceCtrlDispatcher(table))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void WINAPI ServiceMain(DWORD dwNumServicesArgs, LPTSTR* args) {
|
|
g_Status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
|
g_Status.dwWaitHint = 500;
|
|
|
|
bool error = true;
|
|
do { // non-loop for easy breaking
|
|
g_hService = ::RegisterServiceCtrlHandlerEx(L"WinArkSvc",
|
|
ServiceHandler, nullptr);
|
|
if (!g_hService)
|
|
break;
|
|
|
|
g_hStopEvent = ::CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
|
if (!g_hStopEvent)
|
|
break;
|
|
|
|
SetStatus(SERVICE_START_PENDING);
|
|
|
|
// create a mailslotand configure
|
|
// its security descriptor to allow full control to the user running the service,
|
|
// with all other users having write/read access only
|
|
|
|
// create the Everyone SID
|
|
//
|
|
BYTE worldSid[SECURITY_MAX_SID_SIZE];
|
|
DWORD len = sizeof(worldSid);
|
|
auto pWorldSid = (PSID)worldSid;
|
|
|
|
if (!::CreateWellKnownSid(WinWorldSid, nullptr, pWorldSid, &len))
|
|
break;
|
|
|
|
//
|
|
// get SID of the user running this process
|
|
//
|
|
HANDLE hToken;
|
|
if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken))
|
|
break;
|
|
|
|
BYTE userBuffer[SECURITY_MAX_SID_SIZE + sizeof(TOKEN_USER) + sizeof(SID_AND_ATTRIBUTES)];
|
|
auto user = (TOKEN_USER*)userBuffer;
|
|
BOOL ok = ::GetTokenInformation(hToken, TokenUser, userBuffer, sizeof(userBuffer), &len);
|
|
::CloseHandle(hToken);
|
|
if (!ok)
|
|
break;
|
|
auto ownerSid = user->User.Sid;
|
|
|
|
//
|
|
// allocate and initialize a new security descriptor
|
|
//
|
|
PSECURITY_DESCRIPTOR sd = ::HeapAlloc(::GetProcessHeap(), 0, SECURITY_DESCRIPTOR_MIN_LENGTH);
|
|
if (!sd)
|
|
break;
|
|
if (!::InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// build ACEs
|
|
//
|
|
EXPLICIT_ACCESS ea[2] = { 0 };
|
|
ea[0].grfAccessPermissions = FILE_ALL_ACCESS;
|
|
ea[0].grfAccessMode = SET_ACCESS;
|
|
ea[0].grfInheritance = NO_INHERITANCE;
|
|
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
|
ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
|
|
ea[0].Trustee.ptstrName = (PWSTR)ownerSid;
|
|
|
|
ea[1].grfAccessPermissions =
|
|
FILE_GENERIC_WRITE | FILE_GENERIC_READ;
|
|
ea[1].grfAccessMode = SET_ACCESS;
|
|
ea[1].grfInheritance = NO_INHERITANCE;
|
|
ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
|
ea[1].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
|
|
ea[1].Trustee.ptstrName = (PWSTR)pWorldSid;
|
|
|
|
//
|
|
// create DACL from ACEs
|
|
//
|
|
PACL dacl;
|
|
if (ERROR_SUCCESS != ::SetEntriesInAcl(_countof(ea), ea,
|
|
nullptr, &dacl))
|
|
break;
|
|
|
|
//
|
|
// apply owner and DACL
|
|
//
|
|
if (!::SetSecurityDescriptorOwner(sd, ownerSid, FALSE))
|
|
break;
|
|
|
|
if (!::SetSecurityDescriptorDacl(sd, TRUE, dacl, FALSE))
|
|
break;
|
|
|
|
//
|
|
// create mailsolt
|
|
//
|
|
SECURITY_ATTRIBUTES sa = { sizeof(sa) };
|
|
sa.lpSecurityDescriptor = sd;
|
|
g_hMainsolt = ::CreateMailslot(L"\\\\.\\mailslot\\winark", 1024,
|
|
MAILSLOT_WAIT_FOREVER, &sa);
|
|
if (g_hMainsolt == INVALID_HANDLE_VALUE)
|
|
break;
|
|
|
|
//
|
|
// cleanup
|
|
//
|
|
::HeapFree(::GetProcessHeap(), 0, sd);
|
|
::LocalFree(dacl);
|
|
error = false;
|
|
} while (false);
|
|
|
|
if (error) {
|
|
// The most common error reporting
|
|
// mechanism used by services is writing to the Windows event log.
|
|
SetStatus(SERVICE_STOPPED);
|
|
return;
|
|
}
|
|
|
|
SetStatus(SERVICE_RUNNING);
|
|
|
|
DWORD msgSize, count;
|
|
AlarmMessage msg;
|
|
|
|
while (::WaitForSingleObject(g_hStopEvent, 1000) == WAIT_TIMEOUT) {
|
|
do
|
|
{
|
|
if (!::GetMailslotInfo(g_hMainsolt, nullptr, &msgSize, &count, nullptr))
|
|
break;
|
|
if (msgSize == MAILSLOT_NO_MESSAGE)
|
|
break;
|
|
|
|
DWORD bytes;
|
|
if (msgSize == sizeof(msg) &&
|
|
::ReadFile(g_hMainsolt, &msg, sizeof(msg), &bytes, nullptr)) {
|
|
HandleMessage(msg);
|
|
}
|
|
count--;
|
|
} while (count>0);
|
|
}
|
|
SetStatus(SERVICE_STOPPED);
|
|
|
|
::CloseHandle(g_hStopEvent);
|
|
::CloseHandle(g_hMainsolt);
|
|
}
|
|
|
|
void SetStatus(DWORD status) {
|
|
g_Status.dwCurrentState = status;
|
|
g_Status.dwControlsAccepted = status == SERVICE_RUNNING ? SERVICE_ACCEPT_STOP : 0;
|
|
::SetServiceStatus(g_hService, &g_Status);
|
|
}
|
|
|
|
void HandleMessage(const AlarmMessage& msg) {
|
|
switch (msg.Type)
|
|
{
|
|
case MessageType::SetAlarm:
|
|
if (g_Timer == nullptr)
|
|
g_Timer = ::CreateThreadpoolTimer(OnTimerExpired, nullptr, nullptr);
|
|
::SetThreadpoolTimer(g_Timer, (PFILETIME)&msg.Time, 0, 1000);
|
|
break;
|
|
|
|
case MessageType::CancelAlarm:
|
|
if (g_Timer) {
|
|
::CloseThreadpoolTimer(g_Timer);
|
|
g_Timer = nullptr;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
_Use_decl_annotations_
|
|
void NTAPI OnTimerExpired(PTP_CALLBACK_INSTANCE, PVOID, PTP_TIMER) {
|
|
WCHAR title[] = L"!!! ALARM !!!";
|
|
WCHAR msg[] = L"It's time to wake up!";
|
|
DWORD response;
|
|
|
|
::WTSSendMessage(nullptr, ::WTSGetActiveConsoleSessionId(),
|
|
title, sizeof(title), msg, sizeof(msg),
|
|
MB_OK | MB_ICONEXCLAMATION, 0, &response, FALSE);
|
|
}
|
|
|
|
DWORD WINAPI ServiceHandler(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext) {
|
|
switch (dwControl)
|
|
{
|
|
case SERVICE_CONTROL_STOP:
|
|
SetStatus(SERVICE_STOP_PENDING);
|
|
::SetEvent(g_hStopEvent);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool IsRunningElevated() {
|
|
HANDLE hToken;
|
|
if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken))
|
|
return false;
|
|
|
|
ULONG elevated = 0;
|
|
DWORD len;
|
|
::GetTokenInformation(hToken, TokenElevation, &elevated, sizeof(elevated), &len);
|
|
::CloseHandle(hToken);
|
|
|
|
return elevated ? true : false;
|
|
}
|
|
|
|
int HandleCommandLine(int argc, const char* argv[]) {
|
|
if (!IsRunningElevated()) {
|
|
printf("You must run elevated to configure the service.\n");
|
|
return 1;
|
|
}
|
|
|
|
if (!_stricmp(argv[1], "install") || !_stricmp(argv[1], "/install")
|
|
||!_stricmp(argv[1],"-install")) {
|
|
bool start = argc > 2 && _stricmp(argv[2], "start") == 0;
|
|
auto error = InstallService();
|
|
if (error == 0 && start)
|
|
return StartWinArkService();
|
|
return error;
|
|
}
|
|
if (!_stricmp(argv[1], "uninstall")||!_stricmp(argv[1],"-uninstall")
|
|
||!_stricmp(argv[1],"/uinstall")){
|
|
return UninstallService();
|
|
}
|
|
if (!_stricmp(argv[1], "start")||!_stricmp(argv[1],"-start")) {
|
|
return StartWinArkService();
|
|
}
|
|
if (!_stricmp(argv[1], "stop")|| !_stricmp(argv[1], "-stop")) {
|
|
return StopService();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int InstallService() {
|
|
SC_HANDLE hScm = ::OpenSCManager(nullptr, nullptr, SC_MANAGER_CREATE_SERVICE);
|
|
if (!hScm)
|
|
return Error("Failed to open SCM database");
|
|
|
|
WCHAR path[MAX_PATH];
|
|
::GetModuleFileName(nullptr, path, _countof(path));
|
|
|
|
SC_HANDLE hService = ::CreateService(hScm,
|
|
L"WinArkSvc",
|
|
L"WinArk Demo Service",
|
|
SERVICE_ALL_ACCESS,
|
|
SERVICE_WIN32_OWN_PROCESS,
|
|
SERVICE_DEMAND_START,
|
|
SERVICE_ERROR_NORMAL,
|
|
path,
|
|
L"Base",
|
|
nullptr, nullptr, L"NT AUTHORITY\\LocalService", nullptr);
|
|
if (!hService)
|
|
return Error("Failed to install service");
|
|
|
|
printf("Service installed successfully.\n");
|
|
|
|
::CloseServiceHandle(hService);
|
|
::CloseServiceHandle(hScm);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int StartWinArkService() {
|
|
SC_HANDLE hScm = ::OpenSCManager(nullptr, nullptr, SC_MANAGER_CONNECT);
|
|
if (!hScm)
|
|
return Error("Failed to open SCM database");
|
|
|
|
SC_HANDLE hService = ::OpenService(hScm, L"WinArkSvc", SERVICE_START);
|
|
if (!hService)
|
|
return Error("Failed to open service");
|
|
|
|
if (::StartService(hService, 0, nullptr))
|
|
printf("Service started successfully.\n");
|
|
else
|
|
return Error("Failed to start service");
|
|
::CloseServiceHandle(hService);
|
|
::CloseServiceHandle(hScm);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int StopService() {
|
|
auto hScm = ::OpenSCManager(nullptr, nullptr, SC_MANAGER_CONNECT);
|
|
if (!hScm)
|
|
return Error("Failed to open SCM database");
|
|
|
|
auto hService = ::OpenService(hScm, L"WinArkSvc", SERVICE_STOP);
|
|
if (!hService)
|
|
return Error("Failed to open service");
|
|
|
|
if (::ControlService(hService, SERVICE_CONTROL_STOP, &g_Status))
|
|
printf("Service stopped successfully.\n");
|
|
else
|
|
return Error("Failed to stop service");
|
|
::CloseServiceHandle(hService);
|
|
::CloseServiceHandle(hScm);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int UninstallService() {
|
|
auto hScm = ::OpenSCManager(nullptr, nullptr, SC_MANAGER_CONNECT);
|
|
if (!hScm)
|
|
return Error("Failed to open SCM database");
|
|
|
|
auto hService = ::OpenService(hScm, L"WinArkSvc", DELETE | SERVICE_QUERY_STATUS);
|
|
if (!hService)
|
|
return Error("Failed to open service");
|
|
SERVICE_STATUS status;
|
|
if (::QueryServiceStatus(hService, &status) && status.dwCurrentState == SERVICE_RUNNING)
|
|
StopService();
|
|
|
|
if (::DeleteService(hService))
|
|
printf("Service uninstalled successfully.\n");
|
|
else
|
|
Error("Failed to uninstall service");
|
|
::CloseServiceHandle(hService);
|
|
::CloseServiceHandle(hScm);
|
|
return 0;
|
|
} |