Разработка метода сбора информации о ходе исполнения программы, который использует возможность модификации памяти процесса icon

Разработка метода сбора информации о ходе исполнения программы, который использует возможность модификации памяти процесса



Смотрите также:
  1   2   3   4


Санкт-Петербургский государственный университет

математико-механический факультет

кафедра системного программирования


Курсовая работа на тему:

Разработка метода сбора информации о ходе исполнения программы, который использует возможность модификации памяти процесса




Студент 445 группы

Булычев Иван Дмитриевич

Научный руководитель:

Преподаватель кафедры Системного Программирования

Баклановский Максим Викторович


Оценка:


Санкт-Петербург

2010

Содержание

Введение 5

Глава 1. Введение в предметную область 6

1.1. Архитектура фон Неймана 6

1.2. История развития микропроцессоров 6

1.3. Организация памяти 8

1.3.1. Оверлейная загрузка программ 9

1.3.2. Виртуальная память 10

1.3.3. Страничная организация памяти 11

1.4. Многозадачность 13

1.5. Процесс в ОС 13

1.6. PE файл 13

Глава 2. Постановка задачи 18

Глава 3. Терминология 19

Глава 4. Аналогичные продукты 21

Глава 5. Идея метода 23

5.1. Способы внедрения 23

5.1.1. Принципиально возможные способы внедрения 23

5.1.2. Реализуемый метод 24

5.2. Метод модификации кода 25

5.2.1. Глобальный обработчик исключений 25

5.2.2. Блокировка секций кода 26

5.2.3. Инициализация управляющего исключения 27

5.2.4. Перехват управляющего исключения 27

5.2.5. Пошаговый анализ тестируемого кода 28

5.2.6. Внедрение T-кода 29

5.2.7. Продолжение исполнения программы 30

5.2.8. Маскировка внесенных изменений 30

5.2.9. Уменьшение числа управляющих исключений 31

5.2.10. Визуализация результатов профилировки 31

Глава 6. Реализация метода 33

6.1. Глобальный обработчик исключений 33

6.2. Дизассемблер длин инструкций 34

6.3. Фильтрация управляющих исключений 35

6.4. Внедрение T-кода 35

6.4.1. Анализ исходного кода 36

6.4.2. Построение модифицированного кода 37

6.4.3. Добавление счетчиков 40

6.5. Продолжение исполнения 41

Глава 7. Результаты применения 42

7.1. Корректность результатов 45

Заключение 47

Литература 49

Приложения 51

Приложение 1. Утилита №1. Разделяемая секция PE файла 51

Приложение 2. Утилита №2. Атрибуты прав доступа страниц памяти 52

Приложение 3. Утилита №3. Редактирование секции кода PE файла 55

Приложение 4. Дизассемблер длин инструкций 57

Приложение 5. Процедура анализа 58

Приложение 6. Внедряемые процедуры 60

Приложение 7. Тестовый пример 64

Введение

В настоящее время значительная часть промышленного программирования осуществляется на managed языках, таких как JAVA, C#, managed C++ и т.д. В силу своих особенностей они не позволяют оптимальным образом управлять ресурсами компьютера. Из-за работы сборщика мусора, JIT компиляции, контроля типов и других различных проверок производительность приложения падает в разы. С другой стороны, эти языки позволяют писать огромные объемы безопасного кода, который легко отлаживается и дополняется, который легко поддается рефакторингу. Можно привести множество других преимуществ, из-за которых при разработке приложения выбираются именно эти языки.

Проблема с производительностью решается следующим образом: функции, для которых время исполнения критично, реализуют с помощью оптимальных алгоритмов на более быстрых языках (ANSI C++, ассемблер). Как правило, таких критических участков в программе значительно меньше, чем остального кода. В качестве примера можно привести локальные и удаленные базы данных, Platform Invocation Services (в Dot.NET) и Java Native Interface (в JAVA).

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

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

