首页
社区
课程
招聘
[原创]cmd管道嵌套的一种解决方法
发表于: 2018-1-25 10:00 3821

[原创]cmd管道嵌套的一种解决方法

2018-1-25 10:00
3821
        远程cmd一般通过管道实现,但直接管道重定向后是不支持ftp等交互式命令的,这就叫管道嵌套,遇到这种情况处理交互式shell我们回选择直接从屏幕上读取输出(ReadConsoleOutput),但是这种方法可能会丢失数据(如回显超出屏幕缓存),经过一段时间研究发现,管道经过处理后是可以达到支持ftp命令的
代码:
//CMD.h
class CMD
{
public:
	CMD(std::string path);
	~CMD();
public:
	std::string readCMD();
	void writeCMD(std::string command);

private:
	void createChildProcess(std::string path);

public:
	static CMD * cmdptr;
	static bool cmdOpen;

private:
	HANDLE g_hChildStd_IN_Rd = NULL;
	HANDLE g_hChildStd_IN_Wr = NULL;
	HANDLE g_hChildStd_OUT_Rd = NULL;
	HANDLE g_hChildStd_OUT_Wr = NULL;

	static HANDLE g_hChildProcess;
	static HANDLE g_hChildThread;

	SECURITY_ATTRIBUTES saAttr;
};

//CMD.cpp
#include "CMD.h"

https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx

CMD* CMD::cmdptr = NULL;
bool CMD::cmdOpen = false;
HANDLE CMD::g_hChildProcess = NULL;
HANDLE CMD::g_hChildThread = NULL;

CMD::CMD(std::string path)
{
	saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
	saAttr.bInheritHandle = TRUE;
	saAttr.lpSecurityDescriptor = NULL;

	if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
		General::handleError(3, false);

	if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
		General::handleError(3, false);

	if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
		General::handleError(3, false);

	if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) //关键:参考微软文档,重定向流要去      
                                                                              //掉流的继承
		General::handleError(3, false);

	createChildProcess(path);
	cmdptr = this;
}

CMD::~CMD()
{
}

std::string CMD::readCMD()
{
	if (cmdOpen)
	{
		DWORD bytesAvailable = 0;
		DWORD bytesRead = 0;
		int intBytesAvailable = 0;
		char buffer[128] = "";
		std::string output;
		do
		{
			PeekNamedPipe(g_hChildStd_OUT_Rd, NULL, 0, NULL, &bytesAvailable, NULL);
			Sleep(50);
		} while (bytesAvailable <= 0);

		intBytesAvailable = bytesAvailable;
		while (intBytesAvailable > 0)
		{
			ReadFile(g_hChildStd_OUT_Rd, buffer, 127, &bytesRead, NULL);
			buffer[127] = '\0';
			output += buffer;
			intBytesAvailable -= bytesRead;
			if (intBytesAvailable <= 0)
				intBytesAvailable = 0;
			ZeroMemory(buffer, 128);
		}
		return output;
	}
	else
		return "CMD is not open";
}

void CMD::writeCMD(std::string command)
{
	if (cmdOpen)
	{
		command += '\n';
		if (!WriteFile(g_hChildStd_IN_Wr, command.c_str(), command.size(), NULL, NULL))
			Client::clientptr->SendString("Couldn't write command '" + command + "' to stdIn.", PacketType::Warning);
	}
	else
		Client::clientptr->SendString("Couldn't write to CMD: CMD not open", PacketType::Warning);
}

void CMD::createChildProcess(std::string path)
{
	PROCESS_INFORMATION piProcInfo;
	STARTUPINFO siStartInfo;
	BOOL bSuccess = FALSE;

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

	// Create the child process. 
	bSuccess = CreateProcess(path.c_str(), NULL, NULL,
		NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &siStartInfo, &piProcInfo);

	if (!bSuccess)
		General::handleError(3, false);
	else
	{
		CloseHandle(piProcInfo.hProcess);
		CloseHandle(piProcInfo.hThread);
	}
	g_hChildProcess = piProcInfo.hProcess;
	g_hChildThread = piProcInfo.hThread;
}
附上一个测试Demo:

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 0
支持
分享
最新回复 (2)
雪    币: 216
活跃值: (250)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
顶一个.
我又想到个问题,  正常CMD窗口上,按CTRL+C是可以结束正在执行的任务的,  但是用管道的话,  如何实现这个Ctrl+C?前几年遇到这个问题,  后面不了了之了.  楼主有时间研究下看看, 
2018-1-25 14:49
0
雪    币: 2642
活跃值: (114)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
传CTRL+C没必要用管道啊,SetConsoleCtrlHandler注册一个处理函数,UDP直接发送给远端发送一个CTRL_C_EVENT,远端接收到直接调用GenerateConsoleCtrlEvent就行
2018-1-25 14:59
0
游客
登录 | 注册 方可回帖
返回
//