c++编写插件,让web浏览器调用URL Protocol方式打开windows自带远程桌面并自动登

用到的主要技术就是c++操作注册表和密码的加密

都知道普通的方法:windows键+R键,输入命令“mstsc”确认打开远程桌面连接窗口,输入需要远程的计算机控制的IP地址用户名和密码,登录远程云电脑控制。非常麻烦。直接上代码:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include 
#include 
#include 
#include 
#include 
#pragma comment(lib, "wsock32")
#pragma comment(lib,"crypt32.lib")//加密库文件
#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" )//隐藏黑窗口

std::string myLogPath;
void WriteLog(std::string msg);

/**********************************************
函数名:split
功能:字符串分割
参数1:字符串
参数2:分隔符
返回值:vector容器
**********************************************/
std::vector split(std::string str, std::string separator)
{
	std::vector result;
	int cutAt;
	while ((cutAt = str.find(separator)) != str.npos)
	{
		if (cutAt > 0)
		{
			result.push_back(str.substr(0, cutAt));
		}
		str = str.substr(cutAt + separator.size());
	}
	if (str.length() > 0)
	{
		result.push_back(str);
	}

	return result;
}

/**********************************************
函数名:AsciiToUnicode
功能:Ascii转Unicode
参数1:Ascii字符串
返回值:Unicode
**********************************************/
std::wstring AsciiToUnicode(const std::string& str) {
	// 预算-缓冲区中宽字节的长度    
	int unicodeLen = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, nullptr, 0);
	// 给指向缓冲区的指针变量分配内存    
	wchar_t *pUnicode = (wchar_t*)malloc(sizeof(wchar_t)*unicodeLen);
	// 开始向缓冲区转换字节    
	MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, pUnicode, unicodeLen);
	std::wstring ret_str = pUnicode;
	free(pUnicode);
	return ret_str;
}

