支持ftp等交互式命令的远程cmd

       一般远控cmd的实现方式都是通过创建管道重定向来实现,但是直接创建管道重定向后的程序是不支持ftp等交互命令的,这用个问题被称为管道嵌套,遇到这种情况通常的做法是创建一个屏幕缓存区,将stdout重定向到屏幕缓存后,用读取屏幕缓存的api(ReadConsoleOutput)直接读取屏幕中的输出结果,然后将输出传回服务端,但是这种方式有一个缺点,就是只有当一个cmd命令执行完之后并且已经显示再屏幕上才能读取,当遇到长命令(如 dir /s)执行结果超出了屏幕缓存区,就会造成传回的结果丢失一部分,为了解决这个问题,经过各种尝试,我发现管道经过很简单的处理可以解决这个问题。

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

 在微软提供的官方例子中我们可以发现,使用管道重定向分为两种情况,直接写入stdout不用做任何处理,但是重定向输入输出流则需要去掉读取流的继承。这样我们封装一个简单的类来实现这个过程:

class CMD
{
public:
	CMD(std::string path);
	~CMD();
public:
	static void cmdThread(void* pvPath);
	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* 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()
{
}




void CMD::cmdThread(void* pvPath)
{
	char* path = (char*)pvPath;
	CMD cmd(path);
	cmdOpen = true;
	while (cmdOpen)
	{
		Sleep(100);
		Client::clientptr->SendString(cmd.readCMD(), PacketType::CMDCommand);
	}
}


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: 点击打开链接












  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值