Программирование сокетов в контексте реверс-инжиниринга | OTUS
⚡ Подписка на курсы OTUS!
Интенсивная прокачка навыков для IT-специалистов!
Подробнее

Курсы

Программирование
Разработчик на Spring Framework
-5%
iOS Developer. Professional
-8%
Golang Developer. Professional
-6%
Базы данных
-12%
Agile Project Manager
-5%
C# ASP.NET Core разработчик
-6%
Android Developer. Basic
-10%
React.js Developer
-4%
MS SQL Server Developer
-8%
Scala-разработчик
-8%
Java Developer. Basic
-8%
Алгоритмы и структуры данных
-9%
Разработчик IoT
-13%
PostgreSQL
-8%
Подготовка к сертификации Oracle Java Programmer (OCAJP) Python Developer. Professional Разработчик программных роботов (RPA) на базе UiPath и PIX Unity Game Developer. Basic Разработчик голосовых ассистентов и чат-ботов Node.js Developer Интенсив «Оптимизация в Java» Супер - интенсив по паттернам проектирования Супер - интенсив по Kubernetes iOS Developer. Basic Супер-интенсив «СУБД в высоконагруженных системах» Супер-интенсив "Tarantool"
Инфраструктура
DevOps практики и инструменты
-12%
Базы данных
-12%
Network engineer. Basic
-10%
Network engineer
-4%
Инфраструктурная платформа на основе Kubernetes
-6%
Экспресс-курс по управлению миграциями (DBVC)
-10%
Мониторинг и логирование: Zabbix, Prometheus, ELK
-10%
Administrator Linux. Professional
-6%
Разработчик IoT
-13%
Основы Windows Server Cloud Solution Architecture Разработчик голосовых ассистентов и чат-ботов VOIP инженер Супер-практикум по работе с протоколом BGP NoSQL Супер-практикум по использованию и настройке GIT Супер-интенсив «СУБД в высоконагруженных системах» Экспресс-курс «IaC Ansible»
Специализации Курсы в разработке Подготовительные курсы
+7 499 938-92-02

Программирование сокетов в контексте реверс-инжиниринга

В этой статье мы рассмотрим программирование сокетов и разберём простейшую систему клиент-серверного TCP-чата. Понимание аспектов сетевого программирования поможет вам продвинуться в изучении реверс-инжиниринга.

Но прежде, чем мы приступим к разбору кода сервера либо клиента, необходимо (и это важно) внести в код следующую строку:

#define PORT 1337

Данная строка определяет константу PORT как 1337. Константа станет использоваться и на клиенте, и на сервере в роли сетевого порта, применяемого для создания соединения.

Рассмотрим серверную часть

Итак, код:

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, &opt, sizeof(opt));

    // настраиваем адрес
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // привязываем сокет к серверу
    bind(server, (struct sockaddr *)&address, (socklen_t*)&addrlen);

    // ожидание клиентов
    listen(server, 3);

    // принимаем соединение
    sock = accept(server, (struct sockaddr *)&address, (socklen_t*)&addrlen);

    // читаем сообщение, полученное посредством сокета
    value = read(sock, buffer, 1024);

    printf("%s\n", buffer);

    // отсылаем сообщение посредством сокета
    send(sock, serverhello, strlen(serverhello), 0);
    return 0;
}

Поначалу мы создаём файловый дескриптор сокета server с доменом AF_INET, кодом протокола 0 и типом SOCK_STREAM. Потом происходит настройка параметров сокета и адрес. Далее сокет привязывается к порту (сетевому адресу), а сервер начинает прослушивать указанный порт с наибольшей длиной очереди 3. В результате, после получения соединения сервер примет его в переменную sock и считает переданное значение в переменную value.

В конце концов, сервер отправит строку serverhello по соединению до возврата функции.

Что же, пришло время посмотреть, как выглядит инициализация серверных переменных в машинном коде:

reverse_dev_pic_21_1-20219-4f3ad6.jpeg

Поначалу создаются и инициализируются переменные сервера.

reverse_dev_pic_22_1-20219-e0bf02.jpeg

Потом происходит создание файлового дескриптора сокетов server посредством системной функции _socket. В качестве параметров для функции выступают протокол, тип и доменное имя, которые передаются посредством регистров edx, esi и edi.

reverse_dev_pic_23_1-20219-32257e.jpeg

Далее происходит вызов _setsockopt, что необходимо для задания параметров сокета в файле дескриптора “server».

reverse_dev_pic_24_2-20219-f086a8.jpg

Инициализация серверного адреса происходит посредством adress.sin_family, address.sin_addr.s_addr и address.sin_port.

reverse_dev_pic_25_2-20219-cfc0d4.jpg

После конфигурирования сервера он привязывается к интернет-адресу посредством _bind.

reverse_dev_pic_26_1-20219-6d7496.jpeg

А после привязки сервер слушает сокет, передавая файловый дескриптор server. При этом наибольшая длина очереди равняется трём.

reverse_dev_pic_27_2-20219-21d890.jpg

После установления соединения сервер будет принимать соединение сокета в переменную sock.

reverse_dev_pic_28_1-20219-5c8a7f.jpeg

Далее сервер считает сообщение, переданное в переменную value, используя для этого _read.

reverse_dev_pic_29_1-20219-cbffe4.jpeg

В конечном итоге, сервер через переменную s в машинном коде отошлёт сообщение serverhello.

Рассмотрим клиентскую часть

Код выглядит следующим образом:

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(&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", &server_addr.sin_addr);

    // подсоединяемся к серверу
    connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr));

    // отсылаем сообщение серверу
    send(sock, helloclient, strlen(helloclient), 0);

    // читаем ответ от сервера
    value = read(sock, buffer, 1024);

    printf("%s\n", buffer);
    return 0;
}

Итак, поначалу создаётся файловый дескриптор сокета sock посредством кода протокола 0 и переменной домена AF_INET типа SOCK_STREAM. Далее memset применяется в целях заполнения области памяти server_addr нулями. Это происходит до того, как будет установлена информация об адресе посредством server_addr.sin_family и server_addr.sin_port. До подключения клиента к серверу информация об адресе будет преобразована из текстового в двоичный формат посредством inet_pton. После подключения клиент отправит строку helloclient, а потом примет ответ сервера в переменную value. В итоге, переменная value выведется на экран, и произойдёт возврат из функции.

Машинный код инициализации переменных клиента:

reverse_dev_pic_29_2-20219-afec88.jpg

В первую очередь, инициализируем локальные переменные клиента.

reverse_dev_pic_30_1-20219-4f2454.jpeg

Дескриптор файла сокета «sock» создастся путём вызова системной функции _socket и благодаря передаче информации о протоколе, типе и домене с помощью регистров edx, esi и edi.

reverse_dev_pic_31_1-20219-0686c7.jpeg

Переменная server_address (это «s» в машинном коде) заполняется нулями (0x30) посредством системного вызова _memset.

reverse_dev_pic_32_1-20219-e068ef.jpeg

Далее происходит настройка адресной информации сервера.

reverse_dev_pic_33_1-20219-0856fc.jpeg

Потом осуществляется перевод адреса из текстового в двоичный формат посредством системной функции _inet_pton. Здесь важно заметить, что так как адрес в коде явно не указан, мы предполагаем localhost (127.0.0.1).

Далее осуществляется подключение клиента к серверу посредством системного вызова _connect.

reverse_dev_pic_35_1-20219-c487ad.jpeg

После того, как произойдёт подключение, клиент отправит на сервер строку helloClient.

reverse_dev_pic_36_1-20219-419a6d.jpeg

В конце концов, клиент получит ответ сервера в переменную value посредством системного вызова _read.

Источник

Не пропустите новые полезные статьи!

Спасибо за подписку!

Мы отправили вам письмо для подтверждения вашего email.
С уважением, OTUS!

Автор
2 комментария
Комментарий удален
Комментарий удален
Для комментирования необходимо авторизоваться