Работа имеет две содержательные части: изложение метода и пример реализации. Суть метода, основные идеи и приемы изложены в пятой главе “Идея метода”. Один из вариантов использования метода приведен в шестой главе “Реализация метода”. Также в этой главе и приложениях приведены исходные тексты некоторых ключевых частей реализации.

Все материалы работы находятся по адресу:

http://willzyx-edu-project.googlecode.com/svn/trunk/Proj1/


Глава 1. Введение в предметную область

1.1. Архитектура фон Неймана

30 июня 1945 года Джон фон Нейман написал статью “First Draft of a Report on the EDVAC” [8]. Этот отчет описывает компьютер, состоящий из четырех основных частей: центрального арифметического устройства, центрального управляющего устройства, памяти и средств ввода-вывода. Это первая публикация, которая содержит описание логического дизайна компьютера, реализующего концепцию однородности памяти, при которой программы и данные хранятся в одной и той же памяти. Такая архитектура в дальнейшем стала называться архитектурой фон Неймана.

Помимо однородности памяти, идея архитектуры включает в себя:

  • принцип двоичного кодирования, когда вся информация, поступающая в ЭВМ, кодируется с помощью двоичных сигналов,

  • принцип адресности – основная память состоит из пронумерованных ячеек и процессору в произвольный момент времени доступна любая ячейка, и

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

Существует и альтернативный вариант архитектуры – гарвардская архитектура. Она была разработана Говардом Эйкеном в конце 1930-х годов в Гарвардском университете. Главное отличие от фон-неймановской заключается в раздельном хранении и раздельной обработке команд и данных. Этот подход имеет ряд преимуществ и недостатков. Но из-за сложности такой архитектуры, ее серьезное развитие началось лишь с конца 70-х годов.


^ 1.2. История развития микропроцессоров

Сегодня, более полувека спустя, почти все микропроцессоры имеют фон-неймановскую архитектуру.

1971 год. По заказу небольшой японской компании Nippon Calculating Machine, Ltd, занимающейся производством калькуляторов, компанией Intel был выпущен первый в мире микропроцессор. Intel 4004 стал первым 4-битным коммерчески доступным однокристальным микропроцессором.

1972 год. Компания Intel разрабатывает первый 8-битный центральный процессор - Intel 8008. Процессор позиционировался как процессор для продвинутых калькуляторов общего назначения, терминалов ввода-вывода и автоматов бутылочного разлива.

1974 год. Компания Intel выпускает 8-битный микропроцессор Intel 8080. Он представлял собой усовершенствованную версию процессора Intel 8008. По заверениям Intel, этот процессор обеспечивал десятикратный прирост производительности по сравнению с микропроцессором Intel 8008. Благодаря 16-разрядной адресной шине процессор позволял производить адресацию 64 Кбайт памяти, которая не разделялась на память команд и данных. На базе нового микропроцессора Intel 8080 фирмой MITS был выпущен первый в мире миникомпьютерный комплект (персональный компьютер) Altair-8800.

1978 год. Intel 8086 — первый 16-битный микропроцессор выпущенный компанией Intel. Процессор имел набор команд, который применяется и в современных процессорах, именно от этого процессора берёт своё начало известная на сегодня архитектура x86. Размер шины адреса был увеличен с 16 бит до 20 бит, что позволило адресовать 1 Мбайт памяти

Для того чтобы адресовать больший, чем i8080, объём памяти, потребовалось изменить способ адресации памяти. Поэтому для адресации 1 Мбайт памяти применили следующую схему. На шину адреса подавался физический адрес размером 20 бит, который формировался путём сложения содержимого одного из сегментных регистров (16 бит), умноженного на 16, с содержимым указательного регистра: таким образом, адресация ячейки памяти производилась по номеру сегмента и эффективному адресу ячейки в сегменте (называемому также смещением). Этот метод впоследствии назвали реальным режимом адресации процессора, такой режим позволяет адресовать до 1 Мбайт памяти.

