Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги хакеры / Защита_от_взлома_сокеты,_эксплойты,_shell_код_Фостер_Дж_

.pdf
Скачиваний:
14
Добавлен:
19.04.2024
Размер:
3.68 Mб
Скачать

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

Часто задаваемые вопросы 641

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

 

w Click

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

 

 

 

 

 

 

 

 

 

p

 

 

 

 

 

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

 

Часто задаваемые вопросы

 

 

 

 

-x cha

 

 

 

 

 

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Следующие часто задаваемые вопросы, на которые отвечают авторы книги, призваны помочь вам оценить, насколько хорошо вы поняли идеи, изложенные в данной главе, и возможные их применения на практике. Если вы хотите задать авторам вопрос, зайдите на страницу www.syngress.com/solutions и заполните форму Ask the Author. Заодно вы получите доступ к тысячам других FAQов на сайте ITFAQnet.com.

Â: Надо ли знать, как писать shell-код, для того чтобы пользоваться каркасом Metasploit?

Î: Нет. Пользуясь Web-интерфейсом msfweb или утилитами msfpayload è msfencode, разработчик может полностью забыть о написании shell-кода, ограничившись лишь копированием готовых фрагментов в свой эксплойт. Если эксплойт разрабатывается в каркасе Metasploit, то его автор может никогда и не увидеть полезную нагрузку.

Â: Нужно ли обязательно кодировать полезную нагрузку?

Î: Нет. Если полезная нагрузка не содержит недопустимых символов, то ее можно послать и в незакодированном виде. Основная задача кодировщиков – устранить недопустимые символы.

Â: Обязательно ли использовать генератор NOP-команд при встраивании эксплойта к каркас?

Î: Нет. Можете присвоить значение 0 параметрам MaxNops è MinNops â õýøå Payload внутри хэша %info. Тогда каркас не станет автоматически добавлять NOP-команды в полезную нагрузку. Можно вместо этого переопределить методы PayloadMinOps è PayloadMinOps так, чтобы они не возвращали NOP-команд.

Â: Я вычислил смещение, подобрал адрес возврата, определил недопустимые символы и максимальный размер, успешно сгенерировал и закодировал полезную нагрузку. Но по какой-то причине отладчик перехватывает управление процессом где-то в середине исполнения shell-кода. Я точно не знаю, что происходит, но складывается впечатление, что полезная нагрузка изменена. Мне казалось, что я выявил все недопустимые символы.

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

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

i

 

 

F

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

t

 

 

 

 

 

 

 

 

 

t

 

P

D

 

 

 

 

 

 

 

 

o

P

D

 

 

 

 

 

 

 

 

o

 

 

 

 

NOW!

r

 

 

 

 

NOW!

r

 

 

 

 

 

BUY

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

642 Глава 12. Написание эксплойтов III

 

 

 

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

m

w Click

 

 

 

 

 

 

o

w Click

 

 

 

 

 

 

o

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

.

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

.c

 

 

 

p

df

 

 

 

 

e

 

 

 

p

df

 

 

 

 

e

 

 

 

 

 

 

g

 

 

 

 

 

 

 

 

g

 

 

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

-xcha

 

 

вызываться, и вы ничего не можете с этим поделать. Попробуйте изменить-x cha

 

 

 

 

 

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

Â: Всякий раз, как я пытаюсь определить смещение, посылая длинную строку, отладчик слишком рано перехватывает управление и твердит что-то о неправильном адресе в памяти.

Î: Скорее всего, какая-то функция читает значение из стека, предполагая, что там должен находиться корректный адрес и пытается перейти по нему. Посмотрите в окно дизассемблера, это должно помочь вам разобраться, какая команда приводит к ошибке. Если привлечь еще окно содержимого памяти, то вы сможете изменить «плохие» байты в строке атаки, так чтобы они указывали на правильный адрес.

