Русский
Русский
English
Статистика
Реклама

Сокеты

Recovery mode Древний костыль на старом костыле

23.09.2020 20:15:33 | Автор: admin

Начну без обиняков, как-то раз меня постигло откровение(ну не сильно мощное скажу по-честному) и возникла идея напечатать программу которая передает изображение с клиента на сервер. Достаточно просто да? Ну для программиста со стажем так и будет. Условия просты - не использовать сторонние библиотеки. В принципе немного сложнее, но если учесть что придется разбираться и искать примеры, ну такое себе занятие. Я решил, что эта задача мне по плечу. Плюс желательно чтобы было кода столько, чтобы его можно было запостить на форуме, в случае если понадобится помощь. В первую очередь мой взгляд пал на FTP, к слову ОС в которой разрабатывается Windows. Плюс FTP в том, что можно через него передать не только изображение, а любой файл. Скачав Filezilla Server, расшарив одну директорию на чтение/запись и создав юзера с паролем, попробовал подключится Filezilla Client все работало. Создал простенький пример кода на С/С++:

#include <iostream>void main(){FILE* fs;fopen_s(&fs, "1.txt", "w");if (fs){    fwrite("user\r\npassword\r\nsend D:\\share\\1.txt\r\nbye", 1, sizeof("user\r\npassword\r\nsend D:\\share\\1.txt\r\nbye"), fs);    fwrite("\000", 1, sizeof("\000"), fs);    fclose(fs);}system("ftp -s:1.txt 127.0.0.1");}

Если мне не изменяет память, то на локалхосте все работало, а при передаче по сети возникала ошибка в строчке с send. Что здесь удобно а)коротко б)не нужно устанавливать клиент, а использовать уже встроенную тулзу для ftp от майкрософта. Хотя по-мойму ее надо активировать через программы и компоненты. Если вы разберетесь в чем проблема данного метода и напишите в комментарии, будет отлично.

Не найдя ответа на куче форумов, я оставил данный код и решил использовать интерфейс для сетей сокеты. У меня уже был опыт передачи массива char'ов для другой программы. Кстати можете почитать у Таненбаума, Компьютерные сети, в главе про транспортный уровень. Там есть пример клиента и сервера, правда не для соединения "много клиентов - один сервер", а только "один клиент - один сервер". Поскольку передача идет через Интернет, то нужно зашифровать хоть как-то данные. Для этого используется блочный шифр - сеть Фейстеля. Плюсом на сервере надо сделать несколько(больше одного клиента) клиентов. Для этого воспользуемся Thread'ами, изображение для передачи будет брать скриншот экрана с клиента шифроваться и передаваться на сервер, на котором будет расшифровано и сразу же выведено на экран через дефолтную программу для открытия *.tga изображения.

Код сервера:

