Вопрос 4. Формат исполняемого РЕ файла ОС Windows . Приведите фрагмент программы, читающей из РЕ файла список импортируемых программой функций ОС.


Добавил:DMT
Дата создания:30 декабря 2007, 18:56
Дата обновления:30 декабря 2007, 18:56
Просмотров:9373 последний сегодня, 21:57
Комментариев: 2

Вопрос 4. Формат исполняемого РЕ файла ОС Windows . Приведите фрагмент программы, читающей из РЕ файла список импортируемых программой функций ОС.

up

Комментарии для "Вопрос 4. Формат исполняемого РЕ файла ОС Windows . Приведите фрагмент программы, читающей из РЕ файла список импортируемых программой функций ОС. "


Пользователь: ruslan
Сообщений: 23
Статус: Незримый
Зарегистрирован:
5 января 2008, 2:42
Был:29 января 2008, 21:23
ruslan
smsup
Дата: 5 января 2008, 3:47 Сообщение № 1
Формат исполняемого файла операционной системы в значительной степени отражает встроенные в операционную систему предположения и режимы поведения. Динамическая компоновка, поведение загрузчика и управление памятью – это только три примера специфических свойств операционной системы, которые можно понять по мере изучения формата исполняемых файлов.

Исполняемый файл на диске и модуль, получаемый после загрузки, очень похожи. Загрузчик попросту использует отображенные в память файлы Win32, чтобы загрузить соответствующие части РЕ-файла в адресное пространство программы. Так же просто загружается и DLL. После того как ЕХЕ или .DLL модуль загружены, Windows обращается с ними так же, как и с другими отображенными в память файлами.

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

Заголовок MS-DOS


image1


Заголовок MS-DOS занимает первые 64 байта PE файла. Структура, представляющая содержание MS-DOS-заголовка следующая:


typedef struct _IMAGE_DOS_HEADER { //DOS .EXE заголовок
USHORT e_magic; //MZ
USHORT e_cblp; //Байты на последней
//странице файла
USHORT e_cp; //Страницы в файле
USHORT e_crlc; //Настройки
USHORT e_cparhdr; //Размер заголовка в
//параграфах
USHORT e_minalloc; //Минимальная выделенная память
USHORT e_maxalloc; //Максимальная выделенная память
USHORT e_ss; //Начальное (относительное)
//значение SS
USHORT e_sp; //Начальное значение SP
USHORT e_csum; //Контрольная сумма
USHORT e_ip; //Начальное значение IP
USHORT e_cs; //Начальное (относительное)
//значение CS
USHORT e_lfarlc; //адрес Файла таблицы настройки
USHORT e_ovno; //Оверлейный номер
USHORT e_res [4]; //Зарезервированные слова
USHORT e_oemid; //OEM идентификатор (для
//e_oeminfo)
USHORT e_oeminfo; //OEM информация; e_oemid
//специфический
USHORT e_res2 [10]; //Зарезервированные слова
LONG e_lfanew; //адрес смещения PE-заголовка
} IMAGE_DOS_HEADER, * PIMAGE_DOS_HEADER;



Основной заголовок РЕ-файла представляет структуру типа IMAGE_NT_НEADERS, определенную в файле WINNT.H. Структура IMAGE_NT_HEADERS в памяти – это то, что Windows использует в качестве своей базы данных модуля в памяти. Каждый загруженный ЕХЕ-файл или DLL представлены в Windows структурой IMAGE_NT_HEADERS. Эта структура состоит из двойного слова и двух подструктур, как показано ниже:

DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER OptionalHeader;


PE File Signature

Поле Signature (сигнатура – подпись), представленное как ASCII код, – это РЕ\0\0 (два нулевых байта после РЕ). Если поле e_lfanew в заголовке DOS указало вместо обозначения РЕ обозначение NE в этом месте, значит, вы работаете с файлом Win16 NE. Аналогично, если указано обозначение LE в поле Signature, то это файл VxD (VirtualDeviceDriver – драйвер виртуального устройства). Обозначение LX указывает на файл старой соперницы Windows 95 – OS/2.

PE File Header

За двойным словом – сигнатурой РЕ, в заголовке РЕ-файла следует структура типа IMAGE_FILE_HEADER. Поля этой структуры содержат только самую общую информацию о файле.
Далее приводятся поля IMAGE_FILE_HEADER:

typedef struct _IMAGE_FILE_HEADER
{
USHORT Machine;
USHORT NumberOfSections;
ULONG TimeDateStamp;
ULONG PointerToSymbolTable;
ULONG NumberOfSymbols;
USHORT SizeOfOptionalHeader;
USHORT Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER


Machine – это центральный процессор, для которого предназначен файл. Определены следующие идентификаторы процессоров:

Intel I386 0xl4C
Intel I860 0xl4D
MIPS R3000 0х162
MIPS R4000 0х166
DEC Alpha AXP 0х184
Power PC 0x1F0 (little endian)
Motorola 68000 0х268
PA RISC 0х290 (Precision Architecture)


NumberOfSections – количество секций в ЕХЕ- или OBJ-файле.

TimeDateStamp – время, когда файл был создан компоновщиком (или компилятором, если это OBJ-файл). В этом поле указано количество секунд, истекших с 16:00 31.12.1969

PointerToSymbolTable – файловое смещение COFF-таблицы символов. Это поле используется только в OBJ- и РЕ-файлах с информацией COFF-отладчика. РЕ-файлы поддерживают разнообразные отладочные форматы, так что отладчики должны ссылаться ко входу IMAGE_DIRECTORY_ENTRY_DEBUG в каталоге данных.

NumberOfSymbols – количество символов в COFF-таблицс символов.

SizeOfOplionalHeader – размер необязательного заголовка, который может следован, за этой структурой. В исполняемых файлах – это размер структуры IMAGE_OPTIONAL_HEADER, которая следует за этой структурой.

Characteristics – флаги, содержащие информацию о файле. Здесь описываются некоторые важные поля.
0х0001 – файл не содержит перемещений
0х0002 – файл представляет исполняемое отображение (т.е. это не OBJ- или LIB-файл)
0х2000 – файл является библиотекой динамической компоновки (DLL), а не программой

PE File Optional Header

Третьим компонентом заголовка РЕ-файла является структура типа IMAGE_OPTIONAL_HEADER. Для РЕ-файлов эта часть является обязательной. Наиболее важными полями являются поля ImageBase и Subsystem.

ImageBase – когда компоновщик создает исполняемый файл, он предполагает, что файл будет отображен в определенное место в памяти, именно этот адрес и хранится в этом поле.

Subsystem – тип подсистемы, которую данный исполняемый файл использует для своего пользовательского интерфейса. WINNT.H определяет следующие значения:
NATIVE = 1 – подсистема не требуется (например, для драйвера устройства)
WINDOWS_GUI = 2 – запускается в подсистеме Windows GUI
WINDOWS_GUI = 3 – запускается в подсистеме Windows character (терминальное приложение)
OS2_GUI = 5 – запускается в подсистеме OS/2 (только приложения OS/2 IJC)
POSIX_GUI = 7 – запускается в подсистеме Posix

Таблица секций

Сразу после заголовка РЕ-файла в памяти следует массив из 1MAGE_SECT10N_HEADER. Эта таблица. содержит информацию о каждой секции отображения. Количество элементов этого массива задается в заголовке РЕ-файла (поле IMAGE_NT_HEADER.FileHeader.NumberOfSections). Секции в отображении упорядочены по их стартовому адресу, а не в алфавитном порядке.
Каждый IMAGE_SECTION_HEADER представляет собой полную базу данных об одной секции файла ЕХЕ или OBJ \ и имеет следующий формат.

#define IMAGE_SIZEOF_SHORT_NAME 8
typedef struct _IMAGE_SECTION_HEADER
{
UCHAR Name[IMAGE_SIZEOF_SHORT_NAME];
union {
ULONG PhysicalAddress;
ULONG VirtualSize;
} Misc;
ULONG VirtualAddress;
ULONG SizeOfRawData;
ULONG PointerToRawData;
ULONG PointerToRelocations;
ULONG PointerToLinenumbers;
USHORT NumberOfRelocations;
USHORT NumberOfLinenumbers;
ULONG Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;


Name[IMAGE_SIZEOE_SHORT_NAME] – 8-байтовое имя в стандарте ANSI (не Unicode), которое именует секцию.

Misc – это поле имеет различные назначения в зависимости от того, встречается ли оно в ЕХЕ- или OBJ-файле. В ЕХЕ-файле оно содержит виртуальный размер секции программного кода или данных. В случае OBJ-файлов это поле указывает физический адрес секции.

VirtualAddress – в случае ЕХЕ-файлов это поле содержит RVA, куда загрузчик должен отобразить секцию. Средства Microsoft устанавливают по умолчанию RVA первой секции равным 0х101. Для объектных файлов это поле устанавливается в 0.

SizeOfRawData – в ЕХЕ-файлах это поле содержит размер секции, выровненный па ближайшую верхнюю границу размера файла.
PointerToRawData – файловое смещение участка, где находятся исходные данные для секции. Если пользователь сам отображает в мять РЕ- или COFF-файл (вместо того, чтобы доверить загрузку операционной системе), это поле важнее, чем в VirtualAddress.

PointerToRelocations – в объектных файлах это файловое смещение информации о поправках, которая следует за исходными данными для данной секции. В ЕХЕ-файлах это поле устанавливается в 0.

PointerToLinenumhers – файловое смещение таблицы номеров строк. Таблица номеров строк ставит в соответствие номера строк исходного файла адресам, по которым можно найти код, сгенерированный для данной строки. Обычно только секции с программным кодом (например, .text или CODE) имеют номера строк. В ЕХЕ-файлах номера строк собраны в конце файла после исходных данных для секций. В объектных файлах таблица номеров строк для секции следует за исходными данными секции и таблицей перемещений для этой секции.

NumberOfRelocations – количество перемещений в таблице поправок для данной секции (используется только в объектных файлах).
NumberOfLinenumbers – количество номеров строк в таблице номеров строк для данной секции.
Characteristics – набор флагов, которые указывают на атрибуты секции (программа/данные, предназначен для чтения, предназначен для записи и т.н.).

Часто встречающиеся секции

Секция .text (или CODE, если PE-файл создан Borland C++)

В этой секции собран весь программный код общего назначения, генерируемый компилятором или ассемблером. Компоновщик объединяет все секции .text из различных объектных файлов в одну большую секцию .text в ЕХЕ-файле.

Секция .data (или DATA, если PE-файл создан Borland C++)

Инициализированные данные попадают в секцию .data. Инициализированные данные состоят из тех глобальных и статических переменных, которые были проинициализированы во время компиляции. Они также включают строковые литералы (например, строку "Hello World" в программе C/C++). Компоновщик объединяет все секции .data из разных объектных и LIB-файлов в одну секцию .data в ЕХЕ-файле. Локальные переменные расположены в стеке цепочки и не занимают места в секциях .data и .bss.

Секция .bss

В секции .bss хранятся неинициализированные статические и глобальные переменные. Компоновщик объединяет все сек¬ции .bss из разных объектных и LIB-файлов в одну секцию .bss в ЕХЕ-файле.

Секция .CRT

Еще одна секция для инициализированных данных, используемая библиотеками поддержки выполнения программы Microsoft C/C++. Данные из этой секции используются для таких це¬лей, как вызов конструкторов статических классов C++ перед вызовом main или WinMain.

Секция .rsrc

Секция .rsrc содержит ресурсы модуля.

Секция .idata

Секция .idata (или таблица импорта) содержит информацию о функциях (и данных), которые модуль импортирует из других DLL. Таблица импорта начинается с массива, состоящего из IMAGE_IMPORT_DESCRIPTOR. Каждый элемент (IMAGE_IMPORT_DESCRIPTOR) соответствует одной из DLL, с кото¬рой неявно связан данный РЕ-файл. Количество элементов в массиве нигде не учитывается. Вместо этого последняя структу¬ра массива IMAGE_IMPORT_DESCRIPTOR имеет поля, содержащие NULL.
Структура IMAGE_IMPORT_DESCRIPTOR имеет следующий формат

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk;
};
DWORD TimeDateStamp;

DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR


Characteristics/OriginalFirstThunk – в этом поле содержится смещение (RVA) массива двойных слов. Каждое из этих двойных слов в действительности является объединением IMAGE_THUNK_DATA. Каждое двойное слово IMAGE_THUNK_DATA соответствует одной функции, импортируемой данным ЕХЕ-файлом или DLL.

TimeDateStamp – отметка о времени и дате, указывающая, когда был создан данный файл.
ForwarderChain – это поле имеет отношение к передаче, когда одна DLL передает ссылку на какую-то свою функцию другой DLL.
Name – это RVA строки символов ASCII, оканчивающейся нулем и содержащей имена импортируемых DLL.

FirstThunk – RVA-смещение массива двойных слов IMAGE_THUNK_DATA. В большинстве случаев двойное слово рассматривает¬ся как указатель на структуру IMAGE_IMPORT_BY_NAME. Это структура выглядит следующим образом:

typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
BYTE Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;


Hint – номер экспорта у функции импорта.
Name – Строка ASCIIZ с именем импортируемой функции.



Фрагмент программы читающей из РЕ файла список импортируемых программой функций ОС.

Приведенный ниже фрагмент программы зарисывает в файл FunctionList.txt список библиотек с импортируемыми программой функциями.

Код на C++
  1. void ShowImportFunction()
  2. {
  3. BYTE *pImage = (BYTE*)GetModuleHandle(NULL);
  4. IMAGE_DOS_HEADER *idh;
  5. IMAGE_OPTIONAL_HEADER *ioh;
  6. IMAGE_SECTION_HEADER *ish;
  7. IMAGE_IMPORT_DESCRIPTOR *iid;
  8. IMAGE_IMPORT_BY_NAME *ibn;
  9. IMAGE_THUNK_DATA *thunk;
  10. int i = 0;
  11. DWORD j = 0;
  12. char lib[] = "Импортируемая библиотека: ";
  13. HANDLE file = CreateFile(TEXT("FunctionList.txt"),GENERIC_READ|GENERIC_WRITE,
  14. 0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_RANDOM_ACCESS,0);
  15. idh = (IMAGE_DOS_HEADER*)pImage;
  16. ioh = (IMAGE_OPTIONAL_HEADER*)
  17. (pImage + idh->e_lfanew + 4 +
  18. sizeof(IMAGE_FILE_HEADER));
  19. ish = (IMAGE_SECTION_HEADER*)((DWORD)ioh + sizeof(IMAGE_OPTIONAL_HEADER));
  20. for(i = 0; i < 16; i++)
  21. if(strcmp((char*)(ish + i)->Name, ".idata") == 0)
  22. break;
  23. iid =
  24. (IMAGE_IMPORT_DESCRIPTOR*)(pImage +(ish + i)->VirtualAddress);
  25. while(iid->Name)
  26. {
  27. WriteFile(file,lib,strlen(lib)+1, &j,0);
  28. WriteFile(file,
  29. strcat((char*)(pImage + iid->Name),"\x0D\x0A"),
  30. strlen((char*)(pImage + iid->Name))+2, &j,0);
  31. thunk =
  32. (IMAGE_THUNK_DATA*)(pImage + (DWORD)iid->Characteristics);
  33. while(thunk->u1.AddressOfData)
  34. {
  35. ibn = (IMAGE_IMPORT_BY_NAME*)
  36. (pImage + thunk->u1.AddressOfData);
  37. WriteFile(file,strcat((char*)(ibn->Name),"\x0D\x0A"),
  38. strlen((char*)(ibn->Name)) + 2, &j, 0);
  39. thunk = (IMAGE_THUNK_DATA*)((DWORD)
  40. thunk+sizeof(IMAGE_THUNK_DATA));
  41. }
  42. iid++;
  43. }
  44. CloseHandle(file);
  45. }
При использовании обязательна ссылка на http://DMTSoft.ru


P.S. Это фрагмент реально работающей проги (сам писал и сам проверял) sm
Пользователь: DMT
Сообщений: 123
Статус: Программист
Зарегистрирован:
18 октября 2007, 2:35
Был:13 ноября 2017, 4:54
DMT
smsup
Дата: 9 января 2008, 23:32 Сообщение № 2
Спасибо lilo за предоставленные ответы по ОС
Код на C++
  1. #include <windows.h>
  2. #include <stdio.h>
  3. #include <iostream>
  4. using namespace std;
  5. PIMAGE_SECTION_HEADER GetEnclosingSectionHeader(DWORD rva, PIMAGE_NT_HEADERS
  6. pNTHeader)
  7. {
  8. PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader);
  9. for (unsigned i=0; i<pNTHeader->FileHeader.NumberOfSections; i++, section++)
  10. {
  11. if ((rva >= section->VirtualAddress) &&
  12. (rva < (section->VirtualAddress + section->Misc.VirtualSize)))
  13. return section;
  14. }
  15. return 0;
  16. }
  17. void DumpFile(LPSTR filename)
  18. {
  19. HANDLE hFile;
  20. HANDLE hFileMapping;
  21. LPVOID lpFileBase;
  22. ULONG ulSize;
  23. PIMAGE_THUNK_DATA thunk;
  24. PIMAGE_IMPORT_BY_NAME pOrdinalName;
  25. PIMAGE_DOS_HEADER pDOSHeader;
  26. PIMAGE_NT_HEADERS pNTHeader;
  27. PIMAGE_IMPORT_DESCRIPTOR importDesc;
  28. PIMAGE_SECTION_HEADER pSection;
  29. //открываем файл
  30. hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
  31. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  32. if (hFile == INVALID_HANDLE_VALUE) return;
  33. //отображаем файл в память
  34. hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
  35. if (!hFileMapping)
  36. {
  37. CloseHandle(hFile);
  38. return;
  39. }
  40. // преобразовать в указатель
  41. lpFileBase = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0);
  42. if (!lpFileBase)
  43. {
  44. CloseHandle(hFileMapping);
  45. CloseHandle(hFile);
  46. return;
  47. }
  48. printf("Dump of file %s:\n", filename);
  49. //указатель на DOS-заголовок
  50. pDOSHeader = (PIMAGE_DOS_HEADER)lpFileBase;
  51. if (pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE) return;
  52. //находим адрес NT заголовка
  53. pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDOSHeader + pDOSHeader->e_lfanew);
  54. if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) return;
  55. //RVA-адрес таблицы импорта
  56. int importsStartRVA = pNTHeader-> OptionalHeader.DataDirectory
  57. [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
  58. if (!importsStartRVA) return;
  59. //определяем адрес секции
  60. pSection = GetEnclosingSectionHeader(importsStartRVA, pNTHeader);
  61. if (!pSection) return;
  62. int delta = pSection->VirtualAddress - pSection->PointerToRawData;
  63. importDesc = (PIMAGE_IMPORT_DESCRIPTOR) (importsStartRVA - delta +
  64. (DWORD)lpFileBase);
  65. //перебираем список dll
  66. while (importDesc->TimeDateStamp || importDesc->Name)
  67. {
  68. //название dll
  69. printf("%s\n", (PBYTE)(importDesc->Name) - delta + (DWORD)lpFileBase);
  70. //RVA-смещение на массив указателей на функции
  71. thunk = (PIMAGE_THUNK_DATA)importDesc->Characteristics;
  72. if (!thunk) thunk = (PIMAGE_THUNK_DATA)importDesc->FirstThunk;
  73. thunk = (PIMAGE_THUNK_DATA)( (PBYTE)thunk - delta + (DWORD)lpFileBase);
  74. //перебираем функции
  75. while (thunk->u1.AddressOfData)
  76. {
  77. if (!(thunk->u1.Ordinal & IMAGE_ORDINAL_FLAG))
  78. {
  79. pOrdinalName = (PIMAGE_IMPORT_BY_NAME)thunk->u1.AddressOfData;
  80. pOrdinalName = (PIMAGE_IMPORT_BY_NAME)((PBYTE)pOrdinalName –
  81. delta + (DWORD)lpFileBase);
  82. //выводим имя функции
  83. printf(" %s\n", pOrdinalName->Name);
  84. }
  85. thunk++;
  86. }
  87. importDesc++;
  88. }
  89. UnmapViewOfFile(lpFileBase);
  90. CloseHandle(hFileMapping);
  91. CloseHandle(hFile);
  92. }
  93. main(int argc, char *argv[])
  94. {
  95. if ( argv[1] ) DumpFile( argv[1] );
  96. }
  97.  
При использовании обязательна ссылка на http://DMTSoft.ru