StudyDocs.ru Logo

report1.docx


Санкт-Петербургский политехнический университет Петра Великого Институт Информационных Технологий и УправленияКафедра компьютерных систем и программных технологий















Отчёт по практической работе № 1по предмету «Проектирование ОС и компонентов»
Загрузка приложений (Windows/Linux)

















Работу выполнил студент гр. 63501/3 Мартынов С. А. Работу принял преподаватель Душутина Е. В.Санкт-Петербург 2016

Содержание



В рамках данной работы необходимо написать полезную программу для ОС семейства Linux и Windows. Программа должна быть выполнена в качестве резидентного (не де- мона) приложения. Далее переписать ту же программу с использованием динамически загружаемой библиотеки.Таким образом, в результате работы должно получиться четыре программы:
Резидентное приложение для Windows собранное единым модулемРезидентное приложение для Windows с динамической библиотекой (.dll)Резидентное приложение для Linux собранное единым модулемРезидентное приложение для Linux с динамической библиотекой (.so)
В процессе работы требуется изучить принцип загрузки приложений в различных опе- рационных системах и описать особенности приложений использующих динамические библиотеку.


В связи с тем, что сегодня уровень сложности программного обеспечения очень высок, разработка приложений с использованием только какого-либо языка программирования (например, языка C) значительно затрудняется. Программист должен затратить массу времени на решение стандартных задач по созданию многооконного интерфейса. Реализа- ция технологии связывания и встраивания объектов потребует от программиста еще более сложной работы.Чтобы облегчить работу программиста, следует пере использовать ранее написанный код. С одной стороны это позволяет не решать одну задачу дважды, с другой – появляются дополнительные требования по оформлению существующих решений и их распространению. Долгое время в Unix (а потом Linux) среде распространение велось с исходных кодах. При этом предполагалось, что пользователь достаточно грамотен для работы с таким источником.С распространением персональных компьютеров и приложений для них, получили популяр- ность динамические библиотеки, которые позволяли, в частости, обновлять приложения без их пере сборки конечным пользователем, использовать различные языки для решения разных задач и даже упрощение локализации.В данной работе рассматривается процесс загрузки приложений на операционных системах семейства Windows и Linux и порядок работы с динамическими библиотеками.

Процесс загрузки приложений в Linux
ELF – формат исполнения и компоновки
Изначально UNIX производные от нее операционные системы) поддерживали множество исполняемых форматов, но теперь стандартом де-факто для LINUX и BSD стал ELF. Стандарт для формата ELF изначально был разработан и опубликован компанией USL как часть двоичного интерфейса приложений операционной системы UNIX System V. Затем он был выбран комитетом TIS и развит в качестве переносимого формата для различных операционных систем, работающих на 32-разрядной аппаратной архитектуре Intel x86. ELF быстро набрал популярность и, после того как компания HP расширила формат и опубликовала стандарт ELF-64, распространился и на 64-разрядных платформах. Иногда еще встречается древний a.out, но это достаточно особые случаи, требующие совместимости с железом.Аббревиатура ELF расшифровывается как Execution and Linkable Format (формат исполне- ния и компоновки). Он во многом напоминает win32 PE. В начале ELF-файла расположен служебный заголовок (ELF-header), описывающий основные характеристики файла тип (исполнения или линковки), архитектура ЦП, виртуальный адрес точки входа, размеры и смещения остальных заголовков. . .За ELF-header’ом следует таблица сегментов (program header table), перечисляющая имею- щиеся сегменты и их атрибуты. В формате линковки она необязательно. Линкеру сегменты не важны и он работает исключительно на уровне секций. Напротив, системный загрузчик, загружающий исполняемый ELF-файл в память, игнорирует секции, и оперирует целыми сегментами[1].Стандарт формата ELF различает несколько типов файлов:
Перемещаемый файл – хранит инструкции и данные, которые могут быть связаны с другими объектными файлами. Результатом такой связи может быть разделяемый объектный файл или исполняемый файл. К этому типу относятся объектные файлы статических библиотек.Разделяемый объектный файл – также содержит инструкции и данные и может быть связан с другими перемещаемыми файлами и разделяемыми объектными файлами, в результате чего будет создан новый объектный файл, либо при запуске программы на выполнение операционная система может динамически связать его с исполняемым файлом программы, в результате чего будет создан исполняемый образ программы. В последнем случае речь идет о разделяемых библиотеках.

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

Сегменты и секции
Сегмент это непрерывная область адресного пространства со своими атрибутами доступа. В частности, сегмент кода имеет атрибут исполнения, а сегмент данных – атрибуты чтения и записи. Стоит отметить, что ELF-сегменты это не сегменты x86 процессора! В защищенном режиме 386+ никаких "сегментов"уже нет, а есть только селекторы и все сегменты ELF-файла загружается в единый 4 Гбайтовый x86-сегмент! В зависимости от типа сегмента, величина выравнивания в памяти может варьировать от 4h до 1000h байт (размер страницы на x86). В самом ELF-файле хранятся в невыровненном виде, плотно прижатые друг к другу.Ближайший аналог ELF-сегментов – PE-секции, но в PE-файлах, секция – это наименьшая структурная единица, а в ELF-файлах сегмент может быть разбит на один или несколько фрагментов – секций. В частности, типичный кодовый сегмент состоит изсекций .init – процедуры инициализации,секции .plt – секция связок,секции .text – основой код программы,секции .finit – процедуры финализации.Секции нужны линкеру для комбинирования, чтобы он мог отобрать секции с похожими атрибутами и оптимальным образом растасовать их по сегментам при сборке файла, то есть "скомбинировать"[2].Несмотря на то, что системный загрузчик игнорирует таблицу секций, линкер все-таки помещает ее копию в исполняемый файл. Это приводит к не значительному расходу места, зато эта информация полезна для отладчиков и дизассемблеров. По не совсем понятным причинам gdb и многие другие программы отказываются загружать в файл с поврежденной или отсутствующей таблицей секций, чем часто пользуются для защиты программ от постороннего вмешательства. Структура файла представлена на рисунке 1.



Рис. 1: Структура ELF-формат с точки зрения линкера (слева) и системного загрузчика операционной системы (справа)

Структура и назначение полей служебных заголовков
Заголовок файла (ELF Header) имеет фиксированное расположение в начале файла и содержит общее описание структуры файла и его основные характеристики, такие как: тип, версия формата, архитектура процессора, виртуальный адрес точки входа, размеры и смещения остальных частей файла.e_ident[] – Массив байт, каждый из которых определяет общую характеристику файла. Первые четыре байта в массиве определяют сигнатуру файла и всегда должны содержать 0x7f 0x45 0x4c 0x46 соответственно.e_type – Тип файла.e_machine – Архитектура аппаратной платформы, для которой файл создан.e_version – Номер версии формата.e_entry Точка входа.e_phoff – Расположение таблицы заголовков программы.e_shoff – Расположение таблицы заголовков разделов.e_flags – Связанные с файлом флаги, зависящие от процессора.

e_ehsize – Размер[5] заголовка файла.e_phentsize – Размер каждого заголовка программы.e_phnum – Число заголовков программы.e_shentsize – Размер каждого заголовка разделов.e_shnum – Число заголовков разделов.e_shstrndx – Индекс записи в таблице разделов, указывающей на таблицу названий разделов.

Процесс загрузки в память
По умолчанию ELF-заголовок проецируется по адресу 8048000h, который прописан в его заголовке. Это и есть базовый адрес загрузки. На стадии линковки он может быть свободно изменен на другой, но большинство программистов оставляют его "как есть". Все сегменты проецируются в память в соответствии с виртуальными адресами, прописанными в таблице сегментов, причем, виртуальная проекция образа всегда непрерывна, и между сегментами не должно быть незаполненных "дыр".Начиная с адреса 40000000h располагаются совместно используемые библиотеки ld-linix.so, libm.so, libc.so и другие, которые связывают операционную систему с прикладной програм- мой. Ближайший аналог из мира Windows – KERENL32.DLL, реализующая win32 API, что расшифровывается как Application Programming Interface, но при желании программа может вызывать функции операционной системы и напрямую. В NT за это отвечает прерывание INT 2Eh, в LINUX как правило INT 80h (на самом деле к текущему моменту в этом вопросе была проделана некоторая оптимизация, о которой будет сказано позже, при рассмотрении вывода утилиты ldd)[3].Для вызова функций типа открытия файла мы можем обратиться либо к библиотеке libc, либо непосредственно к самой операционной системе. Первый вариант – самый громоздкий, самый переносимый, и наименее приметный. Последний прост в реализации, но испытывает проблемы совместимости с различными версиями LINUX’а.Последний гигабайт адресного пространства (от адреса C0000000h и выше) занимают код и данные операционной системе, к которым можно обращаться только посредством прерывания INT 80h или через разделяемые библиотеки.Стек находится в нижних адресах. Он начинается с базового адреса загрузки и "растет вверх"по направлению к нулевым адресам. В большинстве Линукс-систем стек исполняем

(то есть сюда можно скопировать машинный код и передать на него управления), одна- ко, некоторые администраторы устанавливают заплатки, отнимающие у стека атрибут исполнимости. Карта памяти представлена на рисунке 2.
Рис. 2: Карта памяти загруженного образа исполняемого файла

Резидентное приложение – монитор сетевой активности
В качестве полезного приложение было решено создать простую утилиту, которая отобра- жает количество полученных и отправленных пакетов по указанному сетевому интерфейсу. Процесс организации интерфейса с пользователем интереса не представляет, но работа с системой построена по средствам извлечения информации из файла /proc/net/dev и представлена в листинге 1.Листинг 1: Функция получения информации о трафике по сетевому интерфейсу (src/ELF/lin/parse.cpp)7
8


910111213141516171819std : : s t r i n g i n t e r f a c e ( i fname ) ; i n t e r f a c e . append ( " : " ) ;std : : s t r i n g buf f ;std : : i f s tre a m n e ts ta t ( "/ proc / net/ dev" ) ;while ( std : : g e t l i n e ( ne ts tat , buf f ) ) {s i z e _ t s h i f t = buf f . f ind_ f i rst_ not_ of ( ’ ’ i f ( buf f . compare ( s h i f t , i n t e r f a c e . l e ng th ({std : : regex rx (R" ( [ ^ [ : alpha : ] ] [ [ : d i g i t;std : : s re g e x _ i te ra to r pos ( buf f . cbegin (;
20* rx_bytes = std : : s t o l l ( pos−>s t r ( ) ) ;
21++pos ;
22* rx_packets = std : : s t o l l ( pos−>s t r ( ) ) ;
23std : : advance ( pos , 7 ) ;
24* tx_bytes = std : : s t o l l ( pos−>s t r ( ) ) ;
25++pos ;
26* tx_packets = std : : s t o l l ( pos−>s t r ( ) ) ;
27
28re turn true ;
29}
30}
31re turnf a l s e ;
32}

После компиляции можно собрать информацию об объектном файле. Полная демонстрация возможностей objdump займёт довольно много места, но основные возможности представ- лены в следующем листинге 2. В 1-й строке запрашивается информация о хедерах файла, их именах и расположении.
Листинг 2: Демонстрация работы программы objdump
1
2
3
4

5 S e c t i o ns :6 Idx Name S i z e VMA LMA F i l e o f f Algn7 0 . i n te rp 0000001 c 0000000000400238 0000000000400238

ALLOC, LOAD, READONLY, DATA0000000000400254 0000000000400254

ALLOC, LOAD, READONLY, DATA0024 0000000000400274 0000000000400274

ALLOC, LOAD, READONLY, DATA0000000000400298 0000000000400298

ALLOC, LOAD, READONLY, DATA0000000000400350 0000000000400350

ALLOC, LOAD, READONLY, DATA0000000000400 c68 0000000000400 c68

ALLOC, LOAD, READONLY, DATA0000000000401 ad8 0000000000401 ad8

ALLOC, LOAD, READONLY, DATA0000000000401 ba0 0000000000401 ba0

ALLOC, LOAD, READONLY, DATA0000000000401 c90 0000000000401 c90

ALLOC, LOAD, READONLY, DATA0000000000401 e10 0000000000401 e10

ALLOC, LOAD, READONLY, DATA0000000000402500 0000000000402500

ALLOC, LOAD, READONLY, CODE0000000000402520 0000000000402520


303132333400002520 2**4CONTENTS, ALLOC, LOAD, READONLY, CODE12 . p l t . got 00000008 00000000004029 d0 00000000004029 d0000029 d0 2**3CONTENTS, ALLOC, LOAD, READONLY, CODE13 . te xt 00011 b42 00000000004029 e0 00000000004029 e0000029 e0 2**4CONTENTS, ALLOC, LOAD, READONLY, CODE
3514. f i n i 000000090000000000414524 0000000000414524
3600014524 2**2CONTENTS,ALLOC, LOAD, READONLY, CODE
3715. rodata 00000 d330000000000414540 0000000000414540
3800014540 2**5CONTENTS,ALLOC, LOAD, READONLY, DATA
3916. eh_frame_hdr 0000058 c0000000000415274 0000000000415274
4000015274 2**2CONTENTS,ALLOC, LOAD, READONLY, DATA
4117. eh_frame 000027340000000000415800 0000000000415800
4200015800 2**3CONTENTS,ALLOC, LOAD, READONLY, DATA
4318. gcc_except_table 00000875 0000000000417 f 34 0000000000417 f 34
4400017 f 34 2**2CONTENTS,ALLOC, LOAD, READONLY, DATA
4519. i ni t_ array 000000100000000000618 de8 0000000000618 de8
4600018 de8 2**3CONTENTS,ALLOC, LOAD, DATA
4720. f i ni _ array 000000080000000000618 df 8 0000000000618 df 8
4800018 df 8 2**3CONTENTS,ALLOC, LOAD, DATA
4921. j c r 000000080000000000618 e00 0000000000618 e00
5000018 e00 2**3CONTENTS,ALLOC, LOAD, DATA
5122. dynamic 000001 f 00000000000618 e08 0000000000618 e08
5200018 e08 2**3CONTENTS,ALLOC, LOAD, DATA
5323. got 000000080000000000618 f f 8 0000000000618 f f 8
00018 f f 8 2**3


54CONTENTS,ALLOC, LOAD, DATA
5524. got . p l t 000002680000000000619000 0000000000619000
5600019000 2**3CONTENTS,ALLOC, LOAD, DATA
5725. data 000004200000000000619280 0000000000619280
5800019280 2**5CONTENTS,ALLOC, LOAD, DATA
5926. bss 0000064800000000006196 a0 00000000006196 a0
60000196 a0 2**5ALLOC
6127. comment 0000002 f0000000000000000 0000000000000000
62000196 a0 2**0CONTENTS,READONLY
6328. debug_aranges 00000 ab00000000000000000 0000000000000000
64000196 c f 2**0CONTENTS,READONLY, DEBUGGING
6529. debug_info 00082 dbe0000000000000000 00000000000000000001
66a 17 f 2**0CONTENTS,READONLY, DEBUGGING
6730. debug_abbrev 000019 f 80000000000000000 00000000000000000009
68c f 3 d 2**0CONTENTS,READONLY, DEBUGGING
6931. debug_line 00008 cb80000000000000000 00000000000000000009
70e 935 2**0CONTENTS,READONLY, DEBUGGING
7132. debug_str 0007 f 0 c 70000000000000000 0000000000000000000
72a75ed 2**0CONTENTS,READONLY, DEBUGGING
7333. debug_loc 0004 c 7 c 90000000000000000 0000000000000000
74001266 b4 2**0CONTENTS,READONLY, DEBUGGING
7534. debug_ranges 000126700000000000000000 0000000000000000
7600172 e7d 2**0CONTENTS,READONLY, DEBUGGING

Другой удобной программой для вывода информации о ELF файле является readelf (вывод программы приведён в сокращенном виде, листинг 3).

Листинг 3: Демонстрация работы программы readelf

8990ELF Header :Magic : 7 f 45 4 c 46 02 01 01 03 00 00 00 00 00 00 00 00
91Class :ELF64
92Data :2 ’ s complement , l i t t l e endian
93Version :1 ( c urre nt )
94OS/ABI :UNIX − GNU
95ABI Version :0
96Type :EXEC ( Executable f i l e )
97Machine :Advanced Micro Devices X86−64
98Version :0 x1
99Entry point address :0 x402 c 20
100S tart o f program headers :64 ( bytes i nto f i l e )
101S tart o f s e c t i o n headers :1635848 ( bytes i nto f i l e )
102Flags :0 x0
103S i z e o f t h i s header :64 ( bytes )
104S i z e o f program headers :56 ( bytes )
105Number o f program headers :9
106S i z e o f s e c t i o n headers :64 ( bytes )
107Number o f s e c t i o n headers :39
108S e c t i o n header s t r i n g tab l eindex :36
109
110
111Program Headers :
112Type O f f s e tVirtAddr PhysAddr
113F i l e S i zMemSizFlags Align
114PHDR0 x00000000000000400 x00000000004000400
x0000000000400040
1150 x 00000000000001 f 80 x 00000000000001 f 8R E8
116INTERP0 x00000000000002380 x00000000004002380
x0000000000400238
1170 x 000000000000001 c 0 x 000000000000001 c R 1
118[ Requesting program i n t e r p r e t e r : / l i b 6 4 / ld−linux −x86 −64. so . 2 ]
119LOAD0 x0000000000000000 0 x0000000000400000 0
x0000000000400000
1200 x00000000000187 a9 0 x00000000000187 a9 R E
200000


121LOAD 0 x0000000000018 de 8 0 x0000000000618 de 8 0x0000000000618 de 8
1220 x00000000000008 b80 x 0000000000000 f 00RW
200000
123DYNAMIC0 x 0000000000018 e 080 x 0000000000618 e 080
x 0000000000618 e 08
1240 x 00000000000001 f 00 x 00000000000001 f 0RW8
125NOTE0 x00000000000002540 x00000000004002540
x0000000000400254
1260 x00000000000000440 x0000000000000044R4
127GNU_EH_FRAME0 x00000000000152740 x00000000004152740
x0000000000415274
1280 x 000000000000058 c0 x 000000000000058 cR4
129GNU_STACK0 x00000000000000000 x00000000000000000
x0000000000000000
1300 x00000000000000000 x0000000000000000RW10
131GNU_RELRO0 x0000000000018 de 80 x0000000000618 de 80
132133134x0000000000618 de 80 x0000000000000218 0 x0000000000000218 R 1S e c t i o n to Segment mapping :
135Segment S e c t i o ns . . .
13600
13701. i n te rp
13802. i n te rp . note . ABI−ta g . note . gnu . build −id . gnu . hash. dynsym
. dynstr . gnu . v e r s i o n . gnu . version_ r . r e l a . dyn . r e l a . p l t . i n i t. p l t . p l t . got . te xt . f i n i . rodata . eh_frame_hdr . eh_frame . gcc_except_table
13903. i ni t_ array . f i ni _ array . j c r . dynamic . got . got . p l t . data
. bss
14004. dynamic
14105. note . ABI−ta g . note . gnu . build −id
14206. eh_frame_hdr
14307
14408. i ni t_ array . f i ni _ array . j c r . dynamic . got
Информацию о символах можем получить при помощи утилиты ss (нас интересует функция

parse, строка 56, листинг 4).
Листинг 4: Демонстрация работы программы nm
































regex_traitsIcEELb0ELb0ELb0


regex_traitsIcEELb0ELb0ELb1


regex_traitsIcEELb0ELb1ELb0


regex_traitsIcEELb0ELb1ELb1


pT2_EPKcS5_PmS9_

DpT2_EPKcS5_PmS9_


65
66
67
68

Зависимость от библиотек показывает утилита ldd (листинг 5). Тут стоит обратить внима- ние на виртуальную библиотеку в строке 1. В те времена, когда процессоры с архитектурой x86 только появились, взаимодействие пользовательских приложений со службами опера- ционной системы осуществлялось с помощью прерываний. По мере создания более мощных процессоров эта схема взаимодействия становилась узким местом системы. Во всех про- цессорах, начиная с Pentium II, Intel реализовала механизм быстрых системных вызовов (Fast System Call), в котором вместо прерываний используются инструкции SYSENTER и SYSEXIT, ускоряющие выполнение системных вызовов.Библиотека linux-vdso.so.1 является виртуальной библиотекой, или виртуальным динамиче- ски разделяемым объектом (VDSO), который размещается только в адресном пространстве отдельной программы. В более ранних системах эта библиотека называлась linux-gate.so.1. Эта виртуальная библиотека содержит всю необходимую логику, обеспечивающую для пользовательских приложений наиболее быстрый доступ к системным функциям в зави- симости от архитектуры процессора – либо через прерывания, либо (для большинства современных процессоров) через механизм быстрых системных вызовов.
Листинг 5: Демонстрация работы программы ldd
1
2

3

4
5
6

Адрес загрузки программы не меняется, однако адреса подключения динамических биб- лиотек и область размещения стека изменяются при повторном запуске. Отсюда можно сделать вывод о том, что представленные адреса виртуальные.Теперь программу можно запустить для проверки сетевого интерфейса каждые 2 секунды. Работа программы показана на рисунке 3.



Рис. 3: Исполнение программы

Динамические библиотеки .so
Библиотека - это набор скомпонованных особым образом объектных файлов. Библиотеки подключаются к основной программе во время линковки. По способу компоновки биб- лиотеки подразделяют на архивы (статические библиотеки, static libraries) и совместно используемые (динамические библиотеки, shared libraries). В Linux, кроме того, есть меха- низмы динамической подгрузки библиотек. Суть динамической подгрузки состоит в том, что запущенная программа может по собственному усмотрению подключить к себе какую- либо библиотеку. Благодаря этой возможности создаются программы с подключаемыми плагинами, такие как XMMS.Статическая библиотека - это просто архив объектных файлов, который подключается к программе во время линковки. Эффект такой же, как при компиляции файлов отдельно.В отличие от статических библиотек, код совместно используемых (динамических) биб- лиотек не включается в бинарник. Вместо этого в бинарник включается только ссылка на библиотеку.Рассмотрим преимущества и недостатки статических и совместно используемых библиотек. Статические библиотеки делают программу более автономной: программа, скомпонован- ная со статической библиотекой может запускаться на любом компьютере, не требуя наличия этой библиотеки (она уже "внутри"бинарника). Программа, скомпонованная с динамической библиотекой, требует наличия этой библиотеки на том компьютере, где она запускается, поскольку в бинарнике не код, а ссылка на код библиотеки. Не смотря на такую зависимость, динамические библиотеки обладают двумя существенными преиму- ществами. Во-первых, бинарник, скомпонованный с совместно используемой библиотекой меньше размером, чем такой же бинарник, с подключенной к нему статической библиотекой (статически скомпонованный бинарник). Во-вторых, любая модернизация динамической

библиотеки, отражается на всех программах, использующих ее. Таким образом, если неко- торую библиотеку foo используют 10 программ, то исправление какой-нибудь ошибки в foo или любое другое улучшение библиотеки автоматически улучшает все программы, которые используют эту библиотеку. Именно поэтому динамические библиотеки называют совмест- но используемыми. Чтобы применить изменения, внесенные в статическую библиотеку, нужно пересобрать все 10 программ.В Linux статические библиотеки обычно имеют расширение .a (Archive), а совместно используемые библиотеки имеют расширение .so (Shared Object). Хранятся библиотеки, как правило, в каталогах /lib и /usr/lib. В случае иного расположения (относится только к совместно используемым библиотекам), приходится явно указать путь, чтобы программа запустилась[2].

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



Теперь простой запуск приложения приведёт к ошибке, т.к. система ожидает наличия файла библиотеки в строго определённом месте.
user@host$ ./netmonitor./netmonitor: error while loading shared libraries: libparse.so: cannot open shared user@host$
Отсутствие библиотеки можно легко обнаружить при запуске утилиты ldd (строка 2, листинг 6).Листинг 6: Демонстрация работы программы ldd для приложения, использующего дина- мическую библиотеку1
2
3


4
5


6
7


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

Процесс загрузки приложений Windows
Выполнение ЕХЕ-модуля
При запуске ЕХЕ-файла (сокр. англ. executable — исполнимый) загрузчик операционной системы создает для его процесса виртуальное адресное пространство и проецирует на него исполняемый модуль. Далее загрузчик анализирует раздел импорта и пытается спроецировать все необходимые DLL на адресное пространство процесса.Поскольку в разделе импорта указано только имя DLL (без пути), загрузчику приходит- ся самому искать ее на дисковых устройствах в компьютере пользователя. Поиск DLL осуществляется в следующей последовательности.Каталог, содержащий ЕХЕ-файл.Текущий каталог процесса.Системный каталог WindowsОсновной каталог WindowsКаталоги, указанные в переменной окружения PATH.Проецируя DLL-модули на адресное пространство, загрузчик проверяет в каждом из них раздел импорта. Если у DLL есть раздел импорта (что обычно бывает), загрузчик проецирует следующий DLL-модуль. При этом загрузчик ведет учет загружаемых DLL и проецирует их только один раз, даже если загрузки этих DLL требуют и другие модули.Если найти файл DLL не удается, загрузчик выводит сообщение об ошибке.
Найдя и спроецировав на адресное пространство процесса все необходимые DLL-модули, загрузчик настраивает ссылки на импортируемые идентификаторы. Для этого он вновь просматривает разделы импорта в каждом модуле, проверяя наличие указанного иденти- фикатора в соответствующей DLL.Если идентификатор не найден, то это заканчивается выводом сообщения об ошибке, если же идентификатор найден, загрузчик отыскивает его RVA и прибавляет к виртуальному адресу, по которому данная DLL размещена в адресном пространстве процесса, а затем сохраняет полученный виртуальный адрес в разделе импорта EXE-модуля. И с этого момента ссылка в коде на импортируемый идентификатор приводит к выборке его адреса из раздела импорта вызывающего модуля, открывая таким образом доступ к импортируемой переменной, функции или функции-члену C++ класса. После этого динамические связи установлены, первичный поток процесса начал выполняться, и приложение начинает

работать[4].
Загрузка всех DLL и настройка ссылок занимает какое-то время, но, поскольку такие операции выполняются лишь при запуске процесса, на производительности приложения это не сказывается Тем не менее, для многих программ подобная задержка при инициализации неприемлема. Чтобы сократить время загрузки приложения, можно модифицировать базовые адреса EXE- и DLL-модулей и провести их (модулей) связывание.

Динамически подключаемые библиотеки
Динамические библиотеки (dynamic-link libraries, DLL) – краеугольный камень операцион- ной системы Windows, начиная с самой первой ec версии. В DLL содержатся все функции Windows API. Три самые важные DLL:Kernel32.dll – управление памятью, процессами и потокамиUser32.dll – поддержка пользовательского интерфейса, в том числе функции, связан- ные с созданием окон и передачей сообщенийGDI32.dll – графика и вывод текста.В Windows есть и другие DLL, функции которых предназначены для более специализи- рованных задач. Например, в AdvAPI32.dll содержатся функции для защиты объектов, работы с реестром и регистрации событий, в ComDlg32.dll – стандартные диалоговые окна (вроде File Open и File Save), a ComCrl32.dll поддерживает стандартные элементы управления.Вот основные причины, по которым инструмент DLL получил такую популярность:
Расширение функциональности приложения. DLL можно загружать в адресное пространство процесса динамически, что позволяет приложению, определив, какие действия от него требуются, подгружать нужный код. Поэтому одна компания, создав какое-то приложение, может предусмотреть расширение его функциональности за счет DLL от других компаний.Возможность использования разных языков программирования. У разра- ботчика есть выбор, на каком языке писать ту или иную часть приложения. Поль- зовательский интерфейс приложения можно создавать на Microsoft Visual Basic, а прикладную реализовать на С++. Программа на Visual Basic может загружать DLL, написанные на С++, Коболе, Фортране и др.Более простое управление проектом. Если в процессе разработки программного

продукта отдельные его модули создаются разными группами, то при использовании DLL таким проектом управлять гораздо проще. Однако конечная версия приложения должна включать как можно меньше файлов.Экономия памяти. Если одну и ту же DLL использует несколько приложений, в оперативной памяти может храниться только один ее экземпляр, доступный этим приложениям. Пример DLL-версия библиотеки С/С++. Эту библиотеку используют многие приложения. Если всех их скомпоновать со статически подключаемой версией этой библиотеки, то код таких функций, как sprintf, strcpy, malloc и др., будет многократно дублироваться в памяти. Но если они компонуются с DLL-версией библиотеки С/С++, в памяти будет присутствовать лишь одна копия кода этих функций, что позволит гораздо эффективнее использовать оперативную память.Разделение ресурсов. DLL могут содержать такие ресурсы, как шаблоны диалого- вых окон, строки, значки и битовые карты (растровые изображения). Эти ресурсы доступны любым программам.Упрощение локализации. DLL нередко применяются для локализации приложе- ний. Например, приложение, содержащее только код без всяких компонентов поль- зовательского интерфейса, может загружать DLL с компонентами локализованного интерфейсаРешение проблем, связанных с особенностями различных платформ. В разных версиях Windows содержатся разные наборы функций. Зачастую разработчикам нужны новые функции, существующие в той версии системы, которой они пользуются. Если Windows пользователя не поддерживает эти функции, ему не удастся запустить такое приложение: загрузчик попросту откажется его запускать. Но если эти функции будут находиться в отдельной DLL, можно будет загрузить программу даже в более ранних версиях Windows.Реализация специфических возможностей. Определенная функциональность в Windows доступна только при использовании DLL Например, отдельные виды ловушек (устанавливаемых вызовом SetWindowsHookEx и SetWinEventHook) можно задействовать при том условии, что функция уведомления ловушки размещена в DLL. Кроме того, расширение функциональности оболочки Windows возможно лишь за счет создания СОМ-объектов, существование которых допустимо только в DLL. Это же относится и к загружаемым Web-браузером ActiveX-элементам, позволяющим создавать Web-страницы с более богатой функциональностью.Зачастую создать DLL проще, чем приложение, потому что она является лишь набором автономных функций, пригодных для использования любой программой, причем в DLL

обычно нет кода, предназначенного для обработки циклов выборки сообщений или создания окон. DLL представляет собой набор модулей исходного кода, в каждом из которых содер- жится определенное число функций, вызываемых приложением (исполняемым файлом) или другими DLL. Файлы с исходным кодом компилируются и компонуются так же, как и при создании ЕХЕ-файла. Но, создавая DLL, нужно указывать компоновщику ключ /DLL. Тогда компоновщик записывает в конечный файл информацию, по которой загрузчик операционной системы определяет, что данный файл DLL, а не приложениеЧтобы приложение (или другая DLL) могло вызывать функции, содержащиеся в DLL, образ ее файла нужно сначала спроецировать на адресное пространство вызывающего процесса. Это достигается либо за счёт неявного связывания при загрузке, либо за счет явного – в период выполнения.Как только DLL спроецирована на адресное пространство вызывающего процесса, ее функции доступны всем потокам этого процесса. Фактически библиотеки при этом теряют почти всю индивидуальность: для потоков код и данные DLL – просто дополнительные код и данные, оказавшиеся в адресном пространстве процесса. Когда поток вызывает из DLL какую-то функцию, та считывает свои параметры из стека потока и размещает в этом стеке собственные локальные переменные. Кроме того, любые созданные кодом DLL объекты принадлежат вызывающему потоку или процессу – DLL ничем пе владеет.Например, если DLL-функция вызывает VirtualAlloc, резервируется регион в адресном пространстве того процесса, которому принадлежит поток, обратившийся к DLL-функции. Если DLL будет выгружена из адресного пространства процесса, зарезервированный регион не освободится, так как система не фиксирует того, что регион зарезервирован DLL-функций. Считается, что он принадлежит процессу и поэтому освободится, только если поток этого процесса вызовет VirtualFree или завершится сам процесс.

Реализация резидентного приложения
Как и в случае с Linux, приложение отображает количество переданной и полученной информации сетевым интерфейсом. Тут интересно, что используемую в процессе функцию мы получаем непосредственно во время работы (стр. 20 - 22, листинг 7).Листинг 7: Утилита сбора информации о трафике на сетевых интерфейсах (src/ELF/win/main.cpp)1
2
3

4
5 bool Get I f Table (PMIB_IFTABLE *m_pTable) ;
6
7 i n t main ( i n t argc , char * argv [ ] ) {8 PMIB_IFTABLE m_pTable = NULL;
9
10 i f ( Get I f Table(&m_pTable) == f a l s e ) {11 re turn 1 ;12 }
13
14 // Обход списка с е те вых интерфейс ов15 f o r (UINT i = 0 ; i < m_pTable>dwNumEntries ; i ++) {16 MIB_IFROW Row = m_pTable>t ab l e [ i ] ;17 char sz Descr [ MAXLEN_IFDESCR ] ;18 memcpy( sz Descr , Row. bDescr , Row. dwDescrLen ) ;19 sz Descr [ Row. dwDescrLen ] = 0 ;
20
21 // Выв од с обранной информации22 std : : cout << sz Descr << " : " << std : : endl ;23 std : : cout << "\ t Received : " << Row. dw InOctets24 << " , Sent : " << Row. dwOutOctets << std : : endl ;25 std : : cout << std : : endl ;26 }
27
28 // Завершение работы29 d e l e te ( m_pTable) ;30 char a = getchar ( ) ;31 re turn 0 ;32 }
33
34 bool Get I f Table (PMIB_IFTABLE *m_pTable) {35 // Тип указателя на функцию Get I f Table36 typedef DWORD( _ s tdc a l l * TGetIf Table ) (37 MIB_IFTABLE * p If Table , // Буфер таблицы интерфейс ов38 ULONG * pdwSize , // Размер буфера39 BOOL bOrder ) ; // Сортировать таблицу?
40


414243444546474849505152535455565758596061// Пыта емся HINSTANCE i pi p hl pap i = L i f ( ! i ph l pap std : : c e r rre turn f a l}// Получаем TGetIf Table p GetIf Table// Получили DWORD m_dw p GetIf Table (*m_pTable =i f ( p GetIf Ta{std : : c e r r d e l e te *m re turn f a l
62}
63
64re turn true ;
65}

Результат выполнения программы представлен на рисунке 4.
Заполнение таблицы с информацией об интерфейсах (стр. 37, листинг 7) вынесена в отдельную функцию (стр.34, листинг 7). В приложении производится большое количество обращений к различным DLL. Для упрощения дальнейшего анализа вынесем функцию в отдельную динамическую библиотеку (myDllLib) и продолжим анализ.



Рис. 4: Исполнение программы в среде Windows

Анализ исполнения приложения
Утилита API Monitor может декодировать параметры функций и возвращаемые значе- ния, чтобы представить их в понятном формате, а также отобразить точки обращения разработанной динамической myDllLib.dll



Отладчик OllyDbg позволяет осуществить разбор пользовательского режима (ring 3). При запуске программы, изначальной точкой входа является функция _tmainCRTStartup. Ниже приведена команда вызова данной функции:
CPU Disasm Address Hex dump Command Comments01271BA3 |. E8 ADF4FFFF CALL 01271055; [ security_init_cookie 01271BA8 |. E8 73FCFFFF CALL tmainCRTStartup; [ tmainCRTStartup
Сама функция _tmainCRTStartup находится по адресу 012F1820:
Адрес начала подключения динамической библиотеки находится по адресу 012F1820:


012F182F|.64:A1 0000000MOV EAX,DWORD PTR FS:[0]
012F1835|.50PUSH EAX
012F1836|.83C4 E4ADD ESP,-1C
012F1839|.53PUSH EBX
012F183A|.56PUSH ESI
012F183B|.57PUSH EDI
012F183C|.A1 24802F01MOV EAX,DWORD PTR DS:[ security_cookie]
012F1841|.3145 F8XOR DWORD PTR SS:[EBP-8],EAX
012F1844|.33C5XOR EAX,EBP
012F1846|.50PUSH EAX
012F1847|.8D45 F0LEA EAX,[EBP-10]
012F184A|.64:A3 0000000MOV DWORD PTR FS:[0],EAX
012F1850|.8965 E8MOV DWORD PTR SS:[EBP-18],ESP
012F1853|.C745 FC 00000MOV DWORD PTR SS:[EBP-4],0
012F185A|.C745 E4 00000MOV DWORD PTR SS:[EBP-1C],0

Адреса точки входа и подключения библиотек не изменились.
Process Monitor является усовершенствованным инструментом отслеживания для Windows, который в режиме реального времени отображает активность файловой системы, реестра, а также процессов и потоков. Проверим адреса подключения динамической библиотеки при помощи утилиты Process Monitor.

Ended: 22.03.2016 2:04:46Modules:myDllTest.exe 0x250000 0x1c000 C:\Projects\myDllLib\Debu 22.03.2016 0:45:34MSVCR120D.dll 0x72e10000 0x1bf000 C:\Windows\SysWOW64\MS Microsoft Corporation 12.00.21005.1 built by: REL 05.10.2013 5: myDllLib.dll 0x73be0000 0x1b000 C:\Projects\myDllLib\Deb 22.03.2016 0:45:33wow64win.dll 0x74b40000 0x5c000 C:\Windows\SYSTEM32\wow6 Microsoft Corporation 6.1.7601.19018 (win7sp1_gdr.150928-1507) wow64.dll 0x74ba0000 0x3f000 C:\Windows\SYSTEM32\wow64.d Microsoft Corporation 6.1.7601.19018(win7sp1_gdr.150928-1507) 29.09.2015 6:12:08wow64cpu.dll 0x74c10000 0x8000 C:\Windows\SYSTEM32\wow64 Corporation 6.1.7601.19018 (win7sp1_gdr.150928-1507)29.09.2015 6:12:09KERNELBASE.dll 0x75220000 0x47000 C:\Windows\syswow64\KE Corporation 6.1.7601.18015 (win7sp1_gdr.121129-1432)29.09.2015 6:00:36kernel32.dll 0x75350000 0x110000 C:\Windows\syswow64\ker Corporation 6.1.7601.18015 (win7sp1_gdr.121129-1432)29.09.2015 6:00:35<