windows守护进程

一、逻辑分析

1、windows没有fork函数,无法创建僵尸进程,使用打开非子进程的方式打开外部进程---打开外部进程

2、守护进程应该是单例的进程---》单例进程

3、守护进程应该是存在一个子线程不断的判断是否存在守护目标,若不存在(异常退出)则启动守护目标

4、当守护目标正常退出时,需要关闭守护进程

(1)、直接kill掉守护进程(守护进程没有做成病毒进程(系统进程)---记录下这里没找到方法)

(2)、进程间通讯实现关闭守护进程---有很多的方法博主使用的是命名管道

二、守护线程

#pragma once

#include <QThread>

class HiDaemon : public QThread
{
	Q_OBJECT
public:
	HiDaemon(QObject *parent = nullptr);
	~HiDaemon();

public slots:
	void start();
	void stop();

private:
	void run();

private:
	volatile bool			m_bRunning;

	QString					m_strFilePath;
	QString					m_strProducerName;
};
#include "HiDaemon.h"
#include <windows.h>
#include "tlhelp32.h"
#include "tchar.h"
#include "HiDaemonDef.h"
#include "HiUtils.h"
#include <QMessageBox>

HiDaemon::HiDaemon(QObject *parent)
	: QThread(parent)
	, m_bRunning(false)
	, m_strProducerName("")  // 这里自定义默认值
{
	m_strFilePath = QApplication::applicationDirPath();

	QStringList strList = QApplication::arguments();
	if (strList.size() > 1)
	{
		m_strProducerName = strList.at(1);
	}
}

HiDaemon::~HiDaemon()
{
}

void HiDaemon::start()
{
	if (m_bRunning)
	{
		return;
	}
	m_bRunning = true;

	QThread::start();
}

void HiDaemon::stop()
{
	m_bRunning = false;
	wait(1000);
}