1982 год. Intel 80286 (также известный как i286) — 16-битный x86-совместимый микропроцессор второго поколения фирмы Intel.

В процессоре i286 было реализовано два режима работы — защищённый режим и реальный режим. В реальном режиме работы процессор был полностью совместим с процессорами x86, выпускавшимися до этого, то есть процессор мог выполнять программы, предназначенные для Intel 8086/8088/8018x с минимальными модификациями. В формировании адреса участвовали только 20 линий шины адреса, поэтому максимальный объём адресуемой памяти, в этом режиме, остался прежним — 1 Мбайт.

В защищённом режиме процессор мог адресовать до 16 Мбайт виртуальной памяти, за счёт изменения механизма адресации памяти. Программист и разрабатываемые им программы используют логическое адресное пространство (виртуальное адресное пространство), размер которого может составлять 16 Мбайт. Логический адрес преобразуется в физический адрес аппаратно с помощью блока управления памятью (MMU). Благодаря защищённому режиму, в памяти можно хранить только ту часть данных, которая необходима в данный момент, а остальная часть могла храниться во внешней памяти (например, на жёстком диске). Когда программа обращается к тем данным, которых нет в физической памяти, операционная система может приостановить ее исполнение, загрузить требуемую секцию из внешней памяти и возобновить выполнение программы.

Для защиты от выполнения привилегированных команд, которые могут обрушить всю систему, для защиты доступа к данным и для защиты сегментов кода в процессоре i286 была введена защита по привилегиям [6]. Было выделено 4 уровня привилегий (кольца защиты, режимы работы): от самого привилегированного 0 уровня (Ring 0, режим ядра), предназначенного для ядра системы, до наименее привилегированного 3 уровня (Ring 3, пользовательский режим), предназначенного для прикладных программ.

1985 год. Intel 80386 - 32-битный x86-совместимый процессор третьего поколения фирмы Intel.

Процессор i386 полностью совместим со своими предшественниками — процессорами 8086-80286. Он выполняет программы, предназначенные для них без необходимости модификации кода и перекомпиляции (или с минимальными модификациями).

Адресация в защищенном режиме стала 32-битной (с возможностью создания 16-битных сегментов, для совместимости с 80286). Она позволила впервые со времени появления 8086 забыть о сегментации, а точнее, ограничении размера сегмента 64 килобайтами (ограничение 16-битного адреса). До появления i386 программы и операционные системы использовали несколько моделей организации памяти, различающихся по организации в памяти сегментов кода, стека и данных. 32-битный адрес позволил вместо них использовать одну простую плоскую модель, в которой все сегменты задачи находятся в одном и том же месте адресного пространства памяти. Плоская модель расширяет размер такого “общего” сегмента до 4 Гбайт. И для того, чтобы иметь возможность адресовать такой огромный по тем временам объем данных, был разработан механизм виртуальной памяти.

^ Дальнейшее развитие процессоров. В последующих моделях процессоров компания Intel занималась оптимизацией работы с памятью, ускорением и распараллеливанием выполнения инструкций, совершенствованием архитектуры:

  • ввод кэша первого и второго уровней;

  • раздельное кэширование программного кода и данных;

  • многоядерные процессоры.


^ 1.3. Организация памяти

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

Работа с оперативной памятью в фон-неймановских архитектурах имела свой собственный особый путь развития:

  1. Сначала, когда память измерялась десятками и сотнями килобайт, очень остро стояла проблема размещения данных и исполняемого кода в таком малом объеме памяти.

  2. С появлением защищенного режима проблема малого объема оперативной памяти решилась сама собой (4-х Гб адресного пространства хватало для всех мыслимых задач). Был реализован механизм виртуализации памяти, а с ним появилось множество задач по его оптимизации.


^ 1.3.1. Оверлейная загрузка программ