/**********************************************
函数名:DeleteRegeditKey
功能:遍历删除注册表项
参数1:根项名称
参数2:注册表项名
**********************************************/
VOID DeleteRegeditKey(HKEY rootKey, LPCSTR subKey)
{
	HKEY hKey;
	if (RegOpenKeyExA(rootKey, subKey, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
	{
		while (true)
		{
			// 注册表项名称最长为251个有效字符
			char subKeyName[251] = "";
			if (RegEnumKeyA(hKey, 0, subKeyName, 251) != ERROR_SUCCESS)
				break;

			// 这里的2是路径分隔符"\"和结束符的占位
			size_t fullPathLen = strlen(subKey) + 2 + strlen(subKeyName);
			char *fullPath = new char[fullPathLen];
			strcpy(fullPath, subKey);
			strcat(fullPath, "\\");
			strcat(fullPath, subKeyName);
			DeleteRegeditKey(rootKey, fullPath);
			delete[] fullPath;
		}
		RegDeleteKeyA(rootKey, subKey);
		RegCloseKey(hKey);
	}
}

/**********************************************
函数名:ControlRegedit
功能:操作注册表
参数1:bat脚本文件名
参数2:新增注册项名(前端调用名称)
参数3:安装(TRUE)或卸载(FALSE)注册表
返回值:成功返回TRUE
**********************************************/
BOOL WriteRegedit(std::string ExeFileName="", std::string RegeditName="qtc",BOOL InstallRegedit = TRUE)
{
	HKEY hKey = NULL;
	DWORD dwDisposition;
	//获取文件名
	char szModuleFileName[MAX_PATH];
	GetModuleFileNameA(NULL,szModuleFileName,MAX_PATH);
	//获取运行文件目录
	char FilePath[MAX_PATH];
	GetCurrentDirectoryA(MAX_PATH, FilePath);

	//判断是否卸载注册表
	if(FALSE == InstallRegedit)
	{
		//删除注册表代码
		DeleteRegeditKey(HKEY_CLASSES_ROOT,RegeditName.c_str());
		return TRUE;
	}

	//创建或者打开qtc
	if(ERROR_SUCCESS == RegCreateKeyExA(HKEY_CLASSES_ROOT, RegeditName.c_str(), 0, NULL,REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS | KEY_WOW64_64KEY, NULL, &hKey, &dwDisposition))
	{
		//写入默认"qtcProtocol"和URLProtocol=""
		if (ERROR_SUCCESS != RegSetValueExA(hKey, NULL, 0, REG_SZ, (BYTE*)(RegeditName+"Protocol").c_str(), (RegeditName+"Protocol").size()) ||
			ERROR_SUCCESS != RegSetValueExA(hKey, "URL Protocol", 0, REG_SZ, (BYTE*)"", strlen("")))
		{
			return FALSE;
		}
		RegCloseKey(hKey);
	}
	//创建或者打开qtc\DefaultIcon
	if(ERROR_SUCCESS == RegCreateKeyExA(HKEY_CLASSES_ROOT, (RegeditName+"\\DefaultIcon").c_str(), 0, NULL,
		REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS | KEY_WOW64_64KEY, NULL, &hKey, &dwDisposition))
	{
		char strPath[MAX_PATH]={};
		if(ExeFileName.empty()){
			sprintf(strPath,"%s,%d",szModuleFileName,1);
		}else{
			sprintf(strPath,"%s\\%s,%d",FilePath,ExeFileName.c_str(),1);
		}
		//写入默认值"目录\open.bat,1"
		if (ERROR_SUCCESS != RegSetValueExA(hKey, NULL, 0, REG_SZ, (BYTE*)strPath, strlen(strPath)))
		{
			return FALSE;
		}
		RegCloseKey(hKey);
	}
	//创建或者打开qtc\\shell\\open\\command
	if(ERROR_SUCCESS == RegCreateKeyExA(HKEY_CLASSES_ROOT, (RegeditName+"\\shell\\open\\command").c_str(), 0, NULL,
		REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS | KEY_WOW64_64KEY, NULL, &hKey, &dwDisposition))
	{
		char strPath1[MAX_PATH]={};
		if(ExeFileName.empty()){
			sprintf(strPath1,"\"%s\" \"%%1\"",szModuleFileName);
		}else{
			sprintf(strPath1,"\"%s\\%s\" \"%%1\"",FilePath, ExeFileName.c_str());
		}
		//写入默认值"\"目录\open.bat\" \"%1\""
		if (ERROR_SUCCESS != RegSetValueExA(hKey, NULL, 0, REG_SZ, (BYTE*)strPath1, strlen(strPath1)))
		{
			return FALSE;
		}
		RegCloseKey(hKey);
	}

	return TRUE;
}

/**********************************************
函数名:EncryptionPassword
功能:加密
参数1:传入的字符串参数
参数2:传入加密后的字符串引用
返回值:BOOL
**********************************************/
BOOL EncryptionPassword(const std::wstring str, std::string &cipherText)
{
	cipherText.clear();
	if(str.empty())
		return FALSE;

	DATA_BLOB DataIn;
	DATA_BLOB DataOut;
	//mstsc.exe中使用的是unicode,所以必须做宽字符转换
	BYTE *pbDataInput =(BYTE *)str.c_str();
	DWORD cbDataInput = wcslen(str.c_str())*sizeof(wchar_t);
	DataIn.pbData = pbDataInput;
	DataIn.cbData = cbDataInput;
	if(CryptProtectData(&DataIn, str.c_str(), NULL, NULL, NULL, 0, &DataOut))
	{
		printf("The encryption phase worked.\n");
        int count=0;
		char tempStr[10]={};
        while ( count <= (int)DataOut.cbData ){
            // 因为一个unsigned int 占32位
            // 转换成成16进制要占两位
            // 所以这里需要用%02
			sprintf(tempStr,"%02X",DataOut.pbData[count]);
			cipherText += tempStr;
            //printf("%02X",DataOut.pbData[count]);
            count++;
		}
	}
	else
	{
		return FALSE;
	}
	return TRUE;
}
BOOL EncryptionPasswordA(const std::string str, std::string &cipherText)
{
	std::wstring _str = AsciiToUnicode(str);
	return EncryptionPassword(_str,cipherText);
}

/**********************************************
函数名:WriteRDPFile
功能:功能:写入RDP文件
参数1:RDP文件路径
参数2:解析好的前端数据
参数3:色彩深度
返回值:BOOL
**********************************************/
BOOL WriteRDPFile(std::string RDPFilePath,const std::vector &info, std::string colordepth="32")
{
	FILE *fp;   

	if((fp=fopen(RDPFilePath.c_str(),"wb"))==NULL)
    {
       return FALSE;
    }
	std::string tempStr;
	fputs("screen mode id:i:2\n",fp);
	fputs("smart sizing:i:1\n",fp);
	fputs("use multimon:i:0\n",fp);
	//动态修改屏幕分辨率
	int nScreenWidth, nScreenHeight;
    nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
	nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
	char strScreen[128]={};
	sprintf(strScreen,"desktopwidth:i:%d\n",nScreenWidth);
	fputs(strScreen,fp);
	memset(strScreen,0,sizeof(strScreen));
	sprintf(strScreen,"desktopheight:i:%d\n",nScreenHeight);
	fputs(strScreen,fp);

	fputs("session bpp:i:32\n",fp);
	fputs("winposstr:s:0,1,73,77,873,654\n",fp);
	fputs("compression:i:1\n",fp);
	fputs("keyboardhook:i:2\n",fp);
	fputs("audiocapturemode:i:0\n",fp);
	fputs("videoplaybackmode:i:1\n",fp);
	fputs("connection type:i:7\n",fp);
	fputs("networkautodetect:i:1\n",fp);
	fputs("bandwidthautodetect:i:1\n",fp);
	fputs("displayconnectionbar:i:1\n",fp);
	fputs("enableworkspacereconnect:i:0\n",fp);
	fputs("disable wallpaper:i:0\n",fp);
	fputs("allow font smoothing:i:0\n",fp);
	fputs("allow desktop composition:i:0\n",fp);
	fputs("disable full window drag:i:1\n",fp);
	fputs("disable menu anims:i:1\n",fp);
	fputs("disable themes:i:0\n",fp);
	fputs("disable cursor setting:i:0\n",fp);
	fputs("bitmapcachepersistenable:i:1\n",fp);
	//写入ip信息
	if(info.size() > 0){
		tempStr = "full address:s:"+info[0];
		//写入端口信息
		if(info.size() > 1){
			tempStr += ":";
			tempStr+=info[1];
		}
		tempStr+="\n";
	}
	else{
		tempStr = "full address:s:\n";
	}
	
	fputs(tempStr.c_str(),fp);
	fputs("audiomode:i:0\n",fp);
	fputs("redirectprinters:i:1\n",fp);
	fputs("redirectcomports:i:0\n",fp);
	fputs("redirectsmartcards:i:1\n",fp);
	fputs("redirectclipboard:i:1\n",fp);
	fputs("redirectposdevices:i:0\n",fp);
	fputs("autoreconnection enabled:i:1\n",fp);
	fputs("authentication level:i:2\n",fp);//注意:身份验证级别
	fputs("prompt for credentials:i:0\n",fp);
	fputs("negotiate security layer:i:1\n",fp);
	fputs("remoteapplicationmode:i:0\n",fp);
	fputs("alternate shell:s:\n",fp);
	fputs("shell working directory:s:\n",fp);
	fputs("gatewayhostname:s:\n",fp);
	fputs("gatewayusagemethod:i:4\n",fp);
	fputs("gatewaycredentialssource:i:4\n",fp);
	fputs("gatewayprofileusagemethod:i:0\n",fp);
	fputs("promptcredentialonce:i:0\n",fp);
	fputs("gatewaybrokeringtype:i:0\n",fp);
	fputs("use redirection server name:i:0\n",fp);
	fputs("rdgiskdcproxy:i:0\n",fp);
	fputs("kdcproxyname:s:\n",fp);
	fputs("drivestoredirect:s:\n",fp);
	//写入用户名和密码
	if(info.size() > 2)
	{
		tempStr = "username:s:" +info[2]+"\n";
		fputs(tempStr.c_str(),fp);
	}
	if(info.size() > 3)
	{
		std::string str;
		//密码加密
		if(TRUE == EncryptionPasswordA(info[3],str))
		{
			tempStr = "password 51:b:" +str+"\n";
			fputs(tempStr.c_str(),fp);
		}
	}
	fputs("enablecredsspsupport:i:0\n",fp);//注意:当存在此行时,您不必在建立远程桌面连接之前提供凭据
	fclose(fp);
	WriteLog("rdp文件更新成功");
	return TRUE;
}

/**********************************************
函数名:StartUpMSTSCLogin
功能:启动远程连接且自动登录
参数1:RDP文件路径
参数2:解析好的前端数据
**********************************************/
VOID StartUpMSTSCLogin(std::string RDPFilePath,const std::vector &info)
{
	char tempStr[MAX_PATH]={};
	//启动rdp
	memset(tempStr,0,sizeof(tempStr));
	sprintf(tempStr,"mstsc \"%s\" /console /v: ",RDPFilePath.c_str());
	
	if(info.size() > 0){
		strcat(tempStr,info[0].c_str());
		strcat(tempStr,":");
	}
	if(info.size() > 1){
		strcat(tempStr,info[1].c_str());
	}
	WriteLog("启动rdp服务");
	//system(tempStr);
	WinExec(tempStr,SW_HIDE);
}

/**********************************************
函数名:AnalyticInfo
功能:解析参数(ip,port,username,pwd,userid,time)
参数1:个数
参数2:二级指针
参数3:检测头
返回值:vector容器
**********************************************/
std::vector AnalyticInfo(int argc, char *argv[], std::string KeyWord = "qtc://")
{
	std::vector vec;
	//1.找到前端传过来的数据 获取关键字“qtc://"
	for(int i = 0; i< argc;++i)
	{
		if(strstr(argv[i],KeyWord.c_str()) && strstr(argv[i],"ip="))
		{
			//2.提取重要信息(ip,port,username,pwd,userid,time) 
			//格式:qtc://ip=10.10.1.1&port=22&uname=Administrator&pwd=AiTest&userid=?&time=?
			std::string webinfo = argv[i];
			if(webinfo[webinfo.size()-1] == '/')
			{
				webinfo.erase(webinfo.end() - 1);
			}

			auto vec1 = split(webinfo,"&");
			for(int i = 0;i < vec1.size(); ++i)
			{
				auto vec2 = split(vec1[i],"=");
				if(vec2.size()>1){
					vec.push_back(vec2[1]);
				}
				else{
					vec.push_back("");
				}
				vec2.clear();
			}
			vec1.clear();

			/*char tempStr[6][128]={};
			sscanf(argv[i],(KeyWord+"ip=%[^&]&port=%[^&]&uname=%[^&]&pwd=%[^&]&userid=%[^&]&time=%[^/]").c_str(),
				tempStr[0],tempStr[1],tempStr[2],tempStr[3],tempStr[4],tempStr[5]);
			for(int j = 0; j<6;++j)
			{
				vec.push_back(tempStr[j]);
			}//这种方法遇见空字符串,sscanf就会返回*/
			return vec;
		}
	}
	WriteLog("前端无数据传过来");
	return vec;
}

/*写入日志*/
void WriteLog(std::string msg)
{
	//获取本地时间  
	time_t timep;
    time (&timep);
	char tmp[128]={},logName[56]={};
	strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S",localtime(&timep) );

	FILE *fp;   
	if((fp=fopen(myLogPath.c_str(),"a+"))==NULL)
    {
       return;
    }
	time_t now_time=time(NULL);  
	
	std::string strLog = tmp;
	strLog = strLog + " " + msg + "\n";
	fputs(strLog.c_str(),fp);

	fclose(fp);
}

int main(int argc,char *argv[],char *envp[]){

	WriteLog("运行远程桌面插件");
	//0.分解运行程序路径
	char drive[_MAX_DRIVE] = {0};
    char dir[_MAX_DIR] = {0};
    char fname[_MAX_FNAME] = {0};
    char ext[_MAX_EXT] = {0};
    _splitpath( argv[0], drive, dir, fname, ext );
	//组建log日志路径
	time_t timep;
    time (&timep);
	char logName[56]={};
	strftime(logName, sizeof(logName), "%Y-%m-%d",localtime(&timep) );
	myLogPath = drive;
	myLogPath = myLogPath + dir + logName + ".log";

	//1.提取前端信息
	std::vector tempVec = AnalyticInfo(argc,argv);
	//3.定义rdp文件路径
	char rdpPath[MAX_PATH]={};
	strcpy(rdpPath,drive);
	strcat(rdpPath,dir);
	strcat(rdpPath,"rmstsc.rdp");
	//4.写入RDP文件
	WriteRDPFile(rdpPath,tempVec);
	//5.写入注册表信息
	WriteRegedit();
	//6.启动远程桌面自动登录
	StartUpMSTSCLogin(rdpPath,tempVec);

	for(int i = 0;i < tempVec.size();++i)
	{
		char strInfo[64]={};
		sprintf(strInfo,"前端参数%d 传入",i+1);
		tempVec[i]==""?strcat(strInfo,"null"):strcat(strInfo,"成功");

		WriteLog(strInfo);
	}

	//6.释放容器
	tempVec.clear();
	//system("pause");
	return 0;
}

这里我附上调用方法:
在浏览输入网址:

qtc://ip=10.10.1.1&port=22&uname=Administrator&pwd=AiTest&userid=?&time=?

其中qtc是名称,其中qtc://之后就是传入的参数

你可能感兴趣的:(c++编写插件,让web浏览器调用URL Protocol方式打开windows自带远程桌面并自动登)