#include <iostream>#include <WinSock.h>#pragma comment (lib,"WS2_32.lib")#include <fstream>#include <algorithm>#include <string>#include <iterator>#include <vector>void error(const char* msg){    //perror(msg);    std::cout<<'\n'<<WSAGetLastError();    WSACleanup();    std::cin.ignore();    exit(1);}void bzero(char*buf, int l){    for (int i = 0; i < l; i++)        buf[i] = '\0';}struct arg_s{    unsigned char* buffer2;    bool exit;};char** buffer;struct arg_sa{    struct arg_s* lalk;    int current;};#define type struct arg_saint sockfd, * newsockfd;//слушающий и массив клиентских сокетовint buflen2 = 10292000;//максимальный размер изображения в байтах для RGBA*Width*Heightstruct sockaddr_in *cli_addr;int* clilen;int currentclient,cc;//сс-клиент по счету(для записи инкремента имени файла клиента изображения)typedef unsigned long long uint64_t;typedef unsigned int uint32_t;#define N 8//размер блока#define F32 0xFFFFFFFFuint32_t RK[N];//раундовые ключи#define size64 sizeof(uint64_t)#define ROR(x,n,xsize)((x>>n)|(x<<(xsize-n)))#define ROL(x,n,xsize)((x<<n)|(x>>(xsize-n)))#define RKEY(r)((ROR(K,r*3,size64*8))&F32)const uint64_t K = 0x96EA704CFB1CF671;//ключ шифрованияstruct hostent* server;uint32_t F(uint32_t subblk, uint32_t key){    return subblk + key;//функция шифрования}void createRoundKeys(){    for (int i = 0; i < N; i++)        RK[i] = (ROR(K, i * 8, size64 * 8)) & F32;}uint64_t decrypt(uint64_t c_block)//расшифровка блоков сетью фейстеля{    //select subblocks    uint32_t left = (c_block >> 32) & F32;    uint32_t right = c_block & F32;    uint32_t left_, right_;//subblock in the end of round    for (int r = N - 1; r >= 0; r--)    {        uint32_t fk = F(left, RK[r]);        left_ = left;        right_ = right ^ fk;        if (r > 0)//swap places to next round        {            left = right_;            right = left_;        }        else //last round not swap        {            left = left_;            right = right_;        }    }    //collect subblock in block    uint64_t block = left;    block = (block << 32) | (right & F32);    return block;}void session_(LPVOID args)//функция потока ля каждого клиента{    int current = currentclient++;    bzero((char*)&(cli_addr[current]), sizeof(&(cli_addr[current])));    newsockfd[current] = accept(sockfd, (struct sockaddr*)&(cli_addr[current]), &(clilen[current]));    if (newsockfd[current] < 0)    {        error("Error on accept\n");    }    char* s = new char[100];    int n = recv(newsockfd[current], s, 100, 0);    int buflen2 = atoi(s);//получаем число байтов изображения    FILE* f;    std::string name = "Screen";    cc++;    _itoa_s(cc, s, 100, 10);    name += s;    name += ".tga";    fopen_s(&f,name.c_str(), "wb");//создаем файл изображения с увеличиваещимся на 1 именем, чтобы не перезаписать    if (f != NULL)    {        unsigned char tgaHeader[12] = { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };        unsigned char header[6];        n = recv(newsockfd[current], buffer[current], sizeof(tgaHeader), 0);        fwrite((unsigned char*)buffer[current], 1, sizeof(tgaHeader), f);        bzero(buffer[current], buflen2);        n = recv(newsockfd[current], buffer[current],sizeof(header), 0);        fwrite((unsigned char*)buffer[current], 1, sizeof(header), f);//записали хидеры        bzero(buffer[current], buflen2);        n = recv(newsockfd[current], buffer[current], buflen2, 0);//получили байты самого изображения        //        //расшифровка байтов        createRoundKeys();        unsigned long long id;        std::vector<uint64_t>* plaintext = new std::vector<uint64_t>();        int i = 0;        while (i<buflen2)        {            memcpy(&id, (buffer[current]) + i, N);            plaintext->push_back(decrypt(id));            i += 8;        }        std::cout << "i=" << i << std::endl;        i = 0;        char str_[N + 1];        memset(str_, 0, N);        str_[N] = '\0';        for (std::vector<uint64_t>::iterator it = plaintext->begin(); it != plaintext->end(); ++it)        {            memcpy(str_, &*it, N);            fwrite((unsigned char*)str_, sizeof(unsigned char), N/*strlen(str_)*/, f);            i += 8;        }        std::cout << "i=" << i << std::endl;        //конец рашифровки байтов        //fwrite((unsigned char*)buffer[current], sizeof(char), buflen2, f);        fclose(f);    }    system(name.c_str());//открываем изображение *.tga встроенным редактором}int main(){    cc = 0;    WSADATA ws = { 0 };    if (WSAStartup(MAKEWORD(2, 2), &ws) == 0)    {        currentclient = 0;        int maxclients = 2;//максимальное число клиентов        cli_addr = new struct sockaddr_in[maxclients];        clilen = new int[maxclients];        buffer = new char* [maxclients];        for (int i = 0; i < maxclients; i++)        {            clilen[i] = sizeof(cli_addr[i]);        }        sockfd = socket(AF_INET, SOCK_STREAM, 0);//tcp сокет        if (sockfd < 0)            error("ERROR opening socket");        struct sockaddr_in serv_addr;        bzero((char*)&serv_addr, sizeof(serv_addr));        serv_addr.sin_family = AF_INET;        serv_addr.sin_addr.s_addr = INADDR_ANY;        int port = 30000;//порт        serv_addr.sin_port = htons(port);        if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)            error("ERROR on binding");        if (listen(sockfd, 10) < 0)            error("ERROR listen");        HANDLE* thread;//массив потоков для каждого клиента отдельный        struct arg_sa* args;        while (true)        {            newsockfd = new int[maxclients];            thread = (HANDLE*)malloc(sizeof(HANDLE) * maxclients);            args = new struct arg_sa[maxclients];            for (int i = 0; i < maxclients; i++)            {                args[i].lalk = new struct arg_s();                buffer[i] = new char[buflen2];            }            int i = -1;            while (++i < maxclients)            {                Sleep(1);                args[i].current = i;                args[i].lalk->exit = false;                thread[i] = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)(session_), args, 0, 0);            }                for (int i = 0; i < maxclients; i++)                    WaitForSingleObject(thread[i], INFINITE);//ждем завершения всех потоков            i = -1;            while (++i < maxclients)            {                shutdown(newsockfd[i], 0);                TerminateThread(thread[i], 0);            }            //delete[] newsockfd;            //free(thread);            currentclient = 0;            for (int i = 0; i < maxclients; i++)            {                //delete args[i].lalk;                //delete[] args[i].lalk->buffer;            }            //delete[] args;        }        shutdown(sockfd, 0);        WSACleanup();        return 0;    }    std::cin.ignore();}