Процессоры фирмы Intel, начиная с версии 8086, вплоть модели Intel 80286 использовали реальный режим адресации. Объем памяти в этом режиме был ограничен одним мегабайтом. Также невозможно было создание полноценных многозадачных операционных систем. Но все же такие компьютеры в свое время широко использовались. Конечно, тогда программы были по размеру значительно меньше, чем современные, но очень часто возникала такая ситуация, что мегабайта для них было либо недостаточно, либо они занимали такое значительное место в памяти, что такая растрата была просто неприемлемой. Поэтому появлялись различные методы частичной загрузки приложения в оперативную память. Отметим один из них – оверлейную загрузку программ. Ранее она была очень распространена. Например, ее поддержка была введена в Turbo Pascal 3.0.

Смысл оверлея состоит в том, чтобы не загружать программу в память целиком, а разбить ее на несколько модулей и помещать их в память по мере необходимости. При этом на одни и те же адреса в различные моменты времени будут отображены разные модули. Оверлеи могут значительно сократить объем памяти, необходимый для выполнения программы. Фактически, так как в любой момент времени в памяти размещаются только части программы, с помощью оверлеев можно выполнять программы, многократно превосходящие по объему доступную память.

Один из примеров использования механизма оверлеев:


рис. 1


Здесь (см. рис. 1) в оперативной памяти всегда находится только один оверлей, и, при вызове процедуры из другого оверлея, происходит замещение в памяти одного другим.

Уделялось много внимания тому, как распределять процедуры между оверлейными модулями: т.к. доступ к внешней памяти выполняется всегда в разы дольше, чем к оперативной, то нужно стремиться к минимизации загрузок модулей с диска. Для этого необходимо, чтобы каждый оверлейный модуль был как можно более самодостаточным. Если это невозможно, стараются вынести процедуры, на которые ссылаются из нескольких оверлеев, в отдельный модуль, называемый резидентной частью или резидентным ядром. Это модуль, который всегда находится в памяти и не разделяет свои адреса ни с каким другим оверлеем. Естественно, оверлейный менеджер должен быть частью этого ядра.

Потребность в таком способе загрузки появляется, если у нас виртуальное адресное пространство мало, например 1 Мбайт или даже всего 64 Кбайт, а программа относительно велика. На современных 32-разрядных системах виртуальное адресное пространство измеряется гигабайтами, и большинству программ этого хватает, а проблемы с нехваткой можно решать совсем другими способами. Тем не менее, существуют различные системы, даже и 32-разрядные, в которых нет устройства управления памятью, и размер виртуальной не может превышать объема оперативной памяти, установленных на плате.

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


^ 1.3.2. Виртуальная память

Появление защищенного режима в 32-разрядных процессорах позволило адресовать все 4 Гб, а также аппаратно были реализованы функции, которые обеспечивают необходимую защиту памяти, позволяют реализовать в операционных системах виртуальное адресное пространство и многозадачность.

Для адресации операндов в физическом адресном пространстве прикладные программы используют логическую адресацию, т.е. адреса, используемые программистом, необязательно совпадают с физическими. Блок управления памятью транслирует логические адреса программ в физические и посылает их на системную шину. Все это происходит на аппаратном уровне. Логическое адресное пространство организуется как набор из элементарных структур - байтов, сегментов, страниц. Наиболее популярны следующие модели:

  • ^ Плоское (линейное) логическое адресное пространство. Это массив байтов, не имеющий определенной структуры. Трансляция адреса не требуется, поскольку логический адрес совпадает с физическим.

  • Сегментированное логическое адресное пространство. Состоит из нескольких сегментов, каждый из которых может быть произвольной длины. Логический адрес содержит идентификатор сегмента и смещение внутри сегмента.

  • Страничное логическое адресное пространство. Состоит из большого числа страниц, каждая из которых включает фиксированное число байтов. Логический адрес состоит из идентификатора (номера) страницы и смещения внутри страницы.

  • Сегментно-страничное логическое адресное пространство. Состоит из сегментов, которые в свою очередь состоят из страниц. Логический адрес транслируется в номер страницы и смещение в ней, которые затем транслируются в физический адрес.


