Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Операционная система MS-DOS

© Александр Фролов, Григорий Фролов
Том 1, книги 1-2, М.: Диалог-МИФИ, 1991.

[Назад] [Содеожание] [Дальше]

3.1. Форматы программных файлов

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

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

COM-файл - это двоичный образ Вашей программы, состоящий из кода и данных. То есть это файл, содержащий программу в "чистом" виде. Такая программа (как и EXE-программа) может загружаться в любое место памяти. DOS выполняет ее привязку к физическим адресам при загрузке с помощью установки сегментных регистров. Существенным ограничением COM-программы является то, что она не может занимать больше одного сегмента (соответственно, файл .COM не может быть по длине больше 64К).

Программа в формате EXE может иметь любой размер. В самом начале файла программы содержится заголовок (у COM-файла заголовка нет). Этот заголовок используется операционной системой в процессе загрузки программы в память для правильной установки сегментных регистров. Заголовок EXE-файла нужен только при загрузке; когда программа загружена и готова к работе, самого заголовка уже нет в памяти.

Заголовок EXE-файла состоит из форматированной зоны и таблицы расположения сегментов (Relocation Table). Форматированная зона выглядит следующим образом:

(0) 2 signature два байта 'MZ' (4Dh, 5Ah), индентифицирующие файл в формате EXE
(+2) 2 part_pag длина последней страницы программы в байтах (страница содержит 512 байт)
(+4) 2 file_size размер программы в страницах по 512 байт
(+6) 2 rel_item число элементов в таблице расположения сегментов
(+8) 2 hdr_size размер заголовка файла в параграфах (длина параграфа - 16 байт)
(+10) 2 min_mem минимальное количество памяти в параграфах, которое нужно зарезервировать в памяти за концом загруженной программы
(+12) 2 max_mem максимальное количество памяти в параграфах, которое нужно зарезервировать в памяти за концом загруженной программы
(+14) 2 ss_reg величина смещения от начала программы, которая используется для загрузки сегментного регистра стека SS
(+16) 2 sp_reg величина смещения от начала программы, которая используется для загрузки регистра SP
(+18) 2 chk_summ контрольная сумма всех слов в файле
(+20) 2 ip_reg значение для регистра IP, которое будет использовано при начальном запуске программы
(+22) 2 cs_reg смещение от начала программы для установки сегментного регистра кода CS
(+24) 2 relt_off смещение от начала файла таблицы расположения сегментов программы
(+26) 2 overlay номер оверлея, равен 0 для основного модуля

Таблица расположения сегментов программы начинается сразу после форматированной области и состоит из четырехбайтовых значений в формате "смещение:сегмент".

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

В файле sysp.h есть описание заголовка файла и таблицы расположения сегментов, которые вы можете использовать при обработке заголовка EXE-файла:

typedef struct _EXE_HDR_ {
        unsigned signature;
        unsigned part_pag;
        unsigned file_size;
        unsigned rel_item;
        unsigned hdr_size;
        unsigned min_mem;
        unsigned max_mem;
        unsigned ss_reg;
        unsigned sp_reg;
        unsigned chk_summ;
        unsigned ip_reg;
        unsigned cs_reg;
        unsigned relt_off;
        unsigned overlay;
} EXE_HDR;

typedef struct _RELOC_TAB_ {
        unsigned offset;
        unsigned segment;
} RELOC_TAB;

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

#include <stdio.h>
#include <stdlib.h>
#include "sysp.h"

void main(int, char *[]);

void main(int argc, char *argv[]) {

        printf("Распечатка заголовка EXE-файла\n"
                  "Copyright (C)Frolov A., 1990\n\n");

        if( argc != 2 ) {
           printf( "  Задайте путь EXE-файла в качестве"
                      "параметра\n" );
           exit(0);
        }

        if( gethdr( argv[1]) != 0) {
                printf( "Ошибка в формате файла или нет такого"
                           "файла\n" );
                exit(0);
        }
        exit(0);
}

int gethdr( char *path) {

        EXE_HDR     header;
        RELOC_TAB   *reloc;
        FILE        *inpfile;
        int         i;

        if((inpfile = fopen(path,"rb")) == 0) return(-1);

        if(get_exeh(&header,&reloc,inpfile) != 0) {
                fclose(inpfile);
                return(-1);
        }
        printf("Магическое число:                   %04X\n"
          "Длина последней страницы файла:      %d\n"
          "Количество страниц в файле:          %d\n"
          "Кол. элементов табл. перемещений:    %d\n"
          "Размер заголовка в параграфах:       %d\n"
          "Минимальная память для программы:    %04X\n"
          "Максимальная память для программы:   %04X\n"
          "Значение адреса стека SS:SP:         04X:%04X\n"
          "Контрольная сумма:                   %04X\n"
          "Значения для регистров CS:IP:        %04X:%04X\n"
          "Смещение табл. перемещений:          %02X\n"
          "Номер оверлея:                       %d\n",
                         header.signature,
                         header.part_pag,
                         header.file_size,
                         header.rel_item,
                         header.hdr_size,
                         header.min_mem,
                         header.max_mem,
                         header.ss_reg,
                         header.sp_reg,
                         header.chk_summ,
                         header.cs_reg,
                         header.ip_reg,
                         header.relt_off,
                         header.overlay);

        if(reloc != 0) {
                printf("\nСодержимое таблицы перемещений:\n\n");

                for(i=0;i < header.rel_item; i++) {
                        printf("%04X:%04X\n",
                        (reloc+i)->segment,
                        (reloc+i)->offset);

                }
                free(reloc);
        }
        fclose(inpfile);
        return(0);
}

Приведенная выше программа для чтения заголовка EXE-файла пользуется функцией get-exeh:

/**
*.Name      get_exeh
*
*.Title     Прочитать заголовок EXE-файла
*
*.Descr     Функция читает заголовок EXE-файла в
*           структуру типа EXE_HDR, заказывает память
*           для таблицы размещений сегментов и считывает
*           таблицу в эту область. Адрес заказанной области
*           помещается по адресу, передаваемому в rtb.
*           Если таблица размещений отсутствует, память для
*           нее не заказывается.
*
*.Params    int get_exeh(EXE_HDR *exeh,RELOC_TAB **rtb,
*                                       FILE *exe_file)
*
*              exeh -  указатель на структуру, которая
*                      должна быть заполнена информацией
*                      из заголовка EXE-файла
*
*              rtb  -  указатель на указатель на таблицу
*                      размещений сегментов программы
*
*              exe_file - указатель на открытый EXE-файл
*                      (до вызова функции нельзя обращаться
*                      к этому файлу, т.к. считается, что
*                      указатель текущего смещения
*                                  установлен на начало файла)
*
*.Return    0 при успешном считывании заголовка;
*           -1 в случае неправильного формата заголовка
**/

#include <stdlib.h>
#include <stdio.h>
#include "sysp.h"

int get_exeh(EXE_HDR *exeh,RELOC_TAB **rtb,FILE *exe_file) {

        int i,j,k;

        // считываем форматированную часть заголовка

        for(i=0; i < sizeof(EXE_HDR); i++) {
                *(((char*)exeh) + i) = fgetc(exe_file);
                if(feof(exe_file)) break;
        }

        // это EXE-файл?

     if(exeh->signature != 0x5a4d) return(-1);

        if((i=exeh->rel_item) != 0) {

// если есть таблица перемещений, заказываем для нее память

                *rtb = (RELOC_TAB*)malloc(i*sizeof(RELOC_TAB)+16);

                // считываем таблицу перемещений

                for(k=0; k<i; k++) {
                        for(j=0;j < sizeof(RELOC_TAB);j++) {

                                *((char*)(*rtb)+j+k*sizeof(RELOC_TAB))=
                                        fgetc(exe_file);

                                if(feof(exe_file)) break;
                        }
                }
        }
        else *rtb = (RELOC_TAB *)0;

        return(0);}
[Назад] [Содеожание] [Дальше]