Sāksim bez lišķīšanās, kādu reizi mani pārņēma atklāsme (nu, ne pārāk spēcīga, jāatzīstu) un radās ideja uzrakstīt programmu, kas pārsūta attēlu no klienta uz serveri. Pietiekami vienkārši, vai ne? Nu, pieredzējušam programmētājam tā arī būs. Nosacījumi ir vienkārši — neizmantot trešo pušu bibliotēkas. Principā tas ir nedaudz sarežģītāk, bet, ņemot vērā, ka būs jāizpēta un jāmeklē piemēri, nu, tas būs diezgan garlaicīgs uzdevums. Es nolēmu, ka šo uzdevumu varu paveikt. Plus, vēlams, lai būtu tik daudz koda, lai to varētu ievietot forumā gadījumā, ja būtu nepieciešama palīdzība. Vispirms mana skatiena mērķis bija FTP, starp citu, OS, kurā tiek izstrādāta Windows. FTP priekšrocība ir tā, ka pa to var pārsūtīt ne tikai attēlus, bet arī jebkuru failu. Lejupielādējot Filezilla Server, izdalot vienu direktoriju lasīšanai/ierakstīšanai un izveidojot lietotāju ar paroli, izmēģināju pieslēgties ar Filezilla Client; viss darbojas. Izveidoju vienkāršu koda piemēru C/C++:
#include <iostream>
void main()
{
FILE* fs;
fopen_s(&fs, "1.txt", "w");
if (fs)
{
fwrite("userrnpasswordrnsend D:\share\1.txtrnbye", 1, sizeof("userrnpasswordrnsend D:\share\1.txtrnbye"), fs);
fwrite(" 00", 1, sizeof(" 00"), fs);
fclose(fs);
}
system("ftp -s:1.txt 127.0.0.1");
}Ja man atmiņa neapmāna, lokālajā hostā viss strādāja, bet, pārsūtot pa tīklu, radās kļūda rindiņā ar send. Šajā gadījumā ir ērti a) īsi b) nav nepieciešams uzstādīt klientu, bet izmantot jau iebūvēto FTP rīku no Microsoft. Lai gan, manuprāt, to vajag aktivizēt caur programmām un komponentēm. Ja jūs noskaidrosiet šī metode problēmu un uzrakstīsiet komentārā, tas būs lieliski.
Neatrodot atbildi uz kaudzi forumu, es atstāju šo kodu un nolēmu izmantot saskarni tīklam, sodus. Man jau bija pieredze char masīvu pārsūtīšanā citai programmai. Starp citu, varat izlasīt Tanenbauma grāmatā 'Datora tīkli' par transporta slāni. Tur ir klients un servera piemērs, gan ne savienojumam 'daudz klientu — viens serveris', bet tikai 'viens klients — viens serveris'. Tā kā pārsūtīšana notiek caur internetu, ir nepieciešams šifrēt datus. Šim nolūkam tiek izmantots bloku šifrējums — Feistela tīkls. Papildu: serveri jāizveido vairāk nekā viens klients. Tādēļ izmantojam pavedienus, attēlu pārsūtīšanai ņemsim ekrāna ekrānuzņēmumu no klienta, to šifrēsim un pārsūtīsim uz serveri, kur tas tiks atšifrēts un uzreiz parādīts uz ekrāna, izmantojot noklusējamo programmu *.tga attēlu atvēršanai.
Servera kods:
#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] = ' ';
}
struct arg_s
{
unsigned char* buffer2;
bool exit;
};
char** buffer;
struct arg_sa
{
struct arg_s* lalk;
int current;
};
#define type struct arg_sa
int sockfd, * newsockfd;//слушающий и массив клиентских сокетов
int buflen2 = 10292000;//максимальный размер изображения в байтах для RGBA*Width*Height
struct 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 0xFFFFFFFF
uint32_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 acceptn");
}
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] = ' ';
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();
}Īsumā, mūžīgajā ciklā katram klientam tiek izveidoti straumi un tiek gaidīts accept līdz klienti pieslēdzas. Pēc tam WaitForSingleObject gaida, līdz tie visi nosūta datus. Katram klientam ir savs sockets un savs pārsūtīšanas buferis. Tas nozīmē, ka serveri M+1 sokets, kur M ir klientu skaits. Pabeidzot visu pārsūtīšanu, viss atkārtojas.
Tagad aplūkosim klientu:
#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] = ' ';
}
typedef unsigned long long uint64_t;
typedef unsigned int uint32_t;
#define N 8
#define F32 0xFFFFFFFF
uint32_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] = ' ';
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();
}Šeit ir klienta darba rezultāts, ekrānuzņēmuma fails S.tga, šifrēts

Redzams, ka tas ir darba galds
Bet šeit ir rezultāts, kas tika nosūtīts uz serveri un atšifrēts Screen.tga

Kā redzat, parastā Feistela tīkls nav piemērots šifrēšanai, bet var izmantot CBC un CFB metodes, iespējams, tas tiks labāk šifrēts, godīgi sakot, neesmu pārbaudījis.
Paldies par uzmanību!
Avots: habr.com