^ 1.3.3. Страничная организация памяти

В настоящее время очень широко распространен вариант страничной организации памяти. Поддержка такого режима присутствует в большинстве 32-битных и 64-битных процессоров. Такой режим является классическим для почти всех современных ОС, в том числе Windows и семейства UNIX. Он впервые был реализован фирмой DEC в процессорах VAX и ОС VMS в конце 70-х годов. В семействе x86 поддержка появилась с поколения 386, оно же первое 32-битное поколение.

Суть его заключается в том, что оперативная память делится на страницы: области памяти фиксированной длины (например, 4096 байт), которые являются минимальной единицей выделяемой памяти. Процесс обращается к памяти с помощью адреса виртуальной памяти, который содержит в себе номер страницы и смещение внутри страницы. Операционная система преобразует виртуальный адрес в физический, при необходимости подгружая страницу из внешней памяти в оперативную. При запросе на выделение памяти или при загрузке сохраненной страницы операционная система может “сбросить” на жёсткий диск страницы, к которым давно не было обращений.

С
рис. 2

ейчас рассмотрим процесс трансляции виртуального адреса в физический (см. рис. 2). Для 32-битных систем, адрес делится на три части: старшие 10 бит, средние 10 бит и младшие 12 бит. Первое число определяет номер записи в каталоге страниц. Каталог страниц состоит из 1024 записей по 4 байта – PDE (page directory entries), каждая из которых адресует соответствующую таблицу страниц. Следующее 10-битное число в виртуальном адресе определяет номер записи в таблице страниц. Каждая такая запись также состоит их 4-х байт и является дескриптором виртуальной страницы (PTE – page table entries). В этом дескрипторе хранится номер соответствующей страницы в физической памяти и поле флагов, описывающих состояние и атрибуты защиты страницы. Последние 12 бит определяют смещение внутри страницы.

На долю процессора в этом механизме виртуализации приходится порождение исключений #PF (PAGE FAULT) и #GP (GENERAL PROTECTION FAULT), доступ к памяти в соответствии с настройками дескрипторов страниц, хранящимися в иерархичной структуре в физической памяти, и использование буфера ассоциативной трансляции – TLB [5].

В семействе операционных систем Windows виртуальная память зародилась, начиная с версии 3.0 [11]. В реализациях, которые существуют сейчас, операционная система отвечает за загрузку и выгрузку страниц во внешнюю физическую память: обрабатывая исключения #PF, в оперативную память загружается отсутствующая страница, а если места для нее там нет, другая страница, использование которой не ожидается в ближайшее время, выгружается на внешний носитель. Так, в Windows NT на выбранных разделах создаются страничные файлы pagefile.sys, в которые и записываются неиспользуемые в настоящее время страницы. В некоторых других операционных системах для таких нужд создаются целые отдельные разделы.

1.4. Многозадачность

Существует другой вид виртуализации – многозадачность. Вытесняющая многозадачность, при которой сама операционная система решает, когда и сколько процессорного времени отдать задаче, у Microsoft в ОС Windows впервые появилась в версиях 3.x, в следующих – многократно совершенствовалась. И то совпадение, что начало развития виртуальной памяти совпадает с началом развития многозадачности, не случайно – эти два понятия очень тесно связаны и вместе составляют мощный инструмент управления ресурсами компьютера. За более чем 20 лет этот механизм виртуализации претерпел значительные изменения, появилось много достойных реализаций в других операционных системах.


^ 1.5. Процесс в ОС

Попробуем раскрыть понятие процесса для последних версий операционных систем серии Windows NT: Windows 2000 и последующих версий.