Вкратце в вечном цикле создаются потоки для каждого клиента и ждут accept пока клиенты подключится. После чего WaitForSingleObject ждет пока они все передадут. У каждого клиента свой сокет и свой буфер передачи. То есть на сервере M+1 сокет, где M количество клиентов. После завершения всех передач, всё повторяется.

Теперь рассмотрим клиент:

#include <iostream>#include <WinSock.h>#include <vector>#pragma comment (lib,"WS2_32.lib")void error(const char* msg){    //perror(msg);    std::cout << '\n' << WSAGetLastError();    WSACleanup();    std::cin.ignore();    exit(1);}void bzero(char* buf, int l){    for (int i = 0; i < l; i++)        buf[i] = '\0';}typedef unsigned long long uint64_t;typedef unsigned int uint32_t;#define N 8#define F32 0xFFFFFFFFuint32_t RK[N];//раундовые ключи#define size64 sizeof(uint64_t)#define ROR(x,n,xsize)((x>>n)|(x<<(xsize-n)))#define ROL(x,n,xsize)((x<<n)|(x>>(xsize-n)))#define RKEY(r)((ROR(K,r*3,size64*8))&F32)const uint64_t K = 0x96EA704CFB1CF671;//ключ шифрованияvoid createRoundKeys(){    for (int i = 0; i < N; i++)        RK[i] = (ROR(K, i * 8, size64 * 8)) & F32;}uint32_t F(uint32_t subblk, uint32_t key){    return subblk + key;//функция шифрования}uint64_t encrypt(uint64_t block)//зашифровка блоков сетью Фейстеля{    //select subblocks    uint32_t left = (block >> 32) & F32;    uint32_t right = block & F32;    uint32_t left_, right_;//subblock in the end of round    for (int r = 0; r < N; r++)    {        uint32_t fk = F(left, RK[r]);        left_ = left;        right_ = right ^ fk;        if (r < N - 1)//swap places to next round        {            left = right_;            right = left_;        }        else//last round not swap        {            left = left_;            right = right_;        }    }    //collect subblock in block    uint64_t c_block = left;    c_block = (c_block << 32) | (right & F32);    return c_block;}int main(){    keybd_event(VK_LWIN, 0, 0, 0);    keybd_event('M', 0, 0, 0);    keybd_event('M', 0, KEYEVENTF_KEYUP, 0);    keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP, 0);//эти строки сворачивают все приложения    Sleep(1000);//чтобы сделать скриншот рабочего стола    WSADATA ws = { 0 };    if (WSAStartup(MAKEWORD(2, 2), &ws) == 0)    {        int sockfd;        sockfd = socket(AF_INET, SOCK_STREAM, 0);        struct sockaddr_in serv_addr, cli_addr;        bzero((char*)&serv_addr, sizeof(serv_addr));        bzero((char*)&cli_addr, sizeof(cli_addr));        serv_addr.sin_family = AF_INET;        const char* add = "127.0.0.1";//адрес сервера        serv_addr.sin_addr.s_addr = inet_addr(add);        int port = 30000;//порт        serv_addr.sin_port = htons(port);        int servlen = sizeof(serv_addr);        int n = connect(sockfd, (struct sockaddr*)&serv_addr, servlen);                //ниже код делает скриншот        HDC ScreenDC = GetDC(0);        HDC MemoryDC = CreateCompatibleDC(ScreenDC);        int ScreenHeight = GetSystemMetrics(SM_CYSCREEN);        int ScreenWidth = GetSystemMetrics(SM_CXSCREEN);        ScreenWidth = ((ScreenWidth - 1) / 4 + 1) * 4;        BITMAPINFO BMI;        BMI.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);        BMI.bmiHeader.biWidth = ScreenWidth;        BMI.bmiHeader.biHeight = ScreenHeight;        BMI.bmiHeader.biSizeImage = ScreenWidth * ScreenHeight * 3;        BMI.bmiHeader.biCompression = BI_RGB;        BMI.bmiHeader.biBitCount = 24;        BMI.bmiHeader.biPlanes = 1;        DWORD ScreenshotSize;        ScreenshotSize = BMI.bmiHeader.biSizeImage;        unsigned char* ImageBuffer;        HBITMAP hBitmap = CreateDIBSection(ScreenDC, &BMI, DIB_RGB_COLORS, (void**)&ImageBuffer, 0, 0);        SelectObject(MemoryDC, hBitmap);        BitBlt(MemoryDC, 0, 0, ScreenWidth, ScreenHeight, ScreenDC, 0, 0, SRCCOPY);        DeleteDC(MemoryDC);        ReleaseDC(NULL, ScreenDC);        FILE* sFile = 0;        unsigned char tgaHeader[12] = { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };        unsigned char header[6];        unsigned char tempColors = 0;        fopen_s(&sFile, "S.tga", "wb");        if (!sFile) {            exit(1);        }        header[0] = ScreenWidth % 256;        header[1] = ScreenWidth / 256;        header[2] = ScreenHeight % 256;        header[3] = ScreenHeight / 256;        header[4] = BMI.bmiHeader.biBitCount;        header[5] = 0;        fwrite(tgaHeader, 1, sizeof(tgaHeader), sFile);        fwrite(header, sizeof(header), 1, sFile);        //конец записали изображение в файл                //шифруем блоками полезную нагрузку изображения кроме хидеров        createRoundKeys();        std::vector<uint64_t>* msg = new std::vector<uint64_t>(),*crpt = new std::vector<uint64_t>();        unsigned long long id;        int i = 0;        while (i < BMI.bmiHeader.biSizeImage)        {            memcpy(&id, (ImageBuffer + i), N);            msg->push_back(id);            i += 8;        }        std::cout << "i=" << i << std::endl;         uint64_t cipher;        i = 0;        char str_[N + 1];        memset(str_, 0, N);        str_[N] = '\0';        for (std::vector<uint64_t>::iterator it = msg->begin(); it != msg->end(); ++it)        {            cipher = encrypt(*it);            memcpy(str_, &cipher, N);            fwrite((unsigned char*)str_, sizeof(unsigned char), N, sFile);            i += 8;        }        std::cout << "i=" << i << std::endl;        //        //fwrite(ImageBuffer, BMI.bmiHeader.biSizeImage, 1, sFile);        std::cout << BMI.bmiHeader.biSizeImage << std::endl;        fclose(sFile);        DeleteObject(hBitmap);        FILE* f;        fopen_s(&f, "S.tga", "rb");        int count = 0;        if (f != NULL)        {            while (getc(f) != EOF)                count++;//считаем байты изображения в счетчик чтобы потом передать            fclose(f);        }        count -= 18;        std::cout << count<< std::endl;        char* s = new char[100];        _itoa_s(count, s, 100, 10);        n = send(sockfd, s, 100, 0);//передаем счетчик        char* buffer = new char[count];        fopen_s(&f, "S.tga", "rb");        size_t bytes;        if (f != NULL)        {            memcpy(buffer, tgaHeader, sizeof(tgaHeader));            n = send(sockfd, buffer, sizeof(tgaHeader), 0);            bzero(buffer, count);            memcpy(buffer, header, sizeof(header));            n = send(sockfd, buffer, sizeof(header), 0);            bzero(buffer, count);//передаем хидеры            for(int i=0;i<18;i++)                fgetc(f);            bzero(buffer, count);            bytes = fread(buffer, sizeof(unsigned char), count, f);            n = send(sockfd,buffer, count, 0);//передаем шифрованные байты изображения            fclose(f);        }        Sleep(1000);        shutdown(sockfd, 0);        WSACleanup();        //system("del S.tga");        delete[] buffer,s;        return 0;    }    //std::cin.ignore();}

Вот результат работы клиента файл скриншота S.tga, зашифрованный

Видно, что это рабочий стол

А вот результат который был передан на сервер и расшифрован Screen.tga

Как видите обычная сеть Фейстеля не подходит для шифрования, но можно воспользоваться CBC и CFB методами, возможно будет лучше зашифровано, если честно не проверял.

Спасибо за внимание!

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru