Работа с PE-заголовками

Программирование

Метки : , , , , , , , ,

5peФормат исполняемых файлов Portable Executables (PE) существует уже очень долго, даже в архитектуру Windows 3.11 была заложена возможность запуска подобных файлов при установленном Win32s. Формат файла достаточно прост, но, тем не менее, в нем есть множество подводных камней, из-за которых некоторые программы умирают, а некоторые программисты пользуются ими в своих целях. Здесь кратко описывалось перемещение точки входа в программу для защиты от антивируса на свободное место в файле. Теперь мы рассмотрим подробно, как появилось это место, и что еще можно придумать с исполняемым файлом.

Для данного примера нам потребуются следующие программы:

  • HEX-редактор (в моем случае FlexHex)
  • Компилятор Borland C++ Builder 6.0

Все эти средства общедоступны в Интернете, желательно скачать самые последние версии.

Начнем с огромного количества скучной, но важной теории.

Формат EXE-файла, показанный в таблице, отражает его достаточно сильную структурированность:

Смещение

Описание

00h

DOS 2 Header
Заголовок DOS (форматированная часть), оставлен для совместимости.

1Ch

4 байта для выравнивания форматированной области заголовка с 1Ch до 20h.

20h

OEM Identifier & OEM Info
Информация о программе, обычно не используется.

3Ch

Offset to PE Header
Смещение реального PE заголовка в файле, DWord

min 40h

Программы-заглушки, на это поле указывает ReloOfs заголовка DOS 2 Header, соответственно его значение должно быть >=40h.

min 40h + XXh

Тело DOS программы, которая чаще всего говорит о невозможности запуска (”This program cannot be run in DOS mode“).
40h-нижняя граница данного поля.

XXh

PE Header
Заголовок PE файла, который нас и интересует

XXh

Object Table
Таблица объектов, содержит описаний секций файла.

XXh

Image Pages (import, export, fixup, resource, debug, …)
Собственно основная часть, содержащая секции, служебные таблицы и т.д….

Для запуска файл должен быть в формате PE, минимально необходимо для этого, чтобы он был: во-первых EXE (байты по смещению 0h равны 5A4Dh – “MZ“), во-вторых, слово по смещению 18h должно быть >=40h, тогда и только тогда поле смещения PE Header по адресу 3Ch имеет смысл.

Для нахождения заголовка PE в файле воспользуемся полем Offset to PE Header, находящемуся по смещению 3Ch от начала файла.
Слова DWord и Word, обозначающие тип данных, имеют размеры 4 и 2 байта соответственно.

Формат PE-заголовка представлен в следующей таблице:

Смещение

Размер

Название

Описание

00h

DWord

Signature Bytes

Сигнатура того, что этот файл является PE - должна быть 4550h – “PE”, два последних байта зарезервированы и должны быть равны 0h.

04h

Word

CPU Type

Поле указывает на тип процессора, под которым желательно запускать данную программу, обычно равно 14Ch – “i386″

06h

Word

Num of Objects

Поле указывает на количество элементов таблицы Object Table

08h

DWord

Time/Date Stamp

Поле даты и времени создания/модификации файла при сборке

0Ch

DWord

Pointer to COFF table

Указатель, определяющий местонахождение отладочной COFF таблицы в файлах

10h

DWord

COFF table size

Количество символов в COFF таблице

14h

Word

NT Header Size

Размер заголовка PE файла, начиная с поля Magic, таким образом, полный размер заголовка PE файла составляет NT Header Size + 18h (смещение поля Magic)

16h

Word

Flags

Флаги, указывающие на предназначение программы, с битовыми значениями:

  • 0000h – это программа
  • 0001h - файл не содержит перемещений и таблицы перемещаемых элементов
  • 0002h - образ в файле можно запускать
  • 0200h - грузить фиксировано, т.е. только по адресу, записанному в Image Base
  • 2000h - это библиотека

18h

Word

Magic

Поле указывает на предназначение программы, обычно равно 010Bh.

1Ah

Byte

Link Major

Старший номер версии использовавшегося при создании линкера

1Bh

Byte

Link Minor

Младший номер версии использовавшегося при создании линкера

1Ch

DWord

Size of Code

Размер программного кода в файле, используется для фактического отведения памяти под загружаемую программу

20h

DWord

Size of Init Data

Размер секции инициализированных данных

24h

DWord

Size of UnInit Data

Размер секции неинициализированных данных

28h

DWord

Entry point RVA

Адрес относительно ImageBase, по которому передается управление при запуске программы или адрес инициализации/завершения библиотеки, т.н. точка входа

2Ch

DWord

Base of Code

RVA секции, которая содержит программный код

30h

DWord

Base of Data

RVA секции, которая содержит данные

34h

DWord

Image Base

Виртуальный начальный адрес загрузки программы (ее первого байта).

38h

DWord

Object align

Выравнивание программных секций, должен быть степенью 2 между 512 и 256М включительно

3Ch

DWord

File align

Выравнивание секций в программном файле, указывает на границу, по которую секции дополняются 0 при размещении в файле. Должен быть степенью 2 в диапазоне от 512 до 64К включительно.

40h

Word

OS Major

Старший номер версии ОС, необходимый для запуска программы.

42h

Word

OS Minor

Младший номер версии ОС

44h

Word

USER Major

Пользовательский номер версии, задается пользователем при сборке программы

46h

Word

USER Minor

Аналогично, младший номер

48h

Word

SubSys Major

Старший номер версии подсистемы

4Ah

Word

SubSys Minor

Аналогично, младший номер

4Ch

DWord

Reserved

Зарезервировано

50h

DWord

Image Size

Виртуальный размер в байтах всего загружаемого образа, вместе с заголовками, кратен Object align

54h

DWord

Header Size

Общий размер всех заголовков: DOS Stub + PE Header + Object Table

58h

DWord

File CheckSum

Контрольная сумма всего файла, как правило ее никто не контролирует.

5Ch

Word

SubSystem

Подсистема, необходимая для запуска файла (GUI-0002h, консоль-0003h, …)

5Eh

Word

DLL Flags

Указывает на специальные потребности при загрузке, устарел и не используется.

60h

DWord

Stack Reserve Size

Память, резервируемая для стека приложения.

64h

DWord

Stack Commit Size

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

68h

DWord

Heap Reserve Size

Максимально возможный размер локальной кучи (heap)

6Ch

DWord

Heap Comit Size

Размер отводимой при загрузке кучи

70h

DWord

Loader Flags

Не используется, связано с поддержкой отладки

74h

DWord

Num of RVA and Sizes

Указывает размер массива VA/Size, который следует ниже, зарезервирован и равен 10h

78h

DWord

Export Table RVA

RVA адрес таблицы экспорта

7Ch

DWord

Export Data Size

Размер таблицы экспорта

80h

DWord

Import Table RVA

RVA адрес таблицы импорта

84h

DWord

Import Data Size

Размер таблицы импорта

88h

DWord

Resource Table RVA

RVA адрес таблицы ресурсов

8Ch

DWord

Resource Data Size

Размер таблицы ресурсов

90h

DWord

Exception Table RVA

RVA адрес таблицы исключений

94h

DWord

Exception Data Size

Размер таблицы исключений

98h

DWord

Security Table RVA

RVA адрес таблицы безопасности

9Ch

DWord

Security Data Size

Размер таблицы безопасности

A0h

DWord

Fix Up’s Table RVA

RVA адрес таблицы настроек

A4h

DWord

Fix Up’s Data Size

Размер таблицы настроек

A8h

DWord

Debug Table RVA

RVA адрес таблицы отладочной информации

ACh

DWord

Debug Data Size

Размер таблицы отладочной информации

B0h

DWord

Image Description RVA

RVA адрес строки описания модуля

B4h

DWord

Description Data Size

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

B8h

DWord

Machine Specific RVA

RVA адрес таблицы значений, специфичных для микропроцессора

BCh

DWord

Machnine Data Size

Размер таблицы значений, специфичных для микропроцессора

C0h

DWord

TLS RVA

RVA указатель на локальную область данных цепочек

C4h

DWord

TLS Data Size

Размер области данных цепочек

C8h

DWord

Load Config RVA

?

CCh

DWord

Load Config Data Size

?

D0h

08h

Reserved

Зарезервировано

D8h

DWord

IAT RVA

Указывает на таблицу адресов импорта в файле (помимо структуры импорта)

DCh

DWord

