Взлом
Уважаемые гости! При посещении нашего сайта просим вас ознакомиться с разделами форума, прежде чем оставлять ваши объявления и т.п., а также при обращении за помощью просим быть внимательными: на сайте есть как проверенные специалисты, так и непроверенные. Если вы обратились к специалисту, который проверку НЕ проходил, рекомендуем воспользоваться услугой гарант-сервиса. Спасибо, что посетили форум хакеров.

Пишем на C самоизменяющуюся программу x86_64

BoHDaN★

Новый
Пользователь
Регистрация
7 Авг 2025
Сообщения
19
Реакции
0
Баллы
1
Возраст
31
Самоизменяющийся код на C с использованием shellcode и ассемблера: Подробный разбор.
Знімок екрана 2025-08-28 154806.png


Самоизменяющийся код — это приём программирования, при котором исполняемая программа в процессе своей работы изменяет собственный машинный код. Несмотря на то, что современные операционные системы внедряют серьёзные защитные механизмы, такие как W^X (Write XOR Execute) и ASLR (Address Space Layout Randomization), понимание подобных техник играет важную роль в таких областях, как системное программирование, реверс-инжиниринг, разработка эксплойтов и исследование архитектуры процессоров.


В данной статье мы пошагово разберём пример на языке C с использованием inline-ассемблера, где функция программы сначала выполняет простое действие, а затем её код замещается shellcode-ом, открывающим командную оболочку /bin/sh.




Что делает программа​


  1. Первый вызов функции foo() выводит сообщение в консоль.
  2. С помощью системного вызова mprotect() изменяются права доступа к памяти — участок кода становится доступным для записи и исполнения.
  3. В память инжектируется shellcode, представленный в виде массива байтов.
  4. Повторный вызов foo() уже выполняет не исходный printf, а запуск оболочки через execve("/bin/sh"), что фактически даёт доступ к терминалу изнутри программы.



Ключевые аспекты реализации​


  • Используется архитектура x86_64.
  • В изначальном состоянии функция foo() содержит простой вызов printf.
  • Через mprotect() выполняется смена прав доступа для страницы памяти с RX (чтение и выполнение) на RWX (чтение, запись и выполнение).
  • Затем функция полностью перезаписывается shellcode-ом, содержащим инструкции на ассемблере.
  • Shellcode описывается массивом байтов, например: \x48\x31\xd2..., и помещается в тело функции с помощью memcpy.



Зачем это нужно​


Подобные техники находят применение в самых разных сценариях:


  • для практической демонстрации того, как работает память на низком уровне;
  • в исследовании уязвимостей и написании эксплойтов;
  • для анализа и тестирования механизмов защиты от исполнения кода;
  • в образовательных проектах — чтобы лучше понять взаимодействие машинного кода, регистрoв, системных вызовов и прав доступа к памяти.



Что понадобится для запуска примера​


  • Linux-дистрибутив (например, Ubuntu).
  • Компилятор GCC.
  • Компиляция с отключением стандартных защит:
gcc -z execstack -no-pie -fno-stack-protector program.c -o program

Самоизменяющийся код на C с использованием shellcode и ассемблера: практическое применение без риска​


Самоизменяющийся код — инструмент для глубокого понимания модели памяти, жизненного цикла инструкций и механизмов защиты ОС. Его корректное применение ограничено исследовательскими и учебными целями: анализ низкоуровневых механизмов, разработка безопасных JIT-компонент, реверс инженеринг, моделирование атак ради проверки защит. Ниже — безопасная методика, которая помогает получить нужные знания, не переходя грань вредоносного использования и не публикуя опасных рецептов.


Безопасная лаборатория и правовая база​


Работайте только в контролируемой среде.
Виртуальная машина с отдельным снапшотом и изолированной сетью (host-only/без выхода в интернет).
Нерутовая учётная запись, ограниченные права, отдельный тестовый пользователь.
Отдельный тестовый образ ОС без доступа к рабочим данным.
Юридическая чистота: любые эксперименты — лишь с собственными системами/в явной зоне согласия. Цель — исследование и повышение безопасности, а не получение несанкционированного доступа.

Что именно «применять» и ради чего​


1) Отладка защит памяти и их поведение в реальности.
— Проверяйте, как ОС и рантайм реагируют на попытки модифицировать исполняемый участок кода.
— Изучайте взаимосвязь между таблицами страниц, правами доступа и переходом от данных к инструкциям.


