ПРИЕМ-ПЕРЕДАЧА ФАЙЛОВ ЧЕРЕЗ 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)); } }