IAT Data Size

Размер поля IAT

E0h

08h

Reserved

Зарезервировано

E8h

08h

Reserved

Зарезервировано

F0h

08h

Reserved

Зарезервировано

При этом VA - виртуальный адрес, который уже базирован на смещении Image Base, RVA - относительный адрес, ссылающийся на Image Base. RVA в PE Header, имеющий нулевое значение, указывает на то, что соответствующее поле не используется.

Сразу за заголовком в файле располагается таблица объектов. Число входов в таблице объектов (секций) определяется полем Num of Objects заголовка PE Header. Последовательность секций кода и данных в памяти выбирается линкером. Каждая секция (объект) располагает именем, которое никого ни к чему не обязывает, имя может быть произвольным, но вообще-то смысл содержания секции и ее наименования как правило совпадают.

Структура таблицы объектов показана в следующей таблице:

Смещение

Размер

Название

Описание

00h

08h

Object Name Имя объекта, остаток заполнен нулями

08h

DWord

Virtual Size

Виртуальный размер секции, именно столько памяти будет отведено под секцию. Если Virtual Size превышает Physical Size, то разница заполняется нулями, так определяются секции неинициализированных данных (Physical Size = 0)

0Ch

DWord

Section RVA Размещение секции в памяти, её виртуальный адрес относительно Image Base.

10h

DWord

Physical Size Размер секции (ее инициализированной части) в файле, должно быть меньше или равно Virtual Size.

14h

DWord

Physical Offset Физическое смещение относительно начала EXE файла.

18h

0Ch

Reserved Зарезервировано

28h

DWord

Object Flags Битовые флаги секции со значениями:

  • 00000004h - используется для кода с 16 битными смещениями
  • 00000020h - секция кода
  • 00000040h - секция инициализированных данных
  • 00000080h - секция неинициализированных данных
  • 00000200h - комментарии или любой другой тип информации
  • 00000400h - оверлейная секция
  • 00000800h - не будет являться частью образа программы
  • 00001000h - общие данные
  • 00500000h - выравнивание по умолчанию, если не указано иное
  • 02000000h - может быть выгружен из памяти
  • 04000000h - не кэшируется
  • 08000000h - не подвергается страничному преобразованию
  • 10000000h - разделяемый
  • 20000000h - выполнимый
  • 40000000h - можно читать
  • 80000000h- можно писать

Примеры стандартных названий секций Object Name:

  • .text - исполняемый код Microsoft
  • CODE - исполняемый код Borland
  • .data - секция данных Microsoft
  • DATA - секция данных Borland
  • .bss - неинициализированные данные
  • .CRT - инициализированные данные Borland C/C++
  • .rsrc - ресурсы
  • .idata - секция импорта
  • .edata - секция экспорта
  • .reloc - таблица настроек
  • .tls - данные, на базе которых Windows запускает цепочки
  • .rdata - отладочная информация

При этом следует отметить, что практически все упаковщики и крипторы исполняемых файлов создают свои секции, у многих из них также стандартные имена, например UPX0, .decode, pec1 и т.д.

Пример заголовка программы “Калькулятор” с указанием всех элементов представлен на рисунке. Здесь можно скачать архив с полной BMP-версией [23kb].

image001

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

  • внедрение в пустое пространство;
  • расширение последней секции;
  • добавление новой секции;

Рассмотрим подробно способ внедрения в пустое пространство

В некоторых других статьях этот метод называется внедрением в заголовок, хотя это не совсем так. Классическим примером использования является вирус Win95.CiH (Чернобыль).
Откуда же берется это пустое пространство?

image002Участок, помеченный на рисунке красным цветом, находится между последним элементом таблицы объектов и смещением первой секции. А появился он в результате выравнивания, за которое отвечает поле File Align по смещению 3Ch в заголовке.
Чтобы получить “координаты” и размер указанной области необходимо знать:
a- Размер PE-заголовка (обычно F8h, но лучше NT Header Size+18h, значение берется из PE Header)
b- Смещение PE-заголовка (поле Offset To PE header по смещению 3Ch от начала файла, содержит DWord)
c- Размер таблицы объектов (значение поля Num of Objects по смещению 06h из PE Header умножаем на размер объекта, т.е. на 40)
d- Физическое смещение первой секции (значение Physical Offset по смещению 14h из таблицы объектов для первого элемента).