Â: Чтобы проверить, ведет ли адрес возврата на мою полезную нагрузку, я послал строку букв ‘а’. Я полагал, что EIP должен указывать на одну из этих букв, а поскольку ‘a’ – это не корректная команда, то исполнение должно прекратиться. Так я и собирался проверить, что EIP указывает, куда нужно. Но ничего подобного не происходит. Когда процесс останавливается, состояние процесса оказывается совсем не таким, как я ожидал.

Î: Ошибка состоит в предположении, будто символ ‘a’ обязательно вызовет останов из-за несуществующей команды. Если записать в адрес возврата четыре символа ‘a’, то исполнение может и прерваться, поскольку адрес 0x61616161 не обязательно принадлежит программе. Но на 32-разрядном процессоре x86 символ ‘a’ с кодом 0x61 интерпретируется как однобайтовая команда POPAD, которая последовательно извлекает из стека 32-разрядные значения и загружает их в регистры EDI, ESI, EBP, в «никуда» (вместо загрузки в ESP), EBX, EDX, ECX и EAX. Если EIP указывает на ячейку, содержащую букву ‘a’, то он выполняет команду POPAD, что приводит к многократному извлечению из стека и полностью изменяет текущее состояние процесса. В частности, останов происходит совсем не там, где вы ожидали. Чтобы корректно проверить, что EIP указывает внутрь полезной нагрузки, лучше послать строку, состоящую из байтов 0xCC, которые интерпретируются как команда INT 3 останова в контрольной точке.

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

Глава 13

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Написание компонентов для задач, связанных с безопасностью

Описание данной главы:

Модель СОМ

Библиотека ATL

Добавление СОМ расширений в программу RPCDUMP Cм. также главу 14

Резюме

Обзор изложенного материала

Часто задаваемые вопросы

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

644 Глава 13. Написание компонентов для задач, связанных с безопасностью

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

 

e

 

 

 

 

 

 

n

Введение

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

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

Технология повторного использования кода определяется тем, как он написан. Лучше всего, если нужный код замкнут и легко включается в ваш проект (как, скажем, класс на C++ или DLL-библиотека). Однако так бывает далеко не всегда, поэтому часто приходится погружать код в автономный модуль.

Тип такого модуля-контейнера зависит от характера проекта. Чаще всего в этом качестве выступает класс на языке C++ или динамически загружаемая библиотека. Но что если требования изменятся или новая программа разрабатывается на другом языке? Снова выполняется процедура интеграции или код переписывается.

В этой главе мы рассмотрим другой вид интеграции, который позволяет обращаться к некоторому коду из программы на другом языке и даже с другой машины. Это «модель компонентных объектов» или COM (Component Object Model). Вы узнаете, что такое COM, как реализовать COM-компонент с помощью библиотеки Active Template Library (ATL) и как интегрировать этот компонент в существующую программу, относящуюся к информационной безопасности.

Модель COM

Уверенное владение теорией, лежащей в основе модели COM, необходимо для разработки приложений, основанных на этой технологии. Но это не тема для вводного обзора. Есть немало других хороших книг по этому предмету, например, «Inside COM» Дейла Роджерсона (Microsoft Press, 1996)1. Мы же ставим себе целью дать рабочие знания о наиболее часто используемых сторонах технологии COM, с которыми вам придется столкнуться.

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

1 Имеется русский перевод – Дейл Роджерсон «Основы COM» (Microsoft Press, Русская редакция; 1997).

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

 

 

 

 

 

e

 

 

 

 

df

 

COM3объекты

 

 

 

 

 

n

 

 

 

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

Модель COM 645

 

to

 

 

 

 

 

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

COM-объект похож на любой другой объект в том смысле, что обладает методами, свойствами и внутренним состоянием. Но, в отличие от других объектных технологий, доступ к методам и свойствам, осуществляется через интерфейсы. У COM-объекта может быть много интерфейсов, но все они являются производными от IUnknown (см. ниже). Чтобы получить указатель на интерфейс объекта, нужно его запросить. Если быть точным, интерфейс запрашивается у среды исполнения COM во время загрузки объекта.

