Сломанный канал при чтении из канала дочернего процесса, запущенного от имени другого пользователя - PullRequest
0 голосов
/ 09 июля 2019

Я пытаюсь запустить дочерний исполняемый файл для запуска от имени вошедшего в систему пользователя (написанный на golang) из службы Windows, работающей от имени SYSTEM. Я использую метод CreateProcessAsUser, чтобы начать процесс. Я заметил, что дочерний процесс фактически запускается, но я не могу прочитать стандартный вывод этого. Я выполнил шаги, описанные в https://docs.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output, чтобы перенаправить stdin и stdout ребенка.

Я также установил разрешения для канала для SID всех пользователей, но все еще получаю ту же проблему.

Код родительского процесса выглядит следующим образом (после дальнейшего редактирования на основе предложений в комментариях)

#include "test.h"
#include <windows.h>
#include <winbase.h>
#include <userenv.h>
#include <tchar.h>
#include <wtsapi32.h>
#include <strsafe.h>
#include <processthreadsapi.h>

#define BUFSIZE 4096 
#define PROC_THREAD_ATTRIBUTE_HANDLE_LIST ( 2 | 0x00020000)
typedef struct _STARTUPINFOEXA {
    STARTUPINFOA StartupInfo;
    LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList;
} STARTUPINFOEXA, *LPSTARTUPINFOEXA;

void ReadAndHandleOutput(HANDLE hPipeRead, FILE* fp)
{
    CHAR lpBuffer[BUFSIZE];
    DWORD nBytesRead;
    DWORD bytesAvailable;
    DWORD bytesLeftInMsg;
    do
    {
        if (!PeekNamedPipe(hPipeRead, lpBuffer, sizeof(lpBuffer), &nBytesRead, &bytesAvailable, &bytesLeftInMsg))
        {
            if (GetLastError() == ERROR_BROKEN_PIPE) {
                fprintf(fp, "Broken Pipe \n");
                fflush(fp);
                break; // pipe done - normal exit path.
            }
            else {
                fprintf(fp, "ReadFileError %d\n", GetLastError()); // Something bad happened.
                fflush(fp);
            }
        }   
    } while(bytesLeftInMsg > 0);
    fprintf(fp, "GOT OUTPUT, %s -- %d -- %d -- %d\n", lpBuffer, nBytesRead, bytesAvailable, bytesLeftInMsg);
    fflush(fp);
}

bool LaunchProcess(char *process_path)
{
    FILE *fp = fopen("C:\\test.log", "a");
    DWORD SessionId = WTSGetActiveConsoleSessionId();
    if (SessionId == -1)  { // no-one logged in 
        fprintf(fp, "Session ID is -1\n");
        fclose(fp);
        return false;
    }

    HANDLE hToken;
    BOOL ok = WTSQueryUserToken(SessionId, &hToken);
    if (!ok) {
        fprintf(fp, "Unable to get the token for session id %d\n", SessionId);
        fclose(fp);
        return false;
    }

    void *environment = NULL;
    ok = CreateEnvironmentBlock(&environment, hToken, TRUE);

    if (!ok)
    {
        fprintf(fp, "Unable to create environment with session ID %d\n", SessionId);
        CloseHandle(hToken);
        fclose(fp);
        return false;
    }

    HANDLE hServerPipe = CreateNamedPipe("\\\\.\\Pipe\\test-pipe.em", PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE|PIPE_READMODE_BYTE, 1, 0, 0, 0, 0);
    HANDLE hClientPipe;
    if (hServerPipe != INVALID_HANDLE_VALUE)
    {
        static SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };

        hClientPipe = CreateFile("\\\\.\\Pipe\\test-pipe.em", FILE_GENERIC_READ|FILE_GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);

        if (hClientPipe == INVALID_HANDLE_VALUE)
        {
            fprintf(fp, "Error creating client handle %d", GetLastError());
            CloseHandle(hServerPipe);
            return false;
        }
    } else {
        fprintf(fp, "Error creating server handle %d", GetLastError());
        return false;
    }

    STARTUPINFOEXA si = { { sizeof(si) } };
    PROCESS_INFORMATION pi;
    si.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
    si.StartupInfo.hStdInput = si.StartupInfo.hStdOutput = si.StartupInfo.hStdError = hClientPipe;
    DWORD dwCreationFlags = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;

    BOOL fInit = FALSE;
    SIZE_T Size;
    if (!InitializeProcThreadAttributeList(0, 1, 0, &Size) && GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
            InitializeProcThreadAttributeList(si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)alloca(Size), 1, 0, &Size))
    {
        fInit = TRUE;
        if (UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &si.StartupInfo.hStdError, sizeof(HANDLE), 0, 0)) {
                dwCreationFlags |= EXTENDED_STARTUPINFO_PRESENT;
        }
    }

    fprintf(fp, "calling the process %s\n", process_path);
    fflush(fp);
    ok = CreateProcessAsUser(hToken, NULL, process_path, NULL, NULL, TRUE, dwCreationFlags, environment, NULL, &si.StartupInfo, &pi);
    fprintf(fp, "Running process %s and %d with PID %d\n", process_path, ok, pi.dwProcessId);
    fflush(fp);
    if (!ok) {
        wchar_t buf[BUFSIZE];
        FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
               NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
               buf, (sizeof(buf) / sizeof(wchar_t)), NULL);

        fprintf(fp, "Failed to create processes as user - %d, %d, %S, %s\n", SessionId, GetLastError(), buf, process_path);
        fclose(fp);

        return false;
    } else {
        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);
    }

    // Close pipe handles (do not continue to modify the parent).
    // You need to make sure that no handles to the write end of the
    // output pipe are maintained in this process or else the pipe will
    // not close when the child process exits and the ReadFile will hang.
    if (!CloseHandle(si.StartupInfo.hStdError)) {
        fprintf(fp, "CloseHandle - outputwrite Error %d\n", GetLastError());   
        fclose(fp);
        return false;    
    } 
    ReadAndHandleOutput(hServerPipe, fp);

    CloseHandle(hServerPipe);
    fclose(fp);
    return true;

Cleanup:
    fprintf(fp, "Something went wrong");
    fclose(fp);
    return false;
}

Код дочернего процесса выглядит следующим образом

package main

import (
    "fmt"
    "os"
    "time"
)

func main() {
    os.Stdout.Write([]byte("Hello from the GO side..."))
    f, err := os.OpenFile("C:\\log.text", os.O_APPEND|os.O_WRONLY, 0600)
    if err != nil {
        panic(err)
    }

    defer f.Close()
    var output = fmt.Sprintf("I'm running as %d launched by %d", os.Getpid(), os.Getppid())
    if _, err = f.WriteString(output); err != nil {
        panic(err)
    }
    os.Stdout.Close()
}

Я могу видеть текущий PID и PID родителя (службы) в файле log.text, однако я продолжаю получать сообщение об ошибке канала на родительской стороне.

Любые указатели приветствуются

...