Таким образом, началом пустой области у нас будет место в файле с физическим смещением a+b+c, а концом – место в файле со смещением d, соответственно размер области равен d-(a+b+c)

Попробуем получить это значение для какого-нибудь файла (сразу оговорюсь, с “Калькулятором” это не пройдет из-за оптимизации расположения импорта).

Следует сразу оговориться, что размер этой области не резиновый, поэтому кусок внедряемого кода очень ограничен. Приступим к программированию

Создаем новый проект в Builder, пишем несколько строчек кода, компилируем и берем полученный EXE файл на исследование. Можно взять любой другой не сжатый и не оптимизированный EXE файл. Builder можно не закрывать, программировать еще будем ;-)

После открытия файла считываем из него значение смещения PE заголовка (PEHeaderOffset), это DWord по смещению 3Ch.

image004

На рисунке показано это значение в Hex-редакторе, оно равно 0200h (вспоминаем про порядок расположения байтов).

image003

Так и есть, по адресу 0200h наблюдаем начало PE заголовка. Создаем для него структуру:

image006

На рисунке отмечены необходимые нам поля: синее – количество элементов в таблице объектов; красное – размер заголовка от поля Magic, зеленое – RVA точки входа в программу.

image0051

Выяснив количество элементов (NumObjects) можно рассчитать размер таблицы объектов. В данном случае по смещению 06h от начала заголовка у нас находится число 8, таким образом размер таблицы составляет 8*40=320 байт.

К этому моменту мы уже нашли 3 необходимые нам переменные:

a = F8h (получаем как поле NTheaderSize+18h, у нас NTheaderSize=E0h)
b = 200h (получили из PEheaderOffset)
c = 140h (рассчитали из количества объектов NumObjects*40)

Осталось найти последний элемент, а именно физический адрес смещения первой секции в файле. Для этого мы используем структуру элемента таблицы объектов, а конкретно ее поле PhysicalOffset.

image007

Считываем значения в созданную структуру, получаем, физическое смещение первой секции равное 0600h.

image008

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

image009

В данном случае мы считали PE заголовок, нашли количество объектов и создали динамический массив структур элементов таблицы объектов. Также мы нашли начало таблицы объектов на основании того, что она следует сразу за заголовком.
Считываем все элементы таблицы объектов в созданный массив структур и находим физическое смещение первой секции. После этого вычисляем начало пустого пространства как сумму размера всех заголовков и размера таблицы объектов. Концом пустого пространства является начало 1-й секции, разница между ними и будет размером.

В данном случае началом у нас является сумма Смещение заголовка PE (200h) + размер заголовка (F8h)+ размер таблицы объектов (140h)= 438h
Конец пустой области = 600h
Разница: 1C8h

Проверим, так ли это. Открываем файл в Hex-редакторе и идем к смещению 438h.

image010

Так и есть, на этом месте заканчивается таблица объектов описанием последней секции .reloc и начинается пустое место, заполненное нулями вплоть до адреса 600h. Таким образом, мы получили 456 байт неиспользуемого пространства в файле, которое можно занять под свои нужды.

А дальше можно попробовать несколько вариантов: поменять точку входа (обозначена зеленым цветом на иллюстрации PE заголовка) на этот кусок, как мы уже делали в этом примере, или сделать еще хитрее – по адресу точки входа поставить jmp на эту область, не меняя точки входа. Но это уже дело фантазии. В заключение хочется сказать бессмертную фразу: не пишите вирусы и трояны, они не делают мир лучше!

Статья основана на труде Sars/HiTech, взятого с http://wasm.ru, а также описании Hard Wisdom’а, которое выложено в библиотеке. Его я настоятельно рекомендую скачать и изучить, потому что последующие примеры будут также основаны на нем.

Пожалуйста оцените статью:
Загрузка ... Загрузка ...


Комментарии:

Оставить комментарий

Материал используется исключительно в некоммерческих целях.
Все права на публикуемые графические, текстовые, аудио и видео материалы принадлежат их владельцам, при цитировании или копипасте прошу оставлять ссылку на оригинал.
Я искренне надеюсь на вашу заинтересованность в посещении этого сайта. Спасибо за внимание!