COM3интерфейсы

Спецификация COM требует, чтобы любой объект поддерживал интерфейс IUnknown и следовал соглашениям о вызове.

Интерфейс IUnknown

Первыми тремя методами любого COM-интерфейса должны быть методы, унаследованные от IUnknown: QueryInterface, AddRef и Release. Метод QueryInterface позволяет спросить у объекта, поддерживает ли он некий интерфейс. Если да, то возвращается указатель на этот интерфейс, в противном случае – код ошибки.

Для управления временем жизни COM-объектов применяется подсчет ссылок, этой цели служат методы AddRef и Release. Как явствует из названий, AddRef увеличивает счетчик ссылок на единицу, а Release уменьшает его. Если некоторая функция возвращает указатель на интерфейс в своем выходном параметре, то счетчик ссылок на него необходимо увеличить. Когда работа с интерфейсом закончена, клиент должен вызвать Release.

Соглашение о вызове

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

int __stdcall MyFunction()

{

return 10;

}

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

646 Глава 13. Написание компонентов для задач, связанных с безопасностью

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

 

 

 

 

 

e

 

 

 

 

df

 

 

n

 

Среда исполнения COM

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Именно среда исполнения выполняет действия, необходимые для того, чтобы клиент мог обратиться к COM-объекту. Но предварительно ее следует инициализировать. Для инициализации вызывается функция CoInitialize или CoInitializeEx. Разница между ними в том, что CoInitializeEx позволяет указать потоковую модель. Проще говоря, среда исполнения COM гарантирует, что любой доступ клиента к объекту и наоборот не будет противоречить выбранной потоковой модели. Если заданы несовместимые условия, то среда исполнения постарается исправить ситуацию, загрузив модуль-заместитель. Это процесс прозрачен для клиента, и в однопоточных программах на него можно не обращать внимания. По завершении работы нужно вызвать функцию CoUninitialize без аргументов, чтобы освободить занятые средой исполнения ресурсы.

Когда среда исполнения загружена, клиент может запросить интерфейс у любого объекта, зарегистрированного в локальной или удаленной системе. В первом случае это делает функция CoCreateInstance, во втором – CoCreateInstanceEx. Прототип функции CoCreateInstance следующий:

STDAPI CoCreateInstance( REFCLSID rclsid,

LPUNKNOWN pUnkOuter, DWORD dwClsContext,

REFIID riid, LPVOID * ppv

);

Наибольший интерес представляют параметры rclsid, dwClsContext, riid è ppv. Параметр pUnkOuter применяется для агрегирования, мы о нем говорить не будем. Параметр rclsid определяет, какой объект должен быть загружен. Флаг dwClsContext говорит, куда следует загружать объект: в клиентский процесс или в отдельный.

Среда исполнения COM опознает COM-объекты по глобально уникальному идентификатору (GUID). В качестве синонимов аббревиатуры GUID употребляются также CLSID и IID. Выше было показано, что функция CoCreateInstance принимает аргумент типа REFCLSID, идентифицирующий объект. Кроме того, она принимает еще идентификатор IID того интерфейса, который нужно запросить у объекта после того, как он будет загружен. Откуда берутся эти величины, мы узнаем позже, когда будет говорить о регистрации COM-объектов.

Ниже приведен пример инициализации COM, создания COM-объекта и завершения.

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

X

 

 

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

 

 

F

 

 

 

 

 

 

 

 

t

 

 

 

D

 

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

 

to

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

o

 

 

 

.

 

 

 

 

 

g

.c

 

 

 

 

p

 

 

 

 

 

 

 

 

main()

 

 

 

 

f-xchvoida

 

 

 

 

d

 

 

 

n

e

 

 

 

 

 

 

 

 

 

 

 

 

 

