Создание операционной системы на ассемблере

         

Формат PE.


Во многом он аналогичен формату ELF, ну и не удивительно, там так же должны быть секции, доступные для загрузки.
Как и все в Microsoft :) формат PE базируется на формате EXE. Структура файла такова:

  • 00h - EXE заголовок (не буду его рассматривать, он стар как Дос. :)
  • 20h - OEM заголовок (ничего существенного в нем нет);
  • 3сh - смещение реального PE заголовка в файле (dword).
  • таблица перемещения stub;
  • stub;
  • PE заголовок;
  • таблица объектов;
  • объекты файла;
  • stub - это программа, выполняющаяся в реальном режиме и производящая какие-либо предварительные действия. Может и отсутствовать, но иногда может быть нужна.
    Нас интересует немного другое, заголовок PE.
    Структура его такая:

    struct pe_hdr { unsigned long pe_sign; unsigned short pe_cputype; unsigned short pe_objnum; unsigned long pe_time; unsigned long pe_cofftbl_off; unsigned long pe_cofftbl_size; unsigned short pe_nthdr_size; unsigned short pe_flags; unsigned short pe_magic; unsigned short pe_link_ver; unsigned long pe_code_size; unsigned long pe_idata_size; unsigned long pe_udata_size; unsigned long pe_entry; unsigned long pe_code_base; unsigned long pe_data_base; unsigned long pe_image_base; unsigned long pe_obj_align; unsigned long pe_file_align;

    // ... ну и еще много всякого, неважного. };

    Много всякого там находится. Достаточно сказать, что размер этого заголовка - 248 байт.
    И главное что большинство из этих полей не используется. (Кто так строит?) Нет, они, конечно, имеют назначение, вполне известное, но моя тестовая программа, например, в полях pe_code_base, pe_code_size и тд содержит нули но при этом прекрасно работает. Напрашивается вывод, что загрузка файла осуществляется на основе таблицы объектов. Вот о ней то мы и поговорим.
    Таблица объектов следует непосредственно после PE заголовка. Записи в этой таблице имеют следующий формат:

    struct pe_ohdr { unsigned char o_name[8]; unsigned long o_vsize; unsigned long o_vaddr; unsigned long o_psize; unsigned long o_poff; unsigned char o_reserved[12]; unsigned long o_flags; };




    o_name - имя секции, для загрузки абсолютно безразлично;
    o_vsize - размер секции в памяти;
    o_vaddr - адрес в памяти относительно ImageBase;
    o_psize - размер секции в файле;
    o_poff - смещение секции в файле;
    o_flags - флаги секции;

    Вот на флагах стоит остановиться поподробнее.

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


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


    Содержание раздела