В общем случае задача, или процесс – это выполнение инструкций компьютерной программы на процессоре ЭВМ. В нашем случае, процесс представляет собой сложную структуру, которая включает:

  • структуру данных, содержащую всю информацию о процессе, в том числе список открытых дескрипторов различных системных ресурсов, уникальный идентификатор процесса, различную статистическую информацию и т.д.;

  • адресное пространство - диапазон адресов виртуальной памяти, которым может пользоваться процесс;

  • исполняемую программу и данные, проецируемые на виртуальное адресное пространство процесса. К таким данным относятся исполняемые файлы (EXE) и файлы динамических библиотек (DLL), отображаемые в адресное пространство процесса специальным образом, память стека и память, динамически выделенную из кучи.


1.6. PE файл

Portable Executable – формат исполняемых файлов, объектного кода и динамических библиотек, используемый в 32- и 64-битных версиях операционной системы Microsoft Windows. Формат PE представляет собой структуру данных, содержащую всю информацию, необходимую загрузчику для запуска программы, которая находится в данном файле. Наиболее часто встречаются три вида файлов в формате PE: исполнимые модули (*.EXE), динамически подключаемые библиотеки (*.DLL), драйверы устройств, работающие в режиме ядра (Kernel mode drivers).

Общеизвестно, что Windows NT многое унаследовала от VAX VMS и UNIX. И стандарт PE не является исключением – за основу был взят формат COFF (Common Object File Format — стандартный формат объектного файла), который впервые был введен в UNIX. Но COFF к тому времени несколько устарел, и, чтобы удовлетворить потребностям новых операционных систем, было решено внести в него небольшие изменения и дать ему название PE.

Portable Executable – дословно переводится как “переносимый исполняемый”. Этот формат называется переносимым, так как все реализации Windows NT в различных системах (Intel 386, MIPS, Alpha, Power PC и т.д.) используют один и тот же исполняемый формат. Конечно, имеются различия, например, связанные с двоичной кодировкой команд процессора: нельзя запустить на Intel исполняемый РЕ-файл, откомпилированный в MIPS. Тем не менее, существенно, что нет нужды полностью переписывать загрузчик операционной системы и программные средства для каждого нового процессора.

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

Еще одним важным понятием является секция. Именно из секций состоит большая часть PE файла. Они содержат либо код программы, либо данные. Некоторые секции содержат код и данные, непосредственно объявляемые и используемые программами, тогда как другие секции данных создаются компоновщиками специально для операционной системы и содержат информацию, необходимую для правильной загрузки программы в память.

Для работы с PE-файлами используются три различных схемы адресации: физические адреса (называемые также смещениями raw pointers или raw offset), отсчитываемые от начала файла, виртуальные адреса (virtual address или VA), отсчитываемые от начала адресного пространства процесса и относительные виртуальные адреса (relative virtual address или RVA), отсчитываемые от базового адреса загрузки модуля.

Итак, структура PE-файла имеет следующий вид (см. рис. 3):

  • В начале файла расположен заголовок MS DOS,

  • Заголовок PE, смещение которого от начала файла хранится в заголовке MS DOS,

  • “Опциональный” заголовок PE-файла,

  • Заголовки секций, количество которых указано в заголовке PE,

  • Сами секции, смещенные в файле на значение, указанное в заголовке секции.

Заголовок MS-DOS и программа-заглушка (Stub Program) служит только для выдачи сообщения о том, что программа не может работать в среде MS-DOS, при попытке ее заставить работать в этом режиме. Заголовок представляет собой запись типа IMAGE_DOS_HEADER (структура, объявленная в WinNT.h). Поле e_lfanew – ключевое в этой структуре, оно содержит смещение (от начала файла) основного PE-заголовка.