void HiDaemon::run()
{
	STARTUPINFOA si;
	//进程对象
	PROCESS_INFORMATION pi;
	//初始化 
	ZeroMemory(&si, sizeof(si));
	si.cb = sizeof(si);
	ZeroMemory(&pi, sizeof(pi));

	//构造cmd执行守护进程的字符串
	char pCmd[MAX_PATH_NUM] = { 0 };

	{
		QFileInfo file(m_strFilePath + QDir::separator() + m_strProducerName);

		QString strFilePath = file.filePath();
		strcat_s(pCmd, strFilePath.toStdString().c_str());
	}
	
	while (m_bRunning)
	{
		//检查守护程序是否存在
		pi.hProcess = GetProcessHandleByName(m_strProducerName);

		if (pi.hProcess == nullptr)
		{
			// 创建子进程,判断是否执行成功
			if (CreateProcessA(NULL, pCmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
			{
				// 创建失败
			}
		}

		if (pi.hProcess != nullptr)
		{
			//无限等待子进程退出
			WaitForSingleObject(pi.hProcess, INFINITE);
			CloseHandle(pi.hProcess);
			CloseHandle(pi.hThread);
		}
		

		Sleep(100);
	}
}

二、命名管道实时通讯

#pragma once
#include <windows.h>
#include <QThread>

//
//HANDLE CreateNamedPipe(
//	LPCTSTR lpName,								// pipe name
//	DWORD dwOpenMode,							// pipe open mode
//	DWORD dwPipeMode,							// pipe-specific modes
//	DWORD nMaxInstances,						// maximum number of instances
//	DWORD nOutBufferSize,						// output buffer size
//	DWORD nInBufferSize,						// input buffer size
//	DWORD nDefaultTimeOut,						// time-out interval
//	LPSECURITY_ATTRIBUTES lpSecurityAttributes  // SD
//  );
//

class HiNamePipeServer : public QThread
{
	Q_OBJECT
public:
	HiNamePipeServer(QObject *parent = nullptr);
	~HiNamePipeServer();

	void start();
	void stop();

private slots:
	void onParse();

private:
	void creatPipe();
	void run();

signals:
	void parse();
	void stopDaemon();

private:
	HANDLE						 m_pHandle;
	HANDLE						 m_pEvent;
	volatile bool				 m_bRunning;
	char						 m_readbuffer[100];
	DWORD						 m_readLenth;
	bool						 m_resetStarted;
	QString						 m_strFilePath;
	QString						 m_strProducerName;
};
#include "HiNamePipeServer.h"
#include <windows.h>
#include <QDebug>
#include "HiDaemonDef.h"
#include "HiUtils.h"
HiNamePipeServer::HiNamePipeServer(QObject *parent)
	: QThread(parent)
	, m_bRunning(false)
	, m_pHandle(nullptr)
	, m_resetStarted(false)
{
	creatPipe();
	connect(this, &HiNamePipeServer::parse, this, &HiNamePipeServer::onParse, Qt::DirectConnection);
	m_strFilePath = QApplication::applicationDirPath();

	QStringList strList = QApplication::arguments();
	if (strList.size() > 1)
	{
		m_strProducerName = strList.at(1);
	}
}

HiNamePipeServer::~HiNamePipeServer()
{
	if (m_pHandle)
	{
		CloseHandle(m_pHandle);
	}
	CloseHandle(m_pEvent);
}

void HiNamePipeServer::start()
{
	if (m_bRunning)
	{
		return;
	}
	m_bRunning = true;
	QThread::start();
}

void HiNamePipeServer::stop()
{
	m_bRunning = false;
}

void HiNamePipeServer::onParse()
{
	QString strMsg = m_readbuffer;

	QStringList paramList = strMsg.split(WRITE_SPACER);
	if (paramList.size() != 2)
	{
		return;
	}
	
	PipeWriteType writeType = PipeWriteType(paramList.at(0).toInt());

	switch (writeType)
	{
	case PipeWriteType_ClientExit:
	{
		emit stopDaemon();
		qApp->quit();
		break;
	}
	case PipeWriteType_ClinetRestart:
	{
		if (m_resetStarted == false)
		{
			m_resetStarted = true;
			KillProcess(m_strProducerName);
		}
		break;
	}
	default:
		break;
	}

}

void HiNamePipeServer::creatPipe()
{
	m_pHandle = CreateNamedPipe(PIPE_NAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, PIPE_MAXINSTANCES, 0, 0, 1000, NULL);
	if (INVALID_HANDLE_VALUE == m_pHandle)
	{
		CloseHandle(m_pHandle);
		// 创建管道失败
		m_pHandle = nullptr;
	}
}

void HiNamePipeServer::run()
{
	// 这里暂时同步调用,可以考虑使用异步设置超时事件
	while (m_bRunning)
	{
		OVERLAPPED  ovlap;
		ZeroMemory(&ovlap, sizeof(OVERLAPPED));
		//2. 创建管道连接
		if (!ConnectNamedPipe(m_pHandle, &ovlap))
		{
			if (ERROR_IO_PENDING != GetLastError())
			{
				Sleep(100);
				continue;
			}
		}

		// 这个等待可能非常的久
		if (ReadFile(m_pHandle, m_readbuffer, 100, &m_readLenth, NULL) == TRUE)
		{
			emit parse();
		}
		DisconnectNamedPipe(m_pHandle);
	}
}

管道读取线程是阻塞的

三、主函数

#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <iostream>
#include <string>
#include <QString>
#include <QApplication>
#include "HiNamePipeServer.h"
#include "HiDaemon.h"
#include <QObject>
#include "HiSingleApplication.h"

LPCTSTR szAppClassName = TEXT("HiDaemon");
LPCTSTR szAppWindowName = TEXT("HiDaemon");


// HANDLE hProcess = ::OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessID);
// ::TerminateProcess(hProcess, 0);
// CloseHandle(hProcess);

using namespace std;

// 隐藏DOS黑窗口
#ifdef _MSC_VER
#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" )
#endif

int main(int argc, char* argv[])
{
	HiSingleApplication a(argc, argv);
	// 这里应该传入的产品类型,不同的产品,启动后可能动作不一致
	QCoreApplication::setOrganizationName("EnlightV");
	QCoreApplication::setApplicationName("HiDaemon");
	if (a.isRunning())
	{
		return 0;
	}

	HiDaemon *daemon = new HiDaemon;
	HiNamePipeServer *server = new HiNamePipeServer;

	QObject::connect(server, &HiNamePipeServer::stopDaemon, daemon, &HiDaemon::stop, Qt::DirectConnection);

	daemon->start();
	server->start();

	a.exec();

	return 0;
}

四、应用程序启动守护进程

// 获取守护进程句柄
HANDLE GetProcessHandleByName(const QString &name)
{
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (INVALID_HANDLE_VALUE == hSnapshot)
	{
		return NULL;
	}
	PROCESSENTRY32 pe = { sizeof(pe) };
	BOOL fOk;
	for (fOk = Process32First(hSnapshot, &pe); fOk; fOk = Process32Next(hSnapshot, &pe))
	{
		if (QString::fromWCharArray(pe.szExeFile) == name)
		{
			CloseHandle(hSnapshot);
			return GetProcessHandle(pe.th32ProcessID);
		}
	}
	return NULL;
}

//tempPath 是自定义的一个可写目录
void StartDaemon(const QString &tempPath)
{
	// 1、检测守护进程是否已经启动了
	if (GetProcessHandleByName(DAEMON_NAME) != nullptr)
	{
		return;
	}

	QString batFile = tempPath + QDir::separator() + "StartDaemon.bat";
	if (QFile(batFile).exists())
	{
		QFile(batFile).remove();
	}

	QFile file(batFile);
	if (file.open(QFile::ReadWrite))
	{
		QString strParam = QString("@echo off \n start /d  \"%1\" HiDaemond.exe  \"%2\" \n exit").arg(QCoreApplication::applicationDirPath()).arg(QFileInfo(QCoreApplication::applicationFilePath()).fileName());
		file.write(strParam.toLocal8Bit());
		file.close();

		QProcess p;
		p.start("cmd.exe", QStringList() << "/c" << batFile);
		if (p.waitForStarted())
		{
			p.waitForFinished(2000);
			return;
		}
	}
	else
	{
		QString strAppPath = QApplication::applicationDirPath();
		QStringList arguments;
		{
			QFileInfo file(QApplication::applicationFilePath());
			arguments.append(file.fileName());
		}
		QFileInfo deamonFile(strAppPath + QDir::separator() + DAEMON_NAME);
		if (deamonFile.exists())
		{
			std::wstring operate = QString("runas").toStdWString();//临时提升管理员权限
			std::wstring path = QCoreApplication::applicationDirPath().toStdWString();
			std::wstring appName = QFileInfo(QCoreApplication::applicationFilePath()).fileName().toStdWString();;
			std::wstring file = deamonFile.filePath().toStdWString();
			int ret = (int)ShellExecute(NULL, operate.c_str(), file.c_str(), appName.c_str(), path.c_str(), SW_SHOWNORMAL);
		}

	}
}

五、主端关闭时候,通讯给守护进程自行关闭

#pragma once

#include <windows.h>

class HiNamePipeClient
{
public:
	static HiNamePipeClient* getInstance();
	void writeFile(PipeWriteType type,  const QString & param = QString());

protected:
	HiNamePipeClient();
	~HiNamePipeClient();

private:
	bool openPipe();

private:
	HANDLE  m_pHandle;
};

inline HiNamePipeClient *pipeClient()
{
	return HiNamePipeClient::getInstance();
}
#include "HiNamePipeClient.h"
#include "guard.h"

enum PipeWriteType
{
	PipeWriteType_ClientExit = 0,							// 客户端正常退出
	PipeWriteType_ClinetRestart,							// 客户端重启
};

HiNamePipeClient* HiNamePipeClient::getInstance()
{
	static HiNamePipeClient instance;
	return &instance;
}

void HiNamePipeClient::writeFile(PipeWriteType type, const QString & param)
{
#if HIRENDER_C_SERIES
	if (openPipe())
	{
		DWORD dwWriteLen = 0;
		QString strParam = QString("%1#%2").arg((int)type).arg(param);
		if (WriteFile(m_pHandle, strParam.toStdString().c_str(), strParam.length(), &dwWriteLen, NULL))
		{
			// 写入成功
		}
		CloseHandle(m_pHandle);
		m_pHandle = nullptr;	
	}
	Sleep(200);
#endif
}

HiNamePipeClient::HiNamePipeClient()
	: m_pHandle(nullptr)
{

}

HiNamePipeClient::~HiNamePipeClient()
{

}

bool HiNamePipeClient::openPipe()
{
	if (m_pHandle)
	{
		CloseHandle(m_pHandle);
	}
	if (!WaitNamedPipe(PIPE_NAME, PIPE_TIMEOUT))
	{
		// 当前没有可利用的命名管道实例!
		return false;
	}

	m_pHandle = CreateFile(PIPE_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (INVALID_HANDLE_VALUE == m_pHandle)
	{
		// "打开命名管道失败!
		m_pHandle = NULL;
		return false;
	}
	return true;
}

六、强制关闭守护进程(提供方法、博主程序中未使用)


bool KillProcess(QString ProcessName)
{
	QProcess p;
	QString c = "taskkill /im " + ProcessName + " /f";    //exeFileName为要杀死的进程名
	p.execute(c);
	p.close();
	return true;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值