c + + -tjenesten starter ikke efter Windows-afbrydelse og starter derefter

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg ønskede at lave en windows service i c ++ for at starte mine programmer som administrator hver gang brugeren logger ind uden at åbne vinduet UAC
Som det er første gang for mig at gøre det, brugte jeg projektet herfra: https://code.msdn.microsoft.com/windowsapps/CppWindowsService-cacf4948/view/SourceCode[8]


Jeg redigerede linje 74 i CppWindowsService.cpp til dette:


InstallService(
        SERVICE\_NAME,               // Name of service
        SERVICE\_DISPLAY\_NAME,       // Name to display
        SERVICE\_AUTO\_START,         // Service start type
        SERVICE\_DEPENDENCIES,       // Dependencies
        0,            // Service running account
        SERVICE\_PASSWORD            // Password of the account
        );


og tilføjede en kode til arbejdstrådens tråd i SampleService.cpp linje 101 for at blive sådan:


 void CSampleService::ServiceWorkerThread(void)
{
// Periodically check if the service is stopping.
PSID gpSidMIL\_High;
ConvertStringSidToSidW(L"S-1-16-12288", &gpSidMIL\_High);
DWORD userSessionID = WTSGetActiveConsoleSessionId();
HANDLE hToken, hToken2;


if (!OpenProcessToken(GetCurrentProcess(), TOKEN\_ALL\_ACCESS, &hToken)) WriteEventLogEntry(L"OpenProcessToken failed error", EVENTLOG\_ERROR\_TYPE);
if (!DuplicateTokenEx(hToken, MAXIMUM\_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hToken2)) WriteEventLogEntry(L"DuplicateTokenEx error", EVENTLOG\_ERROR\_TYPE);

if (!SetTokenInformation(hToken2, TokenSessionId, &userSessionID, sizeof(userSessionID))) WriteEventLogEntry(L"SetTokenInformation 1 error", EVENTLOG\_ERROR\_TYPE);

DWORD dwUIAccess = 1;
if (!SetTokenInformation(hToken2, TokenUIAccess, &dwUIAccess, sizeof(dwUIAccess))) WriteEventLogEntry(L"SetTokenInformation 2 error", EVENTLOG\_ERROR\_TYPE);

//Set "high" mandatory integrity level
TOKEN\_MANDATORY\_LABEL tml = { 0 };
tml.Label.Attributes = SE\_GROUP\_INTEGRITY;
tml.Label.Sid = gpSidMIL\_High;

if (!SetTokenInformation(hToken2, TokenIntegrityLevel, &tml, sizeof(TOKEN\_MANDATORY\_LABEL) + ::GetSidLengthRequired(1))) WriteEventLogEntry(L"SetTokenInformation 3 error", EVENTLOG\_ERROR\_TYPE);
LPVOID pEnv = 0;
if (!CreateEnvironmentBlock(&pEnv, hToken2, FALSE)) WriteEventLogEntry(L"CreateEnvironmentBlock error", EVENTLOG\_ERROR\_TYPE);

if (!ImpersonateLoggedOnUser(hToken2)) WriteEventLogEntry(L"ImpersonateLoggedOnUser error", EVENTLOG\_ERROR\_TYPE);

while (!m\_fStopping)
{
     STARTUPINFO stinfo = { 0 };
   PROCESS\_INFORMATION pinfo = { 0 };
   stinfo.cb = sizeof(stinfo);
   stinfo.lpDesktop = L"winsta0\default";
   if (!CreateProcessAsUserW(hToken2, L"path to exe that shows a message box", 0, 0, 0, FALSE, CREATE\_UNICODE\_ENVIRONMENT|CREATE\_BREAKAWAY\_FROM\_JOB, pEnv, L"cwd of the exe file", &stinfo, &pinfo))
   {
// after debugging I found that the error is coming from here 
       std::wstring error = L"CreateProcessAsUserW failed with error : ";
       error += std::to\_wstring(GetLastError());
       WriteEventLogEntry(wcsdup(error.c\_str()), EVENTLOG\_ERROR\_TYPE);
       Sleep(10000);
   }
   while (!m\_fStopping && pinfo.hProcess)
   {
       if(WaitForSingleObject(pinfo.hProcess, 1000) != WAIT\_TIMEOUT) break;
   }
   // ::Sleep(2000);  // Simulate some lengthy operations.
}


Problemet er, at denne tjeneste virker meget godt, efter at du har genstartet Windows eller startet det manuelt via sc.exe eller Service Control Manager, men ikke efter opstart fra tidligere nedlukning
Når jeg lukker computeren, kan jeg se exe i tjenesten, der kører i task manager, så jeg vidste, at tjenesten kører, og der er en fejl, der kommer fra en funktion, jeg brugte Windows-begivenhederne og logget fejlene, og jeg fandt endelig at fejlen kommer fra CreateProcessAsUser, som returnerer fejl 5 (Access nægtet)
Jeg ved ikke, hvor er problemet her, da tjenesten løber godt efter genstart eller ved at starte det manuelt

Bedste reference


ServiceMain kaldes ikke under hurtig opstart. Når hurtig opstart anvendes, var session 0 (inklusive kerne, drivere, alle tjenester) ikke afsluttet. Det dvalede session 0, når du lukker ned og fortsætter med at løbe som at hænge VirtualMachine sammen med bruger logout. Så din tjeneste holder status i stedet for at starte igen.


En enkel måde at håndtere dette på:
håndter SERVICE\_ACCEPT\_SESSIONCHANGE kontrolkode.


Her er et eksempel.


SERVICE\_STATUS gSvcStatus;
DWORD WINAPI HandleCtrlEx(
    DWORD dwControl,
    DWORD dwEventType,
    LPVOID lpEventData,
    LPVOID lpContext
)
{
    // Handle the requested control code. 
    switch (dwControl)
    {
        case SERVICE\_CONTROL\_SHUTDOWN:
        case SERVICE\_CONTROL\_STOP:
            ReportSvcStatus(SERVICE\_STOP\_PENDING, NO\_ERROR, 0);

            // Signal the service to stop.

            SetEvent(ghSvcStopEvent);
            ReportSvcStatus(gSvcStatus.dwCurrentState, NO\_ERROR, 0);

            break;

        case SERVICE\_CONTROL\_SESSIONCHANGE:
            if (dwEventType == WTS\_SESSION\_LOGON) {
                DWORD *data = HeapAlloc(GetProcessHeap(), HEAP\_ZERO\_MEMORY, sizeof(DWORD));
                *data = ((PWTSSESSION\_NOTIFICATION)lpEventData)->dwSessionId;
                HANDLE hThread = CreateThread(NULL, 0, RunProgram, data, 0, NULL);// remember to free memory in RunProgram
                if (!hThread) {
                    PrintLogA(LOG"can't create thread,error:\%u", GetLastError());
                }
                else {
                    CloseHandle(hThread);
                }
            }
            break;

        default:
            break;
    }
    return NO\_ERROR;
}

VOID ReportSvcStatus(DWORD dwCurrentState,
    DWORD dwWin32ExitCode,
    DWORD dwWaitHint)
{
    static DWORD dwCheckPoint = 1;

    // Fill in the SERVICE\_STATUS structure.

    gSvcStatus.dwCurrentState = dwCurrentState;
    gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
    gSvcStatus.dwWaitHint = dwWaitHint;

    if (dwCurrentState == SERVICE\_START\_PENDING)
        gSvcStatus.dwControlsAccepted = 0;
    else {
        gSvcStatus.dwControlsAccepted = SERVICE\_ACCEPT\_STOP;
        gSvcStatus.dwControlsAccepted |= SERVICE\_ACCEPT\_SESSIONCHANGE;// <--- remember add this.
    }

    if ((dwCurrentState == SERVICE\_RUNNING) ||
        (dwCurrentState == SERVICE\_STOPPED))
        gSvcStatus.dwCheckPoint = 0;
    else gSvcStatus.dwCheckPoint = dwCheckPoint++;

    // Report the status of the service to the SCM.
    SetServiceStatus(gSvcStatusHandle, &gSvcStatus);
}

VOID SvcInit(DWORD dwArgc, LPTSTR *lpszArgv)
{
    ghSvcStopEvent = CreateEvent(
        NULL,    // default security attributes
        TRUE,    // manual reset event
        FALSE,   // not signaled
        NULL);   // no name

    if (ghSvcStopEvent == NULL)
    {
        ReportSvcStatus(SERVICE\_STOPPED, NO\_ERROR, 0);
        return;
    }

    // Report running status when initialization is complete.
    ReportSvcStatus(SERVICE\_RUNNING, NO\_ERROR, 0);

    RunProgram();

    // Check whether to stop the service.
    WaitForSingleObject(ghSvcStopEvent, INFINITE);
    ReportSvcStatus(SERVICE\_STOPPED, NO\_ERROR, 0);
    return;
}

VOID WINAPI SvcMain(DWORD dwArgc, LPTSTR *lpszArgv){
    // Register the handler function for the service
    gSvcStatusHandle = RegisterServiceCtrlHandlerEx(
        SVCNAME,
        HandleCtrlEx,
        NULL);
    if (!gSvcStatusHandle)
    {
        SvcReportEventA(LOG"RegisterServiceCtrlHandler failed");
        return;
    }

    // These SERVICE\_STATUS members remain as set here

    gSvcStatus.dwServiceType = SERVICE\_WIN32\_OWN\_PROCESS;
    gSvcStatus.dwServiceSpecificExitCode = 0;
    // Report initial status to the SCM

    ReportSvcStatus(SERVICE\_START\_PENDING, NO\_ERROR, 3000);

    // Perform service-specific initialization and work.

    SvcInit(dwArgc, lpszArgv);
}

Andre referencer 1


disse er medlemmerne af klassen, der skal initialiseres:


PSID gpSidMIL\_High = 0;
DWORD userSessionID = 0;
HANDLE hToken = 0, hToken2 = 0;
DWORD dwUIAccess = 1;
TOKEN\_MANDATORY\_LABEL tml = { 0 };
LPVOID pEnv = 0;
STARTUPINFO stinfo = { 0 };
PROCESS\_INFORMATION pinfo = { 0 };


og i de beskyttede metoder lavede jeg denne metode:


void intialize();


og dette er funktionen:


void CSampleService::intialize() {
// the members may were used before , without assigning them to zero again all the functions below will fail , I don't know why ?
gpSidMIL\_High = 0;
userSessionID = 0;
hToken = 0, hToken2 = 0;
dwUIAccess = 1;
tml = { 0 };
pEnv = 0;
stinfo = { 0 };
pinfo = { 0 };

WriteEventLogEntry(L"initializing", EVENTLOG\_INFORMATION\_TYPE);

ConvertStringSidToSidW(L"S-1-16-12288", &gpSidMIL\_High);
userSessionID = WTSGetActiveConsoleSessionId();

if (!OpenProcessToken(GetCurrentProcess(), TOKEN\_ALL\_ACCESS, &hToken)) WriteErrorLogEntry(L"OpenProcessToken");
if (!DuplicateTokenEx(hToken, MAXIMUM\_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hToken2)) WriteErrorLogEntry(L"DuplicateTokenEx error");

if (!SetTokenInformation(hToken2, TokenSessionId, &userSessionID, sizeof(userSessionID))) WriteErrorLogEntry(L"SetTokenInformation 1 error");

if (!SetTokenInformation(hToken2, TokenUIAccess, &dwUIAccess, sizeof(dwUIAccess))) WriteErrorLogEntry(L"SetTokenInformation 2");

//Set "high" mandatory integrity level
//TOKEN\_MANDATORY\_LABEL tml = { 0 };
tml.Label.Attributes = SE\_GROUP\_INTEGRITY;
tml.Label.Sid = gpSidMIL\_High;

if (!SetTokenInformation(hToken2, TokenIntegrityLevel, &tml, sizeof(TOKEN\_MANDATORY\_LABEL) + ::GetSidLengthRequired(1))) WriteErrorLogEntry(L"SetTokenInformation 3");
LPVOID pEnv = 0;
if (!CreateEnvironmentBlock(&pEnv, hToken2, FALSE)) WriteErrorLogEntry(L"CreateEnvironmentBlock");
 }


endelig i arbejdstrådens tråd:


void CSampleService::ServiceWorkerThread(void)
{
WriteEventLogEntry(L"the thread of the service started", EVENTLOG\_INFORMATION\_TYPE);
intialize();
while (!m\_fStopping)
{
stinfo.cb = sizeof(stinfo);
   stinfo.lpDesktop = L"winsta0\default";
   // the service starts from here after fast boot
   if (!CreateProcessAsUserW(hToken2, L"C:\Users\user\Desktop\ConEmuTrap\test.exe", 0, 0, 0, FALSE, CREATE\_UNICODE\_ENVIRONMENT | CREATE\_BREAKAWAY\_FROM\_JOB, pEnv, L"C:\Users\user\Desktop\ConEmuTrap", &stinfo, &pinfo))
   {
       WriteErrorLogEntry(L"CreateProcessAsUserW 1");
       intialize();
   }
   while (!m\_fStopping && pinfo.hProcess)
   {
       if(WaitForSingleObject(pinfo.hProcess, 1000) != WAIT\_TIMEOUT) break;
   }
 }