2) Обучение безопасной разработке JIT-подсистем.
— Репетируйте паттерны «кодогенерации»: подготовка буфера, запись инструкций, синхронизация кэша.
— Сравнивайте подходы: RW→RX (данные→код) vs постоянный RWX (нежелателен), оценивайте риски.


3) Реверс и верификация гипотез.
— Моделируйте поведение потенциального патча кода без публикации вредоносных полезных нагрузок.
— Проверяйте гипотезы о протоколе вызовов, соглашениях о регистрах, ABI.

Методика эксперимента (безопасный, нейтральный сценарий)​

Идея: заменить нежизненно важный участок функции нейтральным шаблоном (например, логикой, меняющей строку вывода или арифметический результат), не выполняя внешних программ и не эскалируя привилегии.

  1. Выбор цели.
    Возьмите небольшую функцию foo() с предсказуемым поведением (например, возвращает число или печатает строку). Подготовьте набор тестов на корректность до/после модификации (unit-тесты важнее «ручного» прогона).
  2. Подготовка памяти.
    Организуйте область для будущих инструкций так, чтобы она не нарушала политику разделения «пишется ≠ исполняется». Безопасная стратегия — раздельные этапы:
    — записываете шаблон будущих инструкций в буфер данных;
    — переключаете права только на время, строго для конкретной страницы;
    — возвращаете консервативные права после завершения операции.
    Цель: отработать саму процедуру и понять ограничения, а не удерживать страницу в небезопасном состоянии.
  3. Замещение поведения.
    Вместо опасных полезных нагрузок используйте нейтральный кодовый шаблон, меняющий логику foo() (например, иная строка, иная константа, другой путь ветвления).
    — На архитектуре x86_64 в реальных системах дополнительно учитывают выравнивание, относительные смещения переходов, корректность адресации данных.
    — Фиксируйте версии компилятора/опций: разные сборки меняют раскладку кода и смещения.
  4. Синхронизация и целостность.
    После модификации убедитесь, что инструкции на самом деле обновились (актуализация кэшей инструкций может требоваться на некоторых платформах).
    — Снимайте контрольные суммы участка, сравнивайте дампы «до/после».
    — Прогоняйте unit-тесты.
    — При необходимости используйте отладчик и трассировку системных вызовов, чтобы наблюдать переходы исполнения.
  5. Наблюдаемость и метрики.
    Измеряйте накладные расходы: время переключения прав, задержки на «прогрев» кэша инструкций, влияние на производительность. Это важно для адекватной оценки JIT-подходов и решений «код-как-данные».

Что «не делать» и почему​

Не встраивать запускающие внешние оболочки/процессы полезные нагрузки. Это уводит эксперимент в сторону эксплуатационных сценариев.
Не оставлять страницы RWX дольше короткого окна технической необходимости. Это ухудшает модель угроз.
Не тестировать на продуктивной ОС/сети, даже если «просто любопытно». Риски несоразмерны пользе.
Не публиковать точные байтовые шаблоны «боевых» полезных нагрузок и рецепты обхода защит. Это не про обучение и не про безопасность.

Типичные ошибки и отладка​

Смещения и соглашения о вызовах. Убедитесь, что сохраняете/восстанавливаете регистры согласно ABI, не ломаете пролог/эпилог функции.
Битность и позиционно-независимый код. Смешение режимов, PIE/не-PIE и разные опции компоновки меняют адреса и относительные переходы.
Страничные границы. Частичная модификация на стыке страниц может дать неконсистентный результат.
Кэш инструкций/фронтенд процессора. На ряде платформ необходимо явное уведомление о том, что «код обновлён».


Как презентовать результаты на форуме/в команде​

— Покажите сравнение поведения функции «до/после» на наборе тестов.
— Приложите дампы памяти и контрольные суммы (без вредоносных фрагментов).
— Обсудите модель угроз и практики снижения риска: краткоживущие права, минимизация поверхности атаки, строгая изоляция среды.
— Сделайте выводы о применимости подходов в безопасном JIT, профилировании и инструментарии реверса.

Итоги и польза для практики​

Что даёт такой безопасный подход:
— Глубокое понимание того, как код превращается в данные и обратно, где хрупкие места и как их защищают.
— Навык анализа и соблюдения ABI/конвенций вызовов, внимательность к смещениям и кэшам.
— Практику построения надежных исследовательских протоколов, которые полезны в оборонительном ИБ и разработке производительных JIT-компонент.
— Умение документировать эксперименты без публикации опасных «боевых» деталей, что соответствует этике и закону.
 
Яндекс.Метрика