Операционная система MS-DOS© Александр Фролов, Григорий ФроловТом 1, книги 1-2, М.: Диалог-МИФИ, 1991. 3.1. Форматы программных файловТеперь, когда мы знаем структуру памяти на момент завершения загрузки операционной системы, можно посмотреть, а что же происходит дальше, когда оператор запускает какую-нибудь программу. Оператор может запустить два типа программ (если не считать командных файлов, которые, вообще говоря, не являются программами, состоящими из машинных кодов) - программы, имеющие расширение имени .COM и .EXE. Эти файлы имеют различный формат и загружаются по-разному, однако, когда загрузка завершена, в памяти компьютера эти два типа программ выглядят совершенно одинаково. COM-файл - это двоичный образ Вашей программы, состоящий из кода и данных. То есть это файл, содержащий программу в "чистом" виде. Такая программа (как и EXE-программа) может загружаться в любое место памяти. DOS выполняет ее привязку к физическим адресам при загрузке с помощью установки сегментных регистров. Существенным ограничением COM-программы является то, что она не может занимать больше одного сегмента (соответственно, файл .COM не может быть по длине больше 64К). Программа в формате EXE может иметь любой размер. В самом начале файла программы содержится заголовок (у COM-файла заголовка нет). Этот заголовок используется операционной системой в процессе загрузки программы в память для правильной установки сегментных регистров. Заголовок EXE-файла нужен только при загрузке; когда программа загружена и готова к работе, самого заголовка уже нет в памяти. Заголовок EXE-файла состоит из форматированной зоны и таблицы расположения сегментов (Relocation Table). Форматированная зона выглядит следующим образом:
Таблица расположения сегментов программы начинается сразу после форматированной области и состоит из четырехбайтовых значений в формате "смещение:сегмент". Область файла после таблицы расположения сегментов выравнивается на границу параграфа с помощью байта-заполнителя, и дальше начинается сама программа. В файле 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);}
|

