问题描述
我在 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!