Когда компилятор превращает исходный код в готовый исполняемый файл, он не сваливает всё содержимое в одну кучу. Программа делится на логические блоки — секции: машинный код попадает в .text, глобальные и статические данные — в .data, ресурсы вроде иконок, строк интерфейса и диалоговых окон — в .rsrc. Понимание этого разделения помогает быстрее ориентироваться в дизассемблере, находить нужные фрагменты при отладке, оценивать подозрительные файлы и понимать, почему изменение байтов в одной части файла ломает программу, а в другой проходит незаметно.
Главный принцип, который стоит держать в голове: каждая секция имеет своё назначение и свои права доступа в памяти. Код исполняется, но не изменяется; данные изменяются, но не исполняются; ресурсы вообще не участвуют в выполнении напрямую. Нарушение этих правил — либо ошибка сборки, либо признак того, что файл модифицирован.
- Что такое секция в исполняемом файле
- Секция .text: где живёт код
- Что ещё попадает в .text
- Почему .text защищена от записи
- Секция .data: изменяемые данные программы
- .data, .bss и .rdata: три разных типа данных
- Практическое значение для анализа
- Секция .rsrc: ресурсы программы
- Что обычно лежит в ресурсах
- Ресурсы и анализ подозрительных файлов
- Как секции связаны между собой
- Другие секции, о которых стоит знать
- Как посмотреть секции самостоятельно
- Типичные заблуждения
- Практические сценарии: когда эти знания пригождаются
- Что делать дальше
Что такое секция в исполняемом файле
Исполняемый файл формата PE (Portable Executable — стандартный формат для Windows) состоит из заголовков и набора секций. Заголовки описывают общую структуру: точку входа, требования к памяти, таблицу импорта. Секции — это непрерывные области файла, каждая из которых содержит данные одного типа.
Загрузчик Windows при запуске программы отображает (маппирует) секции из файла в виртуальное адресное пространство процесса. Для каждой секции задаются права доступа к страницам памяти:
- execute — разрешение на исполнение кода процессором;
- read — разрешение на чтение;
- write — разрешение на запись.
Типовая раскладка выглядит так: .text получает права «чтение + исполнение», .data — «чтение + запись», .rsrc — только «чтение». Именно поэтому попытка записать данные в область кода вызывает исключение доступа, а попытка исполнить байты из секции данных срабатывает только если защита DEP (Data Execution Prevention) отключена или код специально обходит ограничение.
У каждой секции есть два ключевых адреса, которые новички часто путают:
- Virtual Address (VA) — смещение секции в памяти процесса после загрузки;
- Pointer to Raw Data — смещение секции внутри самого файла на диске.
Эти значения обычно не совпадают, потому что в файле секции выровнены по одному размеру (часто 512 байт), а в памяти — по другому (обычно 4096 байт, размер страницы). Инструменты анализа всегда показывают оба значения, и умение переводить файловое смещение в виртуальный адрес и обратно — базовый навык при работе с дизассемблером и hex-редактором.
Секция .text: где живёт код
В .text находится скомпилированный машинный код всех функций программы, включая код из подключённых статических библиотек. Это самая важная для реверс-инжиниринга секция: именно её читает дизассемблер, когда вы открываете файл в IDA, Ghidra или другом инструменте.
Что ещё попадает в .text
Кроме собственно инструкций процессора, в этой секции часто лежат:
- константы времени компиляции, например литеральные строки, если компилятор решил разместить их рядом с кодом;
- таблицы переходов для операторов switch с плотными диапазонами значений;
- заглушки импорта (import thunks) — небольшие фрагменты, через которые программа обращается к функциям из DLL;
- исключения и метаданные, необходимые раскрутке стека при обработке ошибок.
Название секции не является жёстким требованием: теоретически код можно положить в секцию с любым именем, лишь бы у неё стоял флаг исполнения. Но компиляторы Microsoft по умолчанию используют .text, поэтому имя стало фактическим стандартом. В Linux-мире аналог называется .text тоже, хотя формат ELF устроен иначе.
Почему .text защищена от записи
Запрет записи в секцию кода решает две задачи. Первая — стабильность: случайная порча инструкций привела бы к падению или непредсказуемому поведению. Вторая — безопасность: вредоносному коду сложнее внедриться в уже работающий процесс, если страницы с кодом нельзя перезаписать без явного вызова функций изменения защиты памяти (таких как VirtualProtect), которые легко заметить при анализе.
При этом легитимные механизмы самопроверки и самопатчинга существуют: некоторые программы расшифровывают части кода при запуске или применяют патчи в памяти. Такие действия требуют временного снятия защиты, и это один из маркеров, на которые смотрят антивирусы и песочницы.
Секция .data: изменяемые данные программы
В .data размещаются глобальные и статические переменные, которые могут меняться во время работы. Сюда же попадают инициализированные данные, используемые до входа в main: например, объекты, конструкторы которых должны отработать при загрузке.
.data, .bss и .rdata: три разных типа данных
Строго говоря, «данные» в PE-файле распределены по нескольким секциям, и различать их важно:
| Секция | Содержимое | Права в памяти | Особенность |
|---|---|---|---|
| .data | Инициализированные изменяемые переменные | Чтение + запись | Начальное значение хранится в файле |
| .bss | Неинициализированные и обнулённые переменные | Чтение + запись | Может занимать мало места в файле, но много в памяти |
| .rdata | Константы, строки, таблицы импорта | Только чтение | Изменение запрещено на уровне страниц памяти |
Разница между .data и .bss экономит размер файла. Если вы объявили массив на десять мегабайт и не заполнили его, компилятору незачем писать десять мегабайт нулей в файл: достаточно указать в заголовке секции, сколько памяти выделить при загрузке. Загрузчик сам выдаст обнулённые страницы. Поэтому исполняемый файл может весить пару сотен килобайт, а процесс — потреблять десятки мегабайт.
Секция .rdata (read-only data) содержит то, что менять нельзя: строковые литералы, таблицы виртуальных функций C++, дескрипторы импорта. Попытка записи туда вызывает access violation — это частая причина падений при ошибках работы со строками в C++.
Практическое значение для анализа
При исследовании программы секция данных отвечает на вопрос «где хранится состояние». Конфигурационные флаги, счётчики, кэши, указатели на выделенную память — всё это здесь. В отладчике установка точки наблюдения (watchpoint) на адрес в .data позволяет узнать, какая функция изменяет конкретную переменную — приём, который часто оказывается быстрее чтения кода.
Секция .rsrc: ресурсы программы
.rsrc хранит ресурсы — данные, которые не являются ни кодом, ни рабочими переменными, но нужны программе для работы интерфейса и локализации. Формат ресурсов в PE стандартизирован: это дерево, корень которого разделяет ресурсы по типам, затем по именам или идентификаторам, затем по языкам.
Что обычно лежит в ресурсах
- иконки и курсоры — в том числе та иконка, которую вы видите в проводнике;
- версионный ресурс (VS_VERSION_INFO) — название продукта, версия, компания, описание файла;
- таблицы строк — тексты интерфейса, сообщения об ошибках;
- диалоговые окна и меню — описания элементов интерфейса в декларативном виде;
- растровые изображения;
- манифест — XML-описание требований к версии common controls, уровню прав UAC, совместимости;
- произвольные бинарные данные — разработчики могут встроить в ресурсы шаблоны, шрифты, сертификаты, архивы.
Ресурсы редактируются отдельно от кода. Специальные утилиты позволяют заменить иконку или перевести строки, не пересобирая программу. Именно поэтому локализация Windows-приложений исторически строилась на ресурсах: переводчики работают с ними, не имея доступа к исходному коду.
Ресурсы и анализ подозрительных файлов
Секция ресурсов заслуживает внимания при проверке неизвестных файлов. Вредоносные программы нередко прячут в ресурсах полезную нагрузку: зашифрованный исполняемый файл, скрипт, библиотеку, которая извлекается и запускается позже. Признаки, которые должны насторожить:
- аномально большой объём .rsrc относительно остального файла;
- ресурс с высоким энтропией (выглядит как случайные байты) — вероятный упакованный или зашифрованный контент;
- несоответствие версионного ресурса реальному поведению: подделка имени и версии — распространённый приём маскировки;
- встроенный исполняемый образ, распознаваемый по сигнатуре MZ в начале ресурса.
Легитимные установщики и программы с самообновлением тоже хранят большие бинарные блобы в ресурсах, так что большой .rsrc сам по себе не приговор — это повод посмотреть содержимое внимательнее.
Как секции связаны между собой
Хотя секции разделены, работа программы постоянно их связывает. Типичный цикл выглядит так: процессор исполняет инструкции из .text, инструкция обращается к глобальной переменной в .data, затем вызывает функцию из системной DLL через таблицу импорта в .rdata, а результат выводится в диалоговое окно, описание которого взято из .rsrc.
Отсюда практический вывод: чтобы понять программу целиком, нужно смотреть все три слоя. Только код покажет алгоритмы, но не тексты сообщений и настройки интерфейса. Только ресурсы покажут, как программа выглядит, но не как работает. Данные расскажут о состоянии, но не о логике его изменения.
Другие секции, о которых стоит знать
Помимо трёх основных, в реальных файлах встречаются:
- .edata / .idata — таблицы экспорта и импорта: списки функций, которые программа предоставляет другим модулям и берёт из DLL;
- .reloc — таблица перемещений, позволяющая загрузчику скорректировать абсолютные адреса, если модуль не удалось загрузить по предпочтительному адресу;
- .pdata / .xdata — информация для раскрутки стека и обработки исключений на 64-битных платформах;
- .tls — данные локального хранилища потока;
- .debug — отладочная информация, если она не вынесена в отдельные PDB-файлы.
Отдельного упоминания заслуживают нестандартные имена секций, которые добавляют упаковщики и протекторы. Если вместо привычного набора вы видите странные названия с высоким энтропией содержимого и единственной секцией с флагом исполнения, скорее всего, файл упакован: настоящий код появится в памяти только после распаковки при запуске.
Как посмотреть секции самостоятельно
Для базового осмотра достаточно бесплатных инструментов:
- Откройте файл в утилите просмотра PE-заголовков (например, встроенный просмотр свойств в проводнике покажет только версию, нужен специализированный инструмент).
- Найдите список секций: для каждой будут показаны виртуальный размер, виртуальный адрес, размер в файле, смещение в файле и характеристики (флаги чтения/записи/исполнения).
- Сравните ожидаемую картину с реальной: обычное приложение имеет небольшой набор стандартных секций, разумные размеры и типовые права доступа.
- Для глубокого анализа откройте файл в дизассемблере — он автоматически разметит .text как код, .data как данные, а ресурсы покажет отдельным деревом.
Полезная проверка соотношения размеров: если суммарный размер секций в файле сильно меньше размера, который секции займут в памяти, это нормально (эффект .bss). А вот если одна секция занимает почти весь файл, имеет флаг исполнения и высокую энтропию — перед вами, вероятно, упакованный образ.
Типичные заблуждения
- «Имя секции определяет её свойства». Нет, свойства определяются флагами в заголовке. Имя — соглашение, и злоумышленники могут назвать вредоносную секцию как угодно.
- «Все строки программы лежат в .rsrc». Строки интерфейса, оформленные как ресурсы, действительно там. Но строки, зашитые в код (логирование, URL, ключи), чаще оказываются в .rdata или прямо в .text.
- «Изменение байта в .data безопасно». Изменение начального значения переменной меняет поведение программы, иногда критично. Безопасных изменений в чужом исполняемом файле без понимания структуры не бывает.
- «Большая секция ресурсов означает вирус». Установщики, игры и офисные пакеты законно хранят в ресурсах гигабайты данных. Оценивать нужно содержимое, а не размер.
Практические сценарии: когда эти знания пригождаются
Отладка падения. Если в отчёте об ошибке указан адрес, определив секцию, к которой он относится, вы сразу сужаете поиск: адрес в .text — проблема в коде (например, вызов по неверному указателю на функцию), в .data или .rdata — обращение к повреждённым данным или запись в константу.
Оценка подозрительного файла. Нестандартные имена секций, исполняемые права у секции данных, высокая энтропия, отсутствие ресурсов у «солидной» программы — совокупность таких признаков даёт быструю первичную оценку до полноценного анализа.
Локализация и кастомизация. Замена строк и иконок через редактор ресурсов позволяет адаптировать программу без исходников — легитимно для своих приложений и с юридическими оговорками для чужих.
Оптимизация размера. Понимание, что неинициализированные массивы не увеличивают файл, а константы в .rdata дедуплицируются компилятором, помогает осознанно управлять структурой данных в проекте.
Что делать дальше
Если вы хотите закрепить материал на практике, порядок такой: возьмите любой exe-файл на своей машине, откройте его в просмотрщике PE-структур и найдите список секций. Сопоставьте имена, размеры и флаги с тем, что описано выше. Затем откройте тот же файл в бесплатном дизассемблере и найдите в дереве проекта секцию ресурсов — увидите знакомую иконку и строки версии. На этом примере связь «файл на диске → секции → память процесса» становится наглядной за полчаса.
Главный ориентир при любом анализе: назначение секции определяется её флагами и содержимым, а не именем. Стандартные имена .text, .data и .rsrc — удобная отправная точка, но окончательный вывод делайте по характеристикам, структуре и поведению файла при запуске.
Материал носит информационный характер. Исследование исполняемых файлов, полученных из ненадёжных источников, выполняйте только в изолированной среде, а решения, связанные с безопасностью системы или юридическими ограничениями на анализ чужого ПО, принимайте с учётом применимого законодательства и консультации профильного специалиста.
