Се­год­ня мы оку­нем­ся вглубь исполня­емых фай­лов и изу­чим на прак­тике, как устро­ены прог­раммы в Windows. Я пос­тара­юсь под­робно рас­писать струк­туру PE-фай­лов, а затем напишем с нуля прос­тень­кую прог­рамму пря­мо в Hex-редак­торе.
 

Что такое PE-файл

Portable Executable (перено­симый исполня­емый файл) — это офи­циаль­ное наз­вание фор­мата фай­лов .exe, .dll и .sys для Windows. Сло­во portable здесь озна­чает перено­симость фор­мата меж­ду раз­ными вер­сиями Windows и раз­ными архи­тек­турами про­цес­соров. Ну а сло­во executable говорит нам о том, что этот файл содер­жит в себе инс­трук­ции для про­цес­сора, которые он будет пос­лушно исполнять.

 

Создание заготовки

На­ша прог­рамма будет доволь­но прос­той: мы нем­ного подер­гаем Windows за ее API и поп­росим вывес­ти диало­говое окош­ко с заголов­ком «Wake up, Neo...» и над­писью «The Matrix has you...».

Для наших даль­нейших изыс­каний соз­дадим пус­той файл и нач­нем потихонь­ку запол­нять его дан­ными. Для это­го нам пот­ребу­ется Hex-редак­тор. Лич­но я пред­почитаю Hiew, который даже в наше вре­мя не теря­ет акту­аль­нос­ти. Это очень мощ­ный инс­тру­мент, но мы вос­поль­зуем­ся толь­ко его встро­енным ком­пилято­ром ассем­бле­ра, осталь­ное запол­ним руками.

Для соз­дания пус­того фай­ла в Windows PowerShell есть встро­енная ути­лита fsutil. Наша будущая прог­рамма будет иметь раз­мер 2 Кбайт. Так туда помес­тится заголо­вок, сек­ция прог­раммы, сек­ция с дан­ными и сек­ция импорта. Конеч­но, они мог­ли бы помес­тить­ся и в гораз­до более скром­ный объ­ем, но даль­ше ты пой­мешь, почему я выб­рал имен­но такой раз­мер.

fsutil file createnew Program.exe 2048
 

Структура PE-файла

Сле­дующая кар­тинка показы­вает струк­туру PE-фай­ла. По сути, это крат­кий перес­каз всей статьи. Спа­сибо авто­ру Ange Albertini за столь наг­лядную иллюс­тра­цию и Lyr1k — за ее перевод на рус­ский язык.

Как вид­но по схе­ме, PE-файл сос­тоит из заголов­ка и некото­рого количес­тва сек­ций. В нашем слу­чае их будет три.

 

Заголовок

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

 

Заголовок DOS

За­голо­вок DOS — рудимент, сох­ранен­ный со вре­мен MS-DOS для обратной сов­мести­мос­ти.

Смещение Размер Поле
[0x00] 2 e_magic;
[0x02] 2 e_cblp;
[0x04] 2 e_cp;
[0x06] 2 e_crlc;
[0x08] 2 e_cparhdr;
[0x0A] 2 e_minalloc;
[0x0C] 2 e_maxalloc;
[0x0E] 2 e_ss;
[0x10] 2 e_sp;
[0x12] 2 e_csum;
[0x14] 2 e_ip;
[0x16] 2 e_cs;
[0x18] 2 e_lfarlc;
[0x1A] 2 e_ovno;
[0x1C] 2 e_res[4];
[0x24] 2 e_oemid;
[0x26] 2 e_oeminfo;
[0x28] 2 e_res2[10];
[0x3C] 4 e_lfanew;

info

Раз­мер в этой и сле­дующих таб­лицах ука­зан в бай­тах.

Сей­час необ­ходимос­ти запус­кать фай­лы в DOS нет, а зна­чит, раз­бирать эту струк­туру пол­ностью не будем. Нам здесь важ­ны толь­ко два эле­мен­та — пер­вый и пос­ледний (без них ничего работать не будет):

  • e_magic — всег­да име­ет зна­чение MZ (4d 5a);
  • e_lfanew — содер­жит в себе сме­щение до PE-заголов­ка. В нашем слу­чае сме­щение будет 0х40, то есть PE-заголо­вок будет сра­зу сле­довать за DOS-заголов­ком.

