CreateProcessA() 使用 C++ 中的 cmd 输出以管理员身份运行

问题描述

我在 dll 中创建了一个函数,以使用 CreateProcessA 在命令行中调用 exe。它将从在管理员用户下运行的其他一些外部 Exe 调用。但是该过程在打印任何内容之前退出。我检查了路径。我收到错误“管道已关闭”。

int execute(const char* cmd,std::string &output)
{

    Security_ATTRIBUTES saAttr;

    HANDLE g_hChildStd_OUT_Rd = NULL;
    HANDLE g_hChildStd_OUT_Wr = NULL;

    // Set the bInheritHandle flag so pipe handles are inherited. 

    saAttr.nLength = sizeof(Security_ATTRIBUTES);
    saAttr.bInheritHandle = TRUE;
    saAttr.lpSecurityDescriptor = NULL;

    // Create a pipe for the child process's STDOUT. 

    if (!CreatePipe(&g_hChildStd_OUT_Rd,&g_hChildStd_OUT_Wr,&saAttr,0))
    {
        appendLinetoFile(LOGFILENAME,"StdoutRd CreatePipe");
        return 1;
    }

    // Ensure the read handle to the pipe for STDOUT is not inherited.

    if (!SetHandleinformation(g_hChildStd_OUT_Rd,HANDLE_FLAG_INHERIT,"Stdout SetHandleinformation");
        return 1;
    }

    PROCESS_informatION piProcInfo;
    STARTUPINFOA siStartInfo;
    BOOL bSuccess = FALSE;

    // Set up members of the PROCESS_informatION structure. 

    ZeroMemory(&piProcInfo,sizeof(PROCESS_informatION));

    // Set up members of the STARTUPINFO structure. 
    // This structure specifies the STDIN and STDOUT handles for redirection.

    ZeroMemory(&siStartInfo,sizeof(STARTUPINFO));
    siStartInfo.cb = sizeof(STARTUPINFO);
    siStartInfo.hStdError = g_hChildStd_OUT_Wr;
    siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
    siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

    bSuccess = CreateProcessA(NULL,(char*)cmd,// command line 
        NULL,// process security attributes 
        NULL,// primary thread security attributes 
        TRUE,// handles are inherited 
        0,// creation flags 
        NULL,// use parent's environment 
        NULL,// use parent's current directory 
        &siStartInfo,// STARTUPINFO pointer 
        &piProcInfo);

    if (!bSuccess)
    {
        appendLinetoFile(LOGFILENAME,"Createprocess Failed");
        //printf("createprocess Failed\n");
        return 1;
    }
    else
    {
        // Close handles to the child process and its primary thread.
        // Some applications might keep these handles to monitor the status
        // of the child process,for example. 

        CloseHandle(piProcInfo.hProcess);
        CloseHandle(piProcInfo.hThread);

        // Close handles to the stdin and stdout pipes no longer needed by the child process.
        // If they are not explicitly closed,there is no way to recognize that the child process has ended.

        CloseHandle(g_hChildStd_OUT_Wr);

        DWORD dwRead,dwWritten;
        CHAR chBuf[1024];
        BOOL bSuccess = FALSE;

        bSuccess = ReadFile(g_hChildStd_OUT_Rd,chBuf,1024,&dwRead,NULL);
        if (!bSuccess || dwRead == 0)
        {
            appendLinetoFile(LOGFILENAME,"Cannot read from AgentInstaller.exe");
            return 1;
        }
        chBuf[dwRead] = '\0';
        output = chBuf;
    }
    return 0;
}

我也尝试更改属性权限中的清单文件

任何帮助或建议都会有所帮助

示例方法调用

std::string installcommand = "C:\\Program Files(x86)\\xxxx\\xxxx\\Installer.exe install"
std::string output;
execute(installcommand.c_str(),output);

需要满足三种情况

  • 该进程应在​​管理员权限下运行
  • 进程应该返回输出
  • 进程应该从它被调用的父进程获得访问权限。意味着不应使用用户名或密码/令牌。

解决方法

子进程继承父令牌,根据 How User Account Control Works:“子进程从父进程继承用户访问令牌。但是,父进程和子进程必须具有相同的完整性级别。 ”。因此,在默认 integrity levels 的通常情况下,从提升的进程启动时,子进程将运行提升。

然而,所发布代码的一个问题是父进程试图通过一次 ReadFile 调用一次性读取子管道。相反,应该在循环中读取管道,直到 ReadFile 失败并显示 ERROR_BROKEN_PIPE 以指示子进程结束(或以其他方式关闭管道)。

这相当于代码的这一部分:

        DWORD dwRead,dwWritten;
        CHAR chBuf[1024];
        BOOL bSuccess = FALSE;

        bSuccess = ReadFile(g_hChildStd_OUT_Rd,chBuf,1024,&dwRead,NULL);
        if (!bSuccess || dwRead == 0)
        {
            appendLineToFile(LOGFILENAME,"Cannot read from AgentInstaller.exe");
            return 1;
        }
        chBuf[dwRead] = '\0';
        output = chBuf;

被替换为:

        for (;;)
        {
            CHAR chBuf[4096]; DWORD dwRead;

            if (!ReadFile(g_hChildStd_OUT_Rd,sizeof(chBuf),NULL))
            {
                switch (GetLastError())
                {
                    case ERROR_BROKEN_PIPE: return 0;  // end-of-pipe,done

                    case ERROR_MORE_DATA:   continue;  // n/a here

                //  case ERROR_INVALID_HANDLE:
                //  case ERROR_PIPE_NOT_CONNECTED:
                    default:                return 1;  // error,check GetLastError
                }
            }
            if (dwRead == 0)                return 0;  // end-of-file,done

            output.append(chBuf,dwRead);
        }

以下示例代码需要提升才能写入 C:\Windows 目录:

std::string installcommand = "cmd /c echo Hello Pipe! >C:\\Windows\\testing.tmp & type C:\\Windows\\testing.tmp";
std::string output;
execute(installcommand.c_str(),output);
std::cout << output << std::endl;
  • 在常规非提升提示下运行时:

    C:\etc>sample
    
    Access is denied.
    The system cannot find the file specified.
    
  • 在提升的提示下运行时:

    C:\etc>sample
    
    Hello Pipe!