{

HRESULT hr; IXMLDOMDocument *pDoc = 0;

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

Модель COM 647

 

to

 

 

 

 

 

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

//Инициализировать COM CoInitialize(0);

//Инициализировать экземпляр анализатора MSXML, для которого

//CLSID равен CLSID_DOMDocument

hr = CoCreateInstance( CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (PVOID*)&pDoc);

if (SUCCEEDED(hr) && pDoc)

{

// Сделать что-то с указателем на интерфейс pDoc pDoc->Release();

}

// Завершить работу с COM CoUninitialize();

}

Реализация COM3объекта

Клиент обращается ко всем COM-объектам по единой схеме, не зависящей от реализации самого объекта. Но это вовсе не означает, что все объекты реализуются одинаково. Например, среда исполнения COM поддерживает внутрипроцессные (типа DLL) и внепроцессные (исполняемый файл) объекты.

Если выбрана внутрипроцессная модель, то среда исполнения ожидает, что содержащий COM-объект модуль (DLL) реализует определенную функциональность, в частности, экспортирует некоторые функции.

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

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

Регистрация COM-объекта

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

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

i

 

 

 

F

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

t

 

 

 

 

 

 

 

 

 

 

t

 

P

D

 

 

 

 

 

 

 

 

o

 

P

D

 

 

 

 

 

 

 

 

o

 

 

 

 

NOW!

r

 

 

 

 

 

NOW!

r

 

 

 

 

 

BUY

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

648 Глава 13. Написание компонентов для задач, связанных с безопасностью

 

 

 

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

m

 

w

 

 

 

 

 

 

 

 

 

m

w Click

 

 

 

 

 

 

o

 

w Click

 

 

 

 

 

 

o

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

.

 

 

 

 

 

 

.c

 

 

 

.

 

 

 

 

 

 

.c

 

 

 

p

df

 

 

 

 

e

 

 

 

 

p

df

 

 

 

 

e

 

 

 

 

 

 

g

 

 

 

 

 

 

 

 

 

g

 

 

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

-xcha

 

 

 

 

 

Если COM-объект представляет собой внутрипроцессную DLL, то его-x cha

 

 

 

 

 

можно зарегистрировать вручную с помощью утилиты RegSvr32. Если же это внепроцессный исполняемый файл, то для регистрации вручную он обычно вызывается с флагом /regserver. Как бы то ни было, действия, выполняемые в ходе регистрации, имеют огромное значение для реализации COM-объектов.

Выше уже упоминалось, что COM-объект идентифицируется глобально уникальным значением CLSID. Очевидно, что среда исполнения COM должна знать этот идентификатор, чтобы связать с ним объект. Тут-то и приходит на помощь регистрация.

Основное хранилище регистрационной информации о компонентах – это, естественно, реестр Windows. Реестр представляет собой единую базу конфигурационных данных, управляемую операционной системой. Это иерархическое хранилище, организованное в виде дерева. Внутри реестра есть несколько отдельных баз данных или «ульев» (hives). Обычно их именуют по названиям ключей верхнего уровня: HKEY_LOCAL_MACHINE, HKEY_CLASSES_ROOT и так далее. Данные хранятся в реестре в виде пар «имя / значение». Все имена – это строки, значения же могут быть строками, целыми числами типа DWORD или двоичными данными. Для просмотра и изменения реестра служит утилита RegEdit (рис. 13.1).

Рис. 13.1. Использование RegEdit для просмотра реестра Windows

Относящаяся к COM информация хранится в улье HKEY_CLASSES_ROOT. Ниже описывается та часть структуры этого улья, которая имеет отношение к регистрации.

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

F

 

 

 

 

 

 

t

 

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

HKEY_CLASSES_ROOT\CLSID

 

.

 

 

 

 

 

.c

 

 

 

p

 

-xchÊëþ÷a

 

 

 

 

 

 

g

 

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

Модель COM 649

 

to

 

 

 

 

 

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Ключ реестра HKEY_CLASSES_ROOT\CLSID – это контейнер для всех зарегистрированных в системе компонентов, индексированный значением CLSID. Любой компонент обязан зарегистрировать свой CLSID под этим ключом.