За­пишем эти зна­чения в нашу прог­рамму.

DOS-заголовок
DOS-заголо­вок
 

DOS-заглушка

Сто­ит так­же упо­мянуть DOS-заг­лушку. Ком­понов­щик встав­ляет ее сра­зу за DOS-заголов­ком и перед PE-заголов­ком. Это неболь­шая прог­рамма для DOS, которая обыч­но выводит сооб­щение «This program cannot be run in DOS mode». Она так­же явля­ется рудимен­том, и без нее мож­но спо­кой­но обой­тись, так что в нашей прог­рамме она при­сутс­тво­вать не будет.

 

Заголовок PE

Да­лее идет PE-заголо­вок, адрес которо­го мы записа­ли в перемен­ной e_lfanew. Он сос­тоит из сиг­натуры и COFF (Common Object File Format) заголов­ка фай­ла.

Signature
Смещение Размер Поле
[0x00] 4 Signature
COFF header
Смещение Размер Поле
[0x00] 2 Machine
[0x02] 2 NumberOfSections
[0x04] 4 TimeDateStamp
[0x08] 4 PointerToSymbolTable
[0x0C] 4 NumberOfSymbols
[0x10] 2 SizeOfOptionalHeader
[0x12] 2 Characteristics
  • Signature — сиг­натура PE-фай­ла. Исполь­зует­ся заг­рузчи­ком для про­вер­ки того, что файл дей­стви­тель­но явля­ется PE. Всег­да име­ет зна­чение PE\x00\x00 (50 45 00 00).

Струк­тура COFF-заголов­ка фай­ла такова:

  • Machine — архи­тек­тура про­цес­сора. Нуж­на для пра­виль­ной рас­шифров­ки команд. Мы будем писать под архи­тек­туру x86, соот­ветс­твен­но, зна­чение это­го поля будет 0х14c. С пол­ным спис­ком под­держи­ваемых архи­тек­тур мож­но озна­комить­ся на сай­те.
  • NumberOfSections — количес­тво сек­ций. В нашем при­мере их будет три.
  • TimeDateStamp — вре­мя соз­дания фай­ла в фор­мате UNIX timestamp. Возь­мем 31.03.1999, дата выхода пер­вой «Мат­рицы». Впи­сыва­ем зна­чение 0х3700f500.
  • PointerToSymbolTable — ука­затель на таб­лицу сим­волов. Таб­лица сим­волов фор­миру­ется, нап­ример, для дебага, и в ней содер­жатся име­на фун­кций, перемен­ных и меток. Мы такую таб­лицу фор­мировать не будем, так что это поле оста­вим рав­ным нулю.
  • NumberOfSymbols — количес­тво сим­волов в таб­лице сим­волов. Так­же оставля­ем нуль.
  • SizeOfOptionalHeader — раз­мер опци­ональ­ного заголов­ка, который сле­дует за нашим PE-заголов­ком. Для нашей 32-бит­ной вер­сии он будет равен 224 байт (0xE0), для 64-бит­ной — 240 байт (0xF0). Этот раз­мер мож­но нем­ного умень­шить за счет умень­шения количес­тва дирек­торий дан­ных, которые находят­ся в кон­це опци­ональ­ного заголов­ка, но делать это не име­ет смыс­ла. Нес­коль­ко сво­бод­ных бай­тов в заголов­ке нам ничего не дадут, но, если мы решим допол­нить прог­рамму еще одной дирек­тори­ей дан­ных, нам при­дет­ся перепи­сывать таб­лицу сек­ций, пос­коль­ку она уже будет не на сво­ем мес­те.
  • Characteristics — это сло­во содер­жит атри­буты нашего фай­ла. Здесь нам понадо­бит­ся флаг IMAGE_FILE_EXECUTABLE_IMAGE (0x0002), который озна­чает, что файл исполня­емый. Так­же в этом сло­ве может быть один из фла­гов, нап­ример IMAGE_FILE_SYSTEM, если файл сис­темный (.sys), или IMAGE_FILE_DLL, если это DLL. Пос­мотреть пол­ный спи­сок дос­тупных атри­бутов мож­но в до­кумен­тации.

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии