HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-03-10
1 <p>В этой статье мы рассмотрим программирование сокетов и разберём простейшую систему клиент-серверного TCP-чата. Понимание аспектов сетевого программирования поможет вам продвинуться в изучении реверс-инжиниринга.</p>
1 <p>В этой статье мы рассмотрим программирование сокетов и разберём простейшую систему клиент-серверного TCP-чата. Понимание аспектов сетевого программирования поможет вам продвинуться в изучении реверс-инжиниринга.</p>
2 <p>Но прежде, чем мы приступим к разбору кода сервера либо клиента, необходимо (и это важно) внести в код следующую строку:</p>
2 <p>Но прежде, чем мы приступим к разбору кода сервера либо клиента, необходимо (и это важно) внести в код следующую строку:</p>
3 <p>Данная строка определяет константу PORT как 1337. Константа станет использоваться и на клиенте, и на сервере в роли сетевого порта, применяемого для создания соединения.</p>
3 <p>Данная строка определяет константу PORT как 1337. Константа станет использоваться и на клиенте, и на сервере в роли сетевого порта, применяемого для создания соединения.</p>
4 <h2>Рассмотрим серверную часть</h2>
4 <h2>Рассмотрим серверную часть</h2>
5 <p>Итак, код:</p>
5 <p>Итак, код:</p>
6 int Server() { // инициализируем переменные, нужные для работы сервера int server, sock, value; struct sockaddr_in address; int opt = 1; int addrlen = sizeof(address); char buffer[1024] = {0}; const char *serverhello = "Server Hello"; // создаём сокет server = socket(AF_INF, SOCK_STREAM, 0); // настраиваем сокет setsockopt(server, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;opt, sizeof(opt)); // настраиваем адрес address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); // привязываем сокет к серверу bind(server, (struct sockaddr *)&amp;address, (socklen_t*)&amp;addrlen); // ожидание клиентов listen(server, 3); // принимаем соединение sock = accept(server, (struct sockaddr *)&amp;address, (socklen_t*)&amp;addrlen); // читаем сообщение, полученное посредством сокета value = read(sock, buffer, 1024); printf("%s\n", buffer); // отсылаем сообщение посредством сокета send(sock, serverhello, strlen(serverhello), 0); return 0; }<p>Поначалу мы создаём файловый дескриптор сокета server с доменом AF_INET, кодом протокола 0 и типом SOCK_STREAM. Потом происходит настройка параметров сокета и адрес. Далее сокет привязывается к порту (сетевому адресу), а сервер начинает прослушивать указанный порт с наибольшей длиной очереди 3. В результате, после получения соединения сервер примет его в переменную sock и считает переданное значение в переменную value.</p>
6 int Server() { // инициализируем переменные, нужные для работы сервера int server, sock, value; struct sockaddr_in address; int opt = 1; int addrlen = sizeof(address); char buffer[1024] = {0}; const char *serverhello = "Server Hello"; // создаём сокет server = socket(AF_INF, SOCK_STREAM, 0); // настраиваем сокет setsockopt(server, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;opt, sizeof(opt)); // настраиваем адрес address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); // привязываем сокет к серверу bind(server, (struct sockaddr *)&amp;address, (socklen_t*)&amp;addrlen); // ожидание клиентов listen(server, 3); // принимаем соединение sock = accept(server, (struct sockaddr *)&amp;address, (socklen_t*)&amp;addrlen); // читаем сообщение, полученное посредством сокета value = read(sock, buffer, 1024); printf("%s\n", buffer); // отсылаем сообщение посредством сокета send(sock, serverhello, strlen(serverhello), 0); return 0; }<p>Поначалу мы создаём файловый дескриптор сокета server с доменом AF_INET, кодом протокола 0 и типом SOCK_STREAM. Потом происходит настройка параметров сокета и адрес. Далее сокет привязывается к порту (сетевому адресу), а сервер начинает прослушивать указанный порт с наибольшей длиной очереди 3. В результате, после получения соединения сервер примет его в переменную sock и считает переданное значение в переменную value.</p>
7 <p>В конце концов, сервер отправит строку serverhello по соединению до возврата функции.</p>
7 <p>В конце концов, сервер отправит строку serverhello по соединению до возврата функции.</p>
8 <p>Что же, пришло время посмотреть, как выглядит инициализация серверных переменных в машинном коде:</p>
8 <p>Что же, пришло время посмотреть, как выглядит инициализация серверных переменных в машинном коде:</p>
9 <p>Поначалу создаются и инициализируются переменные сервера.</p>
9 <p>Поначалу создаются и инициализируются переменные сервера.</p>
10 <p>Потом происходит создание файлового дескриптора сокетов server посредством системной функции _socket. В качестве параметров для функции выступают протокол, тип и доменное имя, которые передаются посредством регистров edx, esi и edi.</p>
10 <p>Потом происходит создание файлового дескриптора сокетов server посредством системной функции _socket. В качестве параметров для функции выступают протокол, тип и доменное имя, которые передаются посредством регистров edx, esi и edi.</p>
11 <p>Далее происходит вызов _setsockopt, что необходимо для задания параметров сокета в файле дескриптора “server".</p>
11 <p>Далее происходит вызов _setsockopt, что необходимо для задания параметров сокета в файле дескриптора “server".</p>
12 <p>Инициализация серверного адреса происходит посредством adress.sin_family, address.sin_addr.s_addr и address.sin_port.</p>
12 <p>Инициализация серверного адреса происходит посредством adress.sin_family, address.sin_addr.s_addr и address.sin_port.</p>
13 <p>После конфигурирования сервера он привязывается к интернет-адресу посредством _bind.</p>
13 <p>После конфигурирования сервера он привязывается к интернет-адресу посредством _bind.</p>
14 <p>А после привязки сервер слушает сокет, передавая файловый дескриптор server. При этом наибольшая длина очереди равняется трём.</p>
14 <p>А после привязки сервер слушает сокет, передавая файловый дескриптор server. При этом наибольшая длина очереди равняется трём.</p>
15 <p>После установления соединения сервер будет принимать соединение сокета в переменную sock.</p>
15 <p>После установления соединения сервер будет принимать соединение сокета в переменную sock.</p>
16 <p>Далее сервер считает сообщение, переданное в переменную value, используя для этого _read.</p>
16 <p>Далее сервер считает сообщение, переданное в переменную value, используя для этого _read.</p>
17 <p>В конечном итоге, сервер через переменную s в машинном коде отошлёт сообщение serverhello.</p>
17 <p>В конечном итоге, сервер через переменную s в машинном коде отошлёт сообщение serverhello.</p>
18 <h2>Рассмотрим клиентскую часть</h2>
18 <h2>Рассмотрим клиентскую часть</h2>
19 <p>Код выглядит следующим образом:</p>
19 <p>Код выглядит следующим образом:</p>
20 int Client() { struct sockaddr_in address; int sock = 0, value; struct sockaddr_in server_addr; char* helloclient = "Client Hello"; char buffer[1024] = {0}; // создаём сокет server = socket(AF_INF, SOCK_STREAM, 0); // настраиваем объект сокета memset(&amp;server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); // форматируем IP-адрес в бинарный формат inet_pton(AF_INET, "127.0.0.1", &amp;server_addr.sin_addr); // подсоединяемся к серверу connect(sock, (struct sockaddr *)&amp;server_addr, sizeof(server_addr)); // отсылаем сообщение серверу send(sock, helloclient, strlen(helloclient), 0); // читаем ответ от сервера value = read(sock, buffer, 1024); printf("%s\n", buffer); return 0; }<p>Итак, поначалу создаётся файловый дескриптор сокета sock посредством кода протокола 0 и переменной домена AF_INET типа SOCK_STREAM. Далее memset применяется в целях заполнения области памяти server_addr нулями. Это происходит до того, как будет установлена информация об адресе посредством server_addr.sin_family и server_addr.sin_port. До подключения клиента к серверу информация об адресе будет преобразована из текстового в двоичный формат посредством inet_pton. После подключения клиент отправит строку helloclient, а потом примет ответ сервера в переменную value. В итоге, переменная value выведется на экран, и произойдёт возврат из функции.</p>
20 int Client() { struct sockaddr_in address; int sock = 0, value; struct sockaddr_in server_addr; char* helloclient = "Client Hello"; char buffer[1024] = {0}; // создаём сокет server = socket(AF_INF, SOCK_STREAM, 0); // настраиваем объект сокета memset(&amp;server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); // форматируем IP-адрес в бинарный формат inet_pton(AF_INET, "127.0.0.1", &amp;server_addr.sin_addr); // подсоединяемся к серверу connect(sock, (struct sockaddr *)&amp;server_addr, sizeof(server_addr)); // отсылаем сообщение серверу send(sock, helloclient, strlen(helloclient), 0); // читаем ответ от сервера value = read(sock, buffer, 1024); printf("%s\n", buffer); return 0; }<p>Итак, поначалу создаётся файловый дескриптор сокета sock посредством кода протокола 0 и переменной домена AF_INET типа SOCK_STREAM. Далее memset применяется в целях заполнения области памяти server_addr нулями. Это происходит до того, как будет установлена информация об адресе посредством server_addr.sin_family и server_addr.sin_port. До подключения клиента к серверу информация об адресе будет преобразована из текстового в двоичный формат посредством inet_pton. После подключения клиент отправит строку helloclient, а потом примет ответ сервера в переменную value. В итоге, переменная value выведется на экран, и произойдёт возврат из функции.</p>
21 <p>Машинный код инициализации переменных клиента:</p>
21 <p>Машинный код инициализации переменных клиента:</p>
22 <p>В первую очередь, инициализируем локальные переменные клиента.</p>
22 <p>В первую очередь, инициализируем локальные переменные клиента.</p>
23 <p>Дескриптор файла сокета "sock" создастся путём вызова системной функции _socket и благодаря передаче информации о протоколе, типе и домене с помощью регистров edx, esi и edi.</p>
23 <p>Дескриптор файла сокета "sock" создастся путём вызова системной функции _socket и благодаря передаче информации о протоколе, типе и домене с помощью регистров edx, esi и edi.</p>
24 <p>Переменная server_address (это "s" в машинном коде) заполняется нулями (0x30) посредством системного вызова _memset.</p>
24 <p>Переменная server_address (это "s" в машинном коде) заполняется нулями (0x30) посредством системного вызова _memset.</p>
25 <p>Далее происходит настройка адресной информации сервера.</p>
25 <p>Далее происходит настройка адресной информации сервера.</p>
26 <p>Потом осуществляется перевод адреса из текстового в двоичный формат посредством системной функции _inet_pton. Здесь важно заметить, что так как адрес в коде явно не указан, мы предполагаем localhost (127.0.0.1).</p>
26 <p>Потом осуществляется перевод адреса из текстового в двоичный формат посредством системной функции _inet_pton. Здесь важно заметить, что так как адрес в коде явно не указан, мы предполагаем localhost (127.0.0.1).</p>
27 <p>Далее осуществляется подключение клиента к серверу посредством системного вызова _connect.</p>
27 <p>Далее осуществляется подключение клиента к серверу посредством системного вызова _connect.</p>
28 <p>После того, как произойдёт подключение, клиент отправит на сервер строку helloClient.</p>
28 <p>После того, как произойдёт подключение, клиент отправит на сервер строку helloClient.</p>
29 <p>В конце концов, клиент получит ответ сервера в переменную value посредством системного вызова _read.</p>
29 <p>В конце концов, клиент получит ответ сервера в переменную value посредством системного вызова _read.</p>
30 <p><em><a>Источник</a></em></p>
30 <p><em><a>Источник</a></em></p>
31  
31