Êëþ÷ HKEY_CLASSES_ROOT\CLSID\ {xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}

Наличие ключа CLSID внутри HKEY_CLASSES_ROOT\CLSID означает, что COM-объект зарегистрирован. Значением этого ключа по умолчанию является понятное человеку имя компонента, например, Msxml. Этот ключ должен иметь потомка, описывающего возможные способы создания, к примеру, InprocServer32 èëè LocalServer32. Как следует из названий, InprocServer32

означает, что объект загружается в адресное пространство клиента, а LocalServer32 – что он будет создан как отдельный процесс.

Êëþ÷ InprocServer32

У ключа InprocServer32 есть несколько элементов-потомков, которые сообщают среде исполнения COM, как следует загружать объект. Значением по умолчанию является физический путь к объекту в файловой системе.

Значение, связанное с именем ThreadingModel, говорит, какая потоковая модель поддерживается данным объектом. Возможны, в частности, следующие модели: Apartment (раздельная), Free (свободная) и Both (обоюдная).

Еще один потомок этого ключа называется ProgID, его значением по умолчанию является текстовое имя, по которому можно обращаться к компоненту.

Êëþ÷ LocalServer32

Êàê è InprocServer32, êëþ÷ LocalServer32 сообщает среде исполнения COM, как загружать объекты из конкретного сервера, но в данном случае, сервер представляет собой исполняемый файл.

Значением по умолчанию является путь к файлу, который запускается

âпроцессе загрузки объекта.

Èу этого ключа есть потомок ProgID, содержащий текстовое имя компонента.

Реализация внутрипроцессного сервера

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

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

650 Глава 13. Написание компонентов для задач, связанных с безопасностью

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

 

 

 

 

 

 

 

m

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

-xcha

 

 

.c

 

 

.

 

 

 

 

 

 

.c

 

 

 

p

 

 

 

цессный модуль экспортировал четыре функции, вызываемые средой испол--x cha

g

 

 

 

 

 

 

 

 

 

g

 

 

 

 

 

p

 

 

 

 

 

 

 

 

 

 

df

 

 

n

e

 

 

 

 

df

 

 

n

e

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

нения, а именно: DllGetClassObject, DllCanUnloadNow, DllRegisterServer и DllUnregisterServer. Назначение этих функций обсуждается ниже.

Функция DllGetClassObject

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

STDAPI DllGetClassObject( REFCLSID rclsid, REFIID riid,

LPVOID * ppv

);

Параметр rclsid определяет компонент, который нужно создать. Параметр же riid задает интерфейс не этого компонента, а фабрики класса, то есть IClassFactory или IClassFactory2. Ну а в параметре ppv возвращается указатель на затребованную фабрику класса.

Клиент может применить фабрику класса для создания компонента с помощью функции CoCreateInstance. Но это редко бывает необходимо, поскольку среда исполнения COM сама обрабатывает все стандартные случаи.

Стандартные COM-объекты, совместимые со средой исполнения, должны реализовывать интерфейс IClassFactory (иначе вместо CoCreateInstance им придется вызывать функцию CoGetClassObject).

Функция DllCanUnloadNow

Эта функция сообщает, используется в данный момент DLL или нет. Слово «используется» в этом контексте может означать как то, что с ней происходит интерактивное взаимодействие, так и просто тот факт, что к хранящимся в ней объектам сейчас осуществляется доступ со стороны клиентов. Прототип DllCanUnloadNow следующий:

STDAPI DllCanUnloadNow(void);

Если DLL можно выгрузить, функция возвращает S_OK, в противном слу- чае S_FALSE.

Функция DllRegisterServer

Эта функция отвечает за авторегистрацию модуля в системе. Регистрируется каждый COM-объект, предоставляемый модулем, а также дополнительная информация, к примеру, библиотека типов объекта (TypeLib).