Операционная система 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);} |