И
рис. 3
так, отступив e_lfanew байт от начала файла, мы получим основной заголовок PE файла. Он полностью описывается структурой IMAGE_NT_HEADERS:

  • Первые 4 байта в этой структуре (поле Signature) – сигнатура “PE\0\0”, которая подтверждает, что это действительно файл PE.

  • Главный заголовок PE. Структура IMAGE_FILE_HEADER. Она содержит информацию

    • о количестве секций (поле NumberOfSections),

    • о размере “опционального” заголовка (поле SizeOfOptionalHeader),

    • о типе центрального процессора, под который скомпилирован файл (поле Machine, для i386-машин должен быть выставлен в 0x14C).



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

    • абсолютный базовый адрес загрузки модуля (поле ImageBase), отсчитываемый от начала сегмента; в терминологии спецификации, preferred address (предпочтительный адрес загрузки),

    • RVA адрес точки входа (поле AddressOfEntryPoint),

    • кратность выравнивания секций на диске и в памяти (поля FileAlignment / SectionAlignment),

    • объем зарезервированной / выделенной памяти под стек и кучу в байтах,

    • таблица DATA_DIRECTORY, содержащая указатели на такие структуры как таблицы импорта и экспорта в модуле, таблицу ресурсов, таблицу релокаций и прочие. Из-за того, что документация не задает жестко размер этой структуры, размер всего “опционального” заголовка необходимо определять по соответствующему полю в главном заголовке.

Сразу после “опционального” заголовка один за другим идут заголовки секций. Каждый заголовок представляет собой структуру IMAGE_SECTION_HEADER. Важные поля в этой структуре:

  • PointerToRawData – смещение секции относительно начала файла,

  • SizeOfRawData – размер секции в файле,

  • VirtualAddress – RVA-адрес начала секции в памяти,

  • Misc.VirtualSize – размер секции в памяти,

  • Characteristics – 4-х байтное поле, определяет атрибуты доступа к секции и особенности ее загрузки. Отметим самые важные из них:

    • Первая группа атрибутов описывает содержимое секции

      • IMAGE_SCN_CNT_CODE (0x20) – как секцию кода,

      • IMAGE_SCN_CNT_INITIALIZED_DATA (0x40) – как секцию инициализированных данных,

      • IMAGE_SCN_CNT_UNINITIALIZED_DATA (0x80) – как секцию неинициализированных данных.

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

    • Следующий набор атрибутов настраивает права доступа, проставляемые загрузчиком всем страницам секции в памяти:

      • IMAGE_SCN_MEM_EXECUTE (0x20000000) – права на исполнения,

      • IMAGE_SCN_MEM_READ (0x40000000) – права на чтение,

      • IMAGE_SCN_MEM_WRITE (0x80000000) – права на запись.

Важная особенность процессоров фирмы Intel: при 32-битной страничной адресации атрибуты чтения и исполнения полностью эквивалентны [7].

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

    • IMAGE_SCN_MEM_SHARED (0x10000000) – секция является разделяемой между процессами, использующими этот модуль.

Вот мы рассмотрели структуру заголовка секции. По нему системный загрузчик находит в PE файле секцию и определяет, в какую область виртуальной памяти и как ее необходимо отобразить стандартными средствами мэппинга [13].


^ Глава 2. Постановка задачи

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

Итак, у нас исходный текст некоторой программы, и мы хотим определить, какие процедуры и функции в нем исполняются чаще всего, и, соответственно, те, которые стоит стараться оптимизировать.

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

Метод накладывает следующие ограничения:

  • на аппаратную платформу:

    • архитектура микропроцессора x86;

    • соответственно 32-битная адресация памяти;

  • на операционную систему:

    • ОС Windows NT начиная с версии Windows 2000;

  • на анализируемый исполняемый модуль:

    • должен быть PE файлом (DLL или EXE);

    • исполняемый код не должен изменяться (код не должен быть самомодифицирующимся);

    • исполняемый код должен находиться в секциях кода PE файла и только там;

    • исполняемый код должен состоять только из инструкций общего набора x86 (инструкции расширений, таких как MMX, SSE, 3DNow! – недопустимы).

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

После работы метода, результаты должны быть записаны в файл на диск.





страница1/4
Дата конвертации06.03.2013
Размер0,7 Mb.
ТипКурсовая
  1   2   3   4
Разместите кнопку на своём сайте или блоге:
rud.exdat.com


База данных защищена авторским правом ©exdat 2000-2012
При копировании материала укажите ссылку
обратиться к администрации
Документы