From c6f70136e124de22b7dc9e70f9498dba52492563 Mon Sep 17 00:00:00 2001 From: yuanyuanxiang <962914132@qq.com> Date: Wed, 16 Apr 2025 23:31:34 +0800 Subject: [PATCH] Implement service-generated authorization capability --- server/2015Remote/2015Remote.rc | Bin 55350 -> 56786 bytes server/2015Remote/2015RemoteDlg.cpp | 172 +++++++++++++++++++- server/2015Remote/2015Remote_vs2015.vcxproj | 5 + server/2015Remote/CPasswordDlg.cpp | 52 ++++++ server/2015Remote/CPasswordDlg.h | 33 ++++ server/2015Remote/pwd_gen.cpp | 117 +++++++++++++ server/2015Remote/pwd_gen.h | 20 +++ server/2015Remote/res/password.ico | Bin 0 -> 766 bytes server/2015Remote/resource.h | Bin 27272 -> 27632 bytes 9 files changed, 395 insertions(+), 4 deletions(-) create mode 100644 server/2015Remote/CPasswordDlg.cpp create mode 100644 server/2015Remote/CPasswordDlg.h create mode 100644 server/2015Remote/pwd_gen.cpp create mode 100644 server/2015Remote/pwd_gen.h create mode 100644 server/2015Remote/res/password.ico diff --git a/server/2015Remote/2015Remote.rc b/server/2015Remote/2015Remote.rc index 8ae451e5a11e88d18829d98eb499125b060ffa30..1e174dde6eafe0685ed415ee9252706175eaa0db 100644 GIT binary patch delta 353 zcmdnCf%(#I<_)`qCh-gL2QWA?1Os6>gFizMgUe(_K5zB{hD3&9hT_S81*9kUT-dNV zOSms!vVw*fhT?}-%G{<5h739k3P93evSZX?mdC+Q{3i27r%ev0K%GXt8V19XBJl(d-a7@#`Yz>95iL4w%iPi}Uq$a(^RzD4MQ zcmSl=V)MadYnjOt@|7@sTAexBpiE`5-vW`%Yz+y1py1#}3cJaUXTm4fZQ8V1;=CLu E0KQaLG5`Po delta 41 vcmcb#n|a#?<_)`qCi9%%u=$5@U%=!Er#5V6$qbR+tk9a}2j*?MqQD6NwQdw^ diff --git a/server/2015Remote/2015RemoteDlg.cpp b/server/2015Remote/2015RemoteDlg.cpp index f65f430..ec79a5e 100644 --- a/server/2015Remote/2015RemoteDlg.cpp +++ b/server/2015Remote/2015RemoteDlg.cpp @@ -21,6 +21,8 @@ #include #include "KeyBoardDlg.h" #include "InputDlg.h" +#include "CPasswordDlg.h" +#include "pwd_gen.h" #ifdef _DEBUG #define new DEBUG_NEW @@ -899,15 +901,177 @@ VOID CMy2015RemoteDlg::OnOnlineKeyboardManager() SendSelectedCommand(&bToken, sizeof(BYTE)); } +std::vector splitString(const std::string& str, char delimiter) { + std::vector result; + std::stringstream ss(str); + std::string item; + + while (std::getline(ss, item, delimiter)) { + result.push_back(item); + } + return result; +} + +std::string joinString(const std::vector& tokens, char delimiter) { + std::ostringstream oss; + + for (size_t i = 0; i < tokens.size(); ++i) { + oss << tokens[i]; + if (i != tokens.size() - 1) { // 在最后一个元素后不添加分隔符 + oss << delimiter; + } + } + + return oss.str(); +} + +#define REG_SETTINGS "Software\\YAMA\\Settings" + +// 写入字符串配置(多字节版) +bool WriteAppSettingA(const std::string& keyName, const std::string& value) { + HKEY hKey; + + LONG result = RegCreateKeyExA( + HKEY_CURRENT_USER, + REG_SETTINGS, + 0, + NULL, + 0, + KEY_WRITE, + NULL, + &hKey, + NULL + ); + + if (result != ERROR_SUCCESS) { + Mprintf("无法创建或打开注册表键,错误码: %d\n", result); + return false; + } + + result = RegSetValueExA( + hKey, + keyName.c_str(), + 0, + REG_SZ, + reinterpret_cast(value.c_str()), + static_cast(value.length() + 1) + ); + + RegCloseKey(hKey); + return result == ERROR_SUCCESS; +} + +// 读取字符串配置(多字节版) +bool ReadAppSettingA(const std::string& keyName, std::string& outValue) { + HKEY hKey; + + LONG result = RegOpenKeyExA( + HKEY_CURRENT_USER, + REG_SETTINGS, + 0, + KEY_READ, + &hKey + ); + + if (result != ERROR_SUCCESS) { + return false; + } + + char buffer[256]; + DWORD bufferSize = sizeof(buffer); + DWORD type = 0; + + result = RegQueryValueExA( + hKey, + keyName.c_str(), + nullptr, + &type, + reinterpret_cast(buffer), + &bufferSize + ); + + RegCloseKey(hKey); + + if (result == ERROR_SUCCESS && type == REG_SZ) { + outValue = buffer; + return true; + } + + return false; +} + +// 这个函数用来控制试用的次数,并不严谨;如果编译源码,则可以视情况自己去掉,采用其他更严密的授权方法 +int CanBuildClient() { + std::string freeTrail; + auto b = ReadAppSettingA("free_trial", freeTrail); + if (!b || freeTrail.empty()) + freeTrail = "10"; + return atoi(freeTrail.c_str()); +} + +bool UpdateFreeTrial(int n) { + return WriteAppSettingA("free_trial", std::to_string(n)); +} + void CMy2015RemoteDlg::OnOnlineBuildClient() { + auto n = CanBuildClient(); + if (n<=0) { + auto THIS_APP = (CMy2015RemoteApp*)AfxGetApp(); + auto settings = "settings", pwdKey = "Password"; + // 验证口令 + CPasswordDlg dlg; + std::string hardwareID = getHardwareID(); + std::string hashedID = hashSHA256(hardwareID); + std::string deviceID = getFixedLengthID(hashedID); + CString pwd = THIS_APP->m_iniFile.GetStr(settings, pwdKey, ""); + + dlg.m_sDeviceID = deviceID.c_str(); + dlg.m_sPassword = pwd; + if (pwd.IsEmpty() && IDOK != dlg.DoModal() || dlg.m_sPassword.IsEmpty()) + return; + + // 密码形式:20250209 - 20350209: SHA256 + auto v = splitString(dlg.m_sPassword.GetBuffer(), '-'); + if (v.size() != 6) + { + THIS_APP->m_iniFile.SetStr(settings, pwdKey, ""); + MessageBox("格式错误,请重新申请口令!", "提示", MB_ICONINFORMATION); + return; + } + std::vector subvector(v.begin() + 2, v.end()); + std::string password = v[0] + " - " + v[1] + ": " + PWD_HASH256; + std::string finalKey = deriveKey(password, deviceID); + std::string hash256 = joinString(subvector, '-'); + std::string fixedKey = getFixedLengthID(finalKey); + if (hash256 != fixedKey) { + THIS_APP->m_iniFile.SetStr(settings, pwdKey, ""); + if (pwd.IsEmpty() || (IDOK != dlg.DoModal() || hash256 != fixedKey)) { + if (!dlg.m_sPassword.IsEmpty()) + MessageBox("口令错误, 无法生成服务端!", "提示", MB_ICONWARNING); + return; + } + } + // 判断是否过期 + auto pekingTime = ToPekingTime(nullptr); + char curDate[9]; + std::strftime(curDate, sizeof(curDate), "%Y%m%d", &pekingTime); + if (curDate < v[0] || curDate > v[1]) { + THIS_APP->m_iniFile.SetStr(settings, pwdKey, ""); + MessageBox("口令过期,请重新申请口令!", "提示", MB_ICONINFORMATION); + return; + } + if (dlg.m_sPassword != pwd) + THIS_APP->m_iniFile.SetStr(settings, pwdKey, dlg.m_sPassword); + } // TODO: 在此添加命令处理程序代码 CBuildDlg Dlg; Dlg.m_strIP = ((CMy2015RemoteApp*)AfxGetApp())->m_iniFile.GetStr("settings", "localIp", ""); - CString Port; - Port.Format("%d", ((CMy2015RemoteApp*)AfxGetApp())->m_iniFile.GetInt("settings", "ghost")); - Dlg.m_strPort = Port; - Dlg.DoModal(); + int Port = ((CMy2015RemoteApp*)AfxGetApp())->m_iniFile.GetInt("settings", "ghost"); + Dlg.m_strPort = Port <= 0 ? "6543" : std::to_string(Port).c_str(); + if (IDOK == Dlg.DoModal() && n > 0) { + UpdateFreeTrial(n - 1); + } } diff --git a/server/2015Remote/2015Remote_vs2015.vcxproj b/server/2015Remote/2015Remote_vs2015.vcxproj index 63b477d..83b1f16 100644 --- a/server/2015Remote/2015Remote_vs2015.vcxproj +++ b/server/2015Remote/2015Remote_vs2015.vcxproj @@ -240,6 +240,7 @@ + @@ -248,6 +249,7 @@ + @@ -270,6 +272,7 @@ + @@ -278,6 +281,7 @@ + @@ -306,6 +310,7 @@ + diff --git a/server/2015Remote/CPasswordDlg.cpp b/server/2015Remote/CPasswordDlg.cpp new file mode 100644 index 0000000..9543699 --- /dev/null +++ b/server/2015Remote/CPasswordDlg.cpp @@ -0,0 +1,52 @@ +锘// CPasswordDlg.cpp: 瀹炵幇鏂囦欢 +// + +#include "stdafx.h" +#include "CPasswordDlg.h" +#include "afxdialogex.h" +#include "pwd_gen.h" +#include "2015Remote.h" + +// CPasswordDlg 瀵硅瘽妗 + +IMPLEMENT_DYNAMIC(CPasswordDlg, CDialogEx) + +CPasswordDlg::CPasswordDlg(CWnd* pParent /*=nullptr*/) + : CDialogEx(IDD_DIALOG_PASSWORD, pParent) + , m_sDeviceID(_T("")) + , m_sPassword(_T("")) +{ + +} + +CPasswordDlg::~CPasswordDlg() +{ +} + +void CPasswordDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialogEx::DoDataExchange(pDX); + DDX_Control(pDX, IDC_EDIT_DEVICEID, m_EditDeviceID); + DDX_Control(pDX, IDC_EDIT_DEVICEPWD, m_EditPassword); + DDX_Text(pDX, IDC_EDIT_DEVICEID, m_sDeviceID); + DDV_MaxChars(pDX, m_sDeviceID, 19); + DDX_Text(pDX, IDC_EDIT_DEVICEPWD, m_sPassword); + DDV_MaxChars(pDX, m_sPassword, 37); +} + + +BEGIN_MESSAGE_MAP(CPasswordDlg, CDialogEx) +END_MESSAGE_MAP() + + +BOOL CPasswordDlg::OnInitDialog() +{ + CDialogEx::OnInitDialog(); + + // TODO: 鍦ㄦ娣诲姞棰濆鐨勫垵濮嬪寲 + m_hIcon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_ICON_PASSWORD)); + SetIcon(m_hIcon, FALSE); + + return TRUE; // return TRUE unless you set the focus to a control + // 寮傚父: OCX 灞炴ч〉搴旇繑鍥 FALSE +} diff --git a/server/2015Remote/CPasswordDlg.h b/server/2015Remote/CPasswordDlg.h new file mode 100644 index 0000000..f8dbef6 --- /dev/null +++ b/server/2015Remote/CPasswordDlg.h @@ -0,0 +1,33 @@ +锘#pragma once + +#include +#include +#include "Resource.h" + +// 瀵嗙爜鐨勫搱甯屽 +#define PWD_HASH256 "61f04dd637a74ee34493fc1025de2c131022536da751c29e3ff4e9024d8eec43" + +// CPasswordDlg 瀵硅瘽妗 + +class CPasswordDlg : public CDialogEx +{ + DECLARE_DYNAMIC(CPasswordDlg) + +public: + CPasswordDlg(CWnd* pParent = nullptr); // 鏍囧噯鏋勯犲嚱鏁 + virtual ~CPasswordDlg(); + + enum { IDD = IDD_DIALOG_PASSWORD }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 鏀寔 + + DECLARE_MESSAGE_MAP() +public: + HICON m_hIcon; + CEdit m_EditDeviceID; + CEdit m_EditPassword; + CString m_sDeviceID; + CString m_sPassword; + virtual BOOL OnInitDialog(); +}; diff --git a/server/2015Remote/pwd_gen.cpp b/server/2015Remote/pwd_gen.cpp new file mode 100644 index 0000000..2d8bf29 --- /dev/null +++ b/server/2015Remote/pwd_gen.cpp @@ -0,0 +1,117 @@ +#include "stdafx.h" +#include "pwd_gen.h" + +#pragma comment(lib, "Advapi32.lib") + +// 执行系统命令,获取硬件信息 +std::string execCommand(const char* cmd) { + // 设置管道,用于捕获命令的输出 + SECURITY_ATTRIBUTES saAttr; + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + // 创建用于接收输出的管道 + HANDLE hStdOutRead, hStdOutWrite; + if (!CreatePipe(&hStdOutRead, &hStdOutWrite, &saAttr, 0)) { + Mprintf("CreatePipe failed with error: %d\n", GetLastError()); + return "ERROR"; + } + + // 设置启动信息 + STARTUPINFO si = { sizeof(si) }; + PROCESS_INFORMATION pi; + + // 设置窗口隐藏 + si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + si.wShowWindow = SW_HIDE; + si.hStdOutput = hStdOutWrite; // 将标准输出重定向到管道 + + // 创建进程 + if (!CreateProcess( + NULL, // 应用程序名称 + (LPSTR)cmd, // 命令行 + NULL, // 进程安全属性 + NULL, // 线程安全属性 + TRUE, // 是否继承句柄 + 0, // 创建标志 + NULL, // 环境变量 + NULL, // 当前目录 + &si, // 启动信息 + &pi // 进程信息 + )) { + Mprintf("CreateProcess failed with error: %d\n", GetLastError()); + return "ERROR"; + } + + // 关闭写入端句柄 + CloseHandle(hStdOutWrite); + + // 读取命令输出 + char buffer[128]; + std::string result = ""; + DWORD bytesRead; + while (ReadFile(hStdOutRead, buffer, sizeof(buffer), &bytesRead, NULL) && bytesRead > 0) { + result.append(buffer, bytesRead); + } + + // 关闭读取端句柄 + CloseHandle(hStdOutRead); + + // 等待进程完成 + WaitForSingleObject(pi.hProcess, INFINITE); + + // 关闭进程和线程句柄 + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + // 去除换行符和空格 + result.erase(remove(result.begin(), result.end(), '\n'), result.end()); + result.erase(remove(result.begin(), result.end(), '\r'), result.end()); + + // 返回命令的输出结果 + return result; +} + +// 获取硬件 ID(CPU + 主板 + 硬盘) +std::string getHardwareID() { + std::string cpuID = execCommand("wmic cpu get processorid"); + std::string boardID = execCommand("wmic baseboard get serialnumber"); + std::string diskID = execCommand("wmic diskdrive get serialnumber"); + + std::string combinedID = cpuID + "|" + boardID + "|" + diskID; + return combinedID; +} + +// 使用 SHA-256 计算哈希 +std::string hashSHA256(const std::string& data) { + HCRYPTPROV hProv; + HCRYPTHASH hHash; + BYTE hash[32]; + DWORD hashLen = 32; + std::ostringstream result; + + if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT) && + CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) { + + CryptHashData(hHash, (BYTE*)data.c_str(), data.length(), 0); + CryptGetHashParam(hHash, HP_HASHVAL, hash, &hashLen, 0); + + for (DWORD i = 0; i < hashLen; i++) { + result << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i]; + } + + CryptDestroyHash(hHash); + CryptReleaseContext(hProv, 0); + } + return result.str(); +} + +// 生成 16 字符的唯一设备 ID +std::string getFixedLengthID(const std::string& hash) { + return hash.substr(0, 4) + "-" + hash.substr(4, 4) + "-" + hash.substr(8, 4) + "-" + hash.substr(12, 4); +} + +std::string deriveKey(const std::string& password, const std::string& hardwareID) { + return hashSHA256(password + " + " + hardwareID); +} diff --git a/server/2015Remote/pwd_gen.h b/server/2015Remote/pwd_gen.h new file mode 100644 index 0000000..0dca58a --- /dev/null +++ b/server/2015Remote/pwd_gen.h @@ -0,0 +1,20 @@ +#pragma once +#include "stdafx.h" +#include +#include +#include +#include +#include +#include + + +// 对生成服务端功能进行加密 + +std::string getHardwareID(); + +std::string hashSHA256(const std::string& data); + +std::string getFixedLengthID(const std::string& hash); + +std::string deriveKey(const std::string& password, const std::string& hardwareID); + diff --git a/server/2015Remote/res/password.ico b/server/2015Remote/res/password.ico new file mode 100644 index 0000000000000000000000000000000000000000..de2dbbe1c6bf8099bcd8e5fe504b73cc64c488dd GIT binary patch literal 766 zcmZvau};G<5QeV=DV92<6JzPf7%51tlp%c>9>7uuTk4RLI>eh_1&M*71KC)3h_bQK z%|!y4-wq`y^ml&Scc0HmoDhxBX0r(qUDDeH(G_Ml@C;tUBjke^?7SEdgt;5gV@hq? zVhq5?i@Xmr7%ikicTVYPQYbl0NTFrXX@PvBNXTs`og;2YALA8=1y)NHwGKROQ2(NhON?0a;gbA1~AFb4u6v M?JDoN_Wsp>16>Z_C;$Ke literal 0 HcmV?d00001 diff --git a/server/2015Remote/resource.h b/server/2015Remote/resource.h index 8d759f38cb8fa64cfc38c477b4b505b19c796b33..bcdf5b202d2cb6fa89f78886bbc599923bffd1b4 100644 GIT binary patch delta 157 zcmeCU%J|_r<&WC&&mo~+NOJlQK#YVre7Azl{-SB5YKPX=co z?K!znNRio)!E&;rjQr#tQ7(RD*#L%cpu9X-UQkqevH~B^WF|uz5&~2bs3tyHE=y!{MWz5F0A1%MEC2ui delta 41 zcmV+^0M`HT*8zyt0kF^$0Wp&?9xapp6C9Jk6Izpi2n4g>71UIdSQHqu%xWY7ZgCKg