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

книги хакеры / Защита_от_взлома_сокеты,_эксплойты,_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

 

 

 

 

 

 

Библиотека ATL 671

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w Click

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

 

 

 

 

 

 

 

 

p

 

-x cha

 

 

 

 

 

 

 

-xchåêòûa

без дополнительных знаний. Но вы, конечно, понимаете, что разработ-

 

 

e

 

 

 

 

df

 

 

n

e

 

 

 

 

df

 

 

n

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ка COM-сервера – это не простая задача. Тут-то и приходит на помощь новая возможность, появившаяся в Visual C++.NET, – атрибуты C++.

Назначение атрибута C++ – автоматически вставить в ваш исходный текст некоторый код, решающий конкретную задачу. Атрибуты C++ реализуются провайдерами атрибутов, и таким провайдером в случае ATL служит библиотека atlprov.dll.

Применение атрибутов ATL намного сокращает объем кода, который надо написать для получения компонента. В частности, вам уже не нужно возиться со сценариями реестра и определением интерфейса на языке IDL. Кроме того, провайдер атрибутов включает поддержку для написания ATL-сервера, клиента OLE DB, программирования показателей производительности и Web-сервисов.

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

Чтобы начать пользоваться атрибутами ATL в своей программе, необходимо определить константу _ATL_ATTRIBUTES перед заголовочным файлом atlbase.h. В результате файл atlbase.h включает дополнительный файл atlplus.h, который и содержит необходимые для работы с атрибутами объявления.

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

1 #include <windows.h>

2

3 #define _ATL_ATTRIBUTES

4 #define _ATL_APARTMENT_THREADED

5

6 #include <atlbase.h>

7 #include <atlcom.h>

8

9 [module(type=dll, name="HotFixChecker")];

10

11[object, uuid("EAA203CA-24D4-4C49-9A76-1327068987D8")]

12__interface IHotFixChecker

13{

14HRESULT IsHotFixInstalled([in] BSTR bstrQNumber,

15[out, retval] VARIANT_BOOL *pbInstalled);

16};

17

18[coclass

19uuid("FC9CBC60-4648-4E66-9409-610AD30689C7"),

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

X

 

 

 

 

 

 

 

 

-

 

 

 

 

d

 

 

 

F

 

 

 

 

 

 

t

 

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

to

 

 

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

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

m

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

 

.

 

 

 

 

 

.c

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

 

 

 

 

 

e

 

vi_progid("HotFixChecker")

 

 

 

df-xchan

20

 

 

 

 

 

 

 

 

21 ]

 

 

 

 

 

 

 

 

 

22 class ATL_NO_VTABLE CHotFixChecker :

 

 

 

 

 

 

 

 

23 public IHotFixChecker

 

 

 

 

 

 

 

 

24 {

 

 

 

 

 

 

 

 

 

25 public:

 

 

 

 

 

 

 

 

26

HRESULT IsHotFixInstalled(BSTR bstrQNumber,

 

 

 

 

 

 

 

 

27

VARIANT_BOOL *pbInstalled)

 

 

 

 

 

 

 

 

28

{

 

 

 

 

 

 

 

 

29

// TODO: реализовать функцию

 

 

 

 

 

 

 

 

30

return S_OK;

 

 

 

 

 

 

 

 

31

}

 

 

 

 

 

 

 

 

32 };

 

 

 

 

 

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

 

 

 

 

Всего 27 строк – и мы имеем полнофункциональный COM-сервер в виде DLL вместе с возможностью авторегистрации, библиотекой типов и готовым для компиляции кодом заглушки и заместителя. Чтобы сделать то же самое, не прибегая к ATL, понадобилось бы написать примерно 800 строк кода. Файл, содержащий автоматически вставленный код, содержит 318 строк – столько вы написали бы сами, если бы не пользовались атрибутами (правда, сгенерированный файл грешит многословием). А теперь хватило всего двадцати семи.

Проанализируем этот пример, чтобы понять, как же пишется COM-сервер с применением атрибутов ATL.

Атрибут module

[module(type=dll, name="HotFixChecker")];

Атрибут module необходим, чтобы проект «оторвался от земли». Если забыть про него, то ATL-проект даже не откомпилируется, поскольку этот атрибут отвечает за многие важные операции, которые должен выполнять реализуемый сервер. Тип модуля задается с помощью параметра type, который может принимать значения EXE, DLL и SERVICE. От значения type зависит класс, которому принадлежит глобальная переменная _AtlModule: AtlDllModule, AtExeModule è ò.ä.

Применение этого атрибута приводит к выполнению следующих действий:

åñëè type равно DLL, то реализуются и экспортируются функции DllGetClassObject, DllCanUnloadNow, DllRegisterServer и DllUnregisterServer;

реализуется точка входа в приложение: WinMain или DllMain;

создается библиотека типов и в проекте объявляется блок библиотеки (с использованием значения параметра name);

объявляется и определяется глобальная переменная _AtlModule.

 

 

 

 

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

 

 

 

 

 

 

Библиотека ATL 673

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w Click

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

-xcha

Как видите, атрибут важный и делающий очень много.

 

 

p

 

-x cha

 

 

 

 

 

 

 

 

 

 

 

 

 

 

e

 

 

 

 

df

 

 

n

e

 

 

 

 

df

 

 

n

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

[module(type=dll, name="HotFixChecker")] class CHFChecker

{

public:

int DllMain(DWORD dwReason, PVOID p)

{

return __super::DllMain(dwReason, p);

}

int RegisterServer(BOOL bRegTypeLib)

{

return __super::RegisterServer(bRegTypeLib);

}

};

Ключевое слово __super языка C++ просит компилятор автоматически найти базовый класс.

Ниже приведен список допустимых параметров атрибута module:

[module(type=dll,

name=string,

uuid=uuid,

version=1.0,

lcid=integer,

control=boolean,

helpstring=string,

helpstringdll=string,

helpfile=string,

helpcontext=integer,

hidden=boolean,

restricted=boolean,

custom=string, resource_name=string

) ];

Атрибут interface

Идем дальше. Если вы когда-нибудь писали определения интерфейсов на языке IDL, то без сомнения узнали следующий атрибут:

[object, uuid("EAA203CA-24D4-4C49-9A76-1327068987D8")] __interface IHotFixChecker

{

HRESULT IsHotFixInstalled([in] BSTR bstrQNumber, [out, retval] VARIANT_BOOL *pbInstalled);

};

 

 

 

 

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

 

 

674 Глава 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

 

 

 

 

 

Атрибут object идентичен одноименному атрибуту в IDL, поэтому много-x cha

 

 

 

 

 

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

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

разрешено наследовать произвольному числу базовых интерфейсов;

запрещено наследовать базовому классу;

запрещено включать конструкторы и деструкторы;

разрешены только открытые, чисто виртуальные методы;

запрещено наличие данных-членов;

запрещено включать статические методы.

Таким образом, это ключевое слово может оказаться полезным не только для реализации COM-объектов.

COM-интерфейсы описаны внутри блока, вводимого словом __interface. Как и в случае IDL, определение интерфейса должно быть однозначным, все параметры следует помечать атрибутом [in] или [out].

Атрибут coclass

[coclass uuid("FC9CBC60-4648-4E66-9409-610AD30689C7"), vi_progid("HotFixChecker")

]

Этот синтаксис очень напоминает применяемый в IDL, но есть несколько расширений. Атрибут coclass применяется к объявлению класса, в котором будет реализована функциональность компонента. Поэтому нужно лишь описать характеристики класса компонента в предшествующем ему атрибуте. В данном случае мы задаем CLSID и ProgID компонента. Вот некоторые особенности работы этого атрибута:

вставляет блок coclass в библиотеку типов;

вставляет код для регистрации CLSID и ProgID компонента.

Еще один важный параметр, который можно задать в атрибуте coclass, – это потоковая модель компонента. В зависимости от нее вставляется код, выводящий ваш класс из подходящей конкретизации CComObjectRootEx. Параметр может задаваться так: threading=apartment èëè threading=free.

Объявление класса, следующего за этим атрибутом – как раз то место, куда провайдер атрибутов и помещает большую часть кода. Так, в рассматри-

 

 

 

 

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

 

 

 

 

 

 

Добавление СОМ расширений в программу RPCDUMP 675

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w Click

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

 

 

 

 

 

 

 

 

p

 

-x cha

 

 

 

 

 

 

 

-xchваемомa

примере в объявление нашего класса будут добавлены следующие

 

 

e

 

 

 

 

df

 

 

n

e

 

 

 

 

df

 

 

n

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

базовые классы:

public CComCoClass<CHotFixChecker, &__uuidof(CHotFixChecker)>, public CComObjectRootEx<CComSingleThreadModel>,

public IProvideClassInfoImpl<&__uuidof(CHotFixChecker)>

Как вы знаете, CComCoClass наделяет компонент способностью создавать фабрику класса. CComObjectRootEx реализует подсчет ссылок в зависимости от заданной потоковой модели. IProvideClassInfoImpl реализует интерфейс, позволяющий клиентам получать указатель на интерфейс ITypeInfo.

Следующий важный кусок, автоматически вставляемый в код компонента, – это карта COM:

BEGIN_COM_MAP(CHotFixChecker)

COM_INTERFACE_ENTRY(IHotFixChecker),

COM_INTERFACE_ENTRY(IProvideClassInfo)

END_COM_MAP()

Поскольку атрибуты наделены «интеллектом», ATL знает, что наш класс предоставляет только один интерфейс IHotFixChecker и, естественно, он включен в карту.

Компиляция COM-сервера

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

Добавление COM расширений в программу RPCDUMP

Утилита RPCDump распечатывает содержимое карты оконечных точек RPC на удаленном компьютере. Это полезно для разных целей, в том числе для поиска потенциально небезопасных RPC-интерфейсов. В частности, этот инструмент можно применять для защиты ПК от сетевых вторжений. Но прежде чем останавливать сетевые службы на ПК, нужно знать, что именно данный компьютер предоставляет. Специалисты по сетевой безопасности часто используют программу отображения портов (port mapper) для выявления открытых TCP и UDP-портов. Описываемая утилита эквивалентна сканеру портов в плане определения служб RPC, предоставляемых конкретной машиной.

 

 

 

 

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

 

 

 

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

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

 

 

 

 

 

 

 

m

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

-xcha

 

 

 

 

 

p

 

 

 

 

 

 

 

 

 

 

 

 

 

В корпоративной сети ей можно было бы воспользоваться, например, чтобы-x cha

 

e

 

 

 

 

df

 

 

n

e

 

 

 

 

df

 

 

n

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

проверить, все ли RPC-интерфейсы удовлетворяют принятой политике. Типы RPC-привязок ncacn_np и ncacn_ip_tcp определяют удаленно дос-

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

nacan_ip_tcp:127.0.0.1[1025] ncacn_np:\\\\MYCOMPUTER[\\PIPE\atsvc] ncacn_np:\\\\MYCOMPUTER[\\pipe\Ctx_WinStation_API_service] ncacn_np:\\\\MYCOMPUTER[\\PIPE\DAV RPC SERVICE] ncacn_np:\\\\MYCOMPUTER[\\PIPE\winreg]

Из этой распечатки видно, что порт 1025 соответствует оконечной точке RPC, равно как и именованные каналы Ctx_WinStation_API_service, DAV RPC SERVICE и winreg. И все они доступны для дистанционного манипулирования. Имея это в виду, можете закрыть столько служб, доступных через RPC, сколько необходимо для доведения безопасности ПК до желаемого уровня.

Типичная относящаяся к безопасности утилита на платформе Win32, представляет собой консольное приложение, которому большая часть, если не все, аргументы передаются в командной строке. Результаты своей работы такая программа выводит на стандартный вывод. Памятуя об этом, мы покажем, как добавить COM-расширение к существующей утилите RPCDump, которую написал Тодд Сабин (Todd Sabin). В этом примере мы будем пользоваться ATL-атрибутами, имеющимися в языке Visual C++.NET.

Но сначала сформулируем критерии успешности нашей попытки интеграции с существующим COM-приложением:

сохранить семантику командной строки;

минимизировать изменения в исходном тексте утилиты.

Основные шаги, необходимые для добавления COM-расширения в утилиты типа RPCDump, таковы:

добавить возможность работы в качестве внепроцессного COM-серве- ра путем применения ATL-атрибутов;

перехватить управление точками входа;

определить интерфейсы COM-объектов;

реализовать COM-объекты, предоставляемые утилитой;

добавить процедуры интеграции с утилитой.

Программа RPCDump состоит из единственного файла RPCdump.c. После добавления COM-расширений файлов станет три: RPCdump.c, COMSupport.cpp и COMSupport.h. Для добавления COM-расширений в исходную программу надо будет добавить или изменить в ней всего семь строк кода.

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

F

 

 

 

 

 

 

t

 

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

 

.

 

 

 

 

 

.c

 

13.1.

 

 

p

 

-xchПримерa

 

 

 

 

 

 

g

 

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

F

 

 

 

 

 

 

t

 

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

Добавление СОМ расширений в программу RPCDUMP 677

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

Интеграция путем добавления ATL*атрибута module

 

.

 

-x cha

 

.c

 

 

 

p

 

g

 

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

 

 

 

 

 

1 [module(exe, name="RPCDump")]

2 class CConsoleApp

3 {

4 public:

5bool IsComRequest()

6{

7 LPTSTR lpCmdLine = GetCommandLine();

8

9CString str = lpCmdLine;

10str = str.MakeLower();

11if (str.Find(_T("comserver")) != -1 ||

12str.Find(_T("regserver")) != -1)

13return true;

14

15return false;

16}

17

18int WINAPI WinMain(int nShow)

19{

20g_IsCOM = IsComRequest();

21if (!g_IsCOM)

22{

23BEGIN_ENTRYPOINT();

24rpcdump_main(g_argc, g_argv);

25END_ENTRYPOINT();

26return 0;

27}

28

29// Если мы дошли до этой точки, значит, это запрос на создание

30// объекта, следовательно, консоль не нужна.

31FreeConsole();

32

33// Информация о состоянии при вызове функции rpcdump_main

34// хранится в локальной памяти потока (TLS). О порядке работы

35// с ней см. описание функции SetInterfaceID (и связанных с ней)

36// а также метода IRpcEnum::Execute.

37

38g_dwCOMCallTls = TlsAlloc();

39int nRes = super::WinMain(nShow);

40TlsFree(g_dwCOMCallTls);

41

42return nRes;

43}

44

45// Следующая функция специализирует регистрацию COM

46HRESULT RegisterServer(BOOL bregTypeLib = 0, CLSID *pCLSID = 0)

47{

48// Выполнить стандартную регистрацию

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

X

 

 

 

 

 

 

 

 

-

 

 

 

 

d

 

 

 

F

 

 

 

 

 

 

t

 

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

to

 

 

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

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

m

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

 

.

 

 

 

 

 

.c

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

 

 

 

 

 

e

 

HRESULT hr = __super::RegisterServer(bregTypeLib, pCLSID);

 

 

 

df-xchan

49

 

 

 

 

 

 

 

 

50

 

51CRegKey key;

52if (hr == S_OK)

53{

54// Открыть ключ CLSID для этого объекта

55LPOLESTR lpCLSID = 0;

56StringFromCLSID(__uuidof(CEndpoint), &lpCLSID);

57strKey.Format(_T("CLSID\\%s\\LocalServer32"), lpCLSID);

58CoTaskMemFree(lpCLSID);

59

60 key.Open(HKEY_CLASSES_ROOT, strKey.GetBuffer(0));

61

62TCHAR szPath[MAX_PATH];

63DWORD cb;

64

65// Добавить в конец старого значения ключа строку

66// " -COMSERVER".

67key.QueryValue(szPath, NULL, &cb);

68lstrcat(szPath, _T(" -COMSERVER"));

69key.SetValue(szPath);

70}

71

72return hr;

73}

74};

 

 

 

 

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

 

 

 

 

Анализ

В строке 1 объявлен ATL-атрибут module. В нем приложение описывается как EXE-сервер COM, а библиотеке типов назначается имя RPCDump. Как вы знаете, атрибут module объявляет глобальную переменную _AtlModule, которая принадлежит классу, производному от соответствующего класса ATL: CAtlExeModule èëè CAtlDllModule. В данном случае мы строим EXE-сервер, значит, будет выбран класс CAtlExeModule. Поскольку за объявлением атрибута module нет точки с запятой, то следующий далее класс считается производным от CAtlExeModule. Поэтому мы можем переопределить в нем некоторые методы, в частности, точку входа и регистрацию COM-объектов.

Раз мы определяем EXE-сервер, то ATL считает, что точкой входа в него будет функция WinMain, применяемая в графических приложениях. Поэтому атрибут module определяет точку входа как _tWinMain, ожидая, что она будет управлять потоком управления в EXE-сервере. Но одна из наших целей – сохранить консольный интерфейс утилиты, так что поток управления придется перехватить. Как это сделать, будет показано ниже.

В строке 2 в классе CConsoleApp (который атрибут module сделает производным от CAtlExeModule) реализованы два метода, вызываемые библиоте-

 

 

 

 

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

 

 

 

 

 

 

Добавление СОМ расширений в программу RPCDUMP 679

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w Click

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

 

 

 

 

 

 

 

 

p

 

-x cha

 

 

 

 

 

 

 

-xchêîéa

ATL в нужный момент: WinMain и RegisterServer. Если бы мы не стали их

 

 

e

 

 

 

 

df

 

 

n

e

 

 

 

 

df

 

 

n

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

Функция WinMain (строка 18) решает две важных задачи. Во-первых, если приложение загружено как автономная программа, то вызывается исходная точка входа rpcdump_main (строки 20–27). В этом случае функция IsComRequest (вызываемая из WinMain в строке 20) возвращает FALSE, и тогда после вызова rpcdump_main приложение завершается (строка 26).

Обратите внимание на пару макросов BEGIN_ENTRYPOINT() и END_ENTRYPOINT() перед вызовом rpcdump_main. Они расширяются следующим образом:

#define BEGIN_ENTRYPOINT() __try { #define END_ENTRYPOINT() } \ __except(EXCEPTION_EXECUTE_HANDLER) {}

Это означает, что мы перехватываем все исключения, так что управление обязательно вернется в WinMain. Бывают случаи, когда функция rpcdump_main намеренно возбуждает исключение. Мы еще вернемся к этому при рассмотрении файла COMSupport.h.

Если же утилита загружена в результате запроса от COM, то она не должна как-то взаимодействовать с пользователем. Поэтому необходимо закрыть предоставляемое системой по умолчанию окно консоли, вызвав функцию FreeConsole в строке 31.

Примечание

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

В строке 38 мы получаем индекс в локальную память потока (Thread Local Storage – TLS), а в строке 40 освобождаем его. TLS – это механизм, позволяющий сохранить значения типа DWORD, связанные с данным потоком и только с ним. Для получения памяти, в которой их можно сохранить, нужно вызвать функцию TlsAlloc. В нашем случае локальная память нужна для хранения указателя на структуру TOOL_CALL_CONTEXT. Ее назначение мы объясним ниже при рассмотрении процедур интеграции с приложением и кокласса CRPCDump.

 

 

 

 

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

 

 

680 Глава 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

 

 

 

 

 

Функция IsComRequest (строка 5) определяет, было ли приложение вызва--x cha

 

 

 

 

 

но средой исполнения COM, анализируя наличие флага -COMSERVER в командной строке. Почему такой метод работает, объяснено ниже в разделе «Поток управления».

Метод RegisterServer (строка 46) вызывается инфраструктурой ATL, если приложение должно зарегистрировать себя в качестве COM-сервера, то есть в командной строке есть флаг /RegServer.

Поскольку в данном проекте для регистрации класса компонента применяются средства ATL, а один аспект регистрации нужно реализовать нестандартно, нам приходится переопределить этот метод. Нестандартность заключается в добавлении строки «-COMSERVER» к значению по умолчанию для ключа LocalServer32. Стандартно это значение содержит путь к файлу EXE-сервера и используется средой исполнения COM для запуска сервера при поступлении запроса от клиента. Таким образом, когда клиент запрашивает наш COMобъект, сервер будет загружен, а в его командной строке будет присутствовать флаг «-COMSERVER».

Поток управления

Следующий шаг – интегрировать в RPCDump поток управления из файла COMSupport.cpp.

Как вы помните, ATL-атрибут module определяет в качестве точки входа в EXE-сервер функцию _tWinMain (см. рис. 13.4). Но она не вызывается при входе, потому что в параметрах проекта сказано, что это консольное приложение, значит, должна быть вызвана функция с именем main. Раз ATL не предоставляет такой функции, нам придется реализовать ее самостоятельно, как показано в примере 13.2.

Пример 13.2. Перехват потока управления в точке входа

1 int main(int argc, char *argv[])

2 {

3// Сохранить аргументы

4g_argc = argc;

5 g_argv = argv;

6

7 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);

8

9STARTUPINFO si = {0};

10GetStartupInfo(&si);

11LPTSTR lpCmdLine = GetCommandLine();

13 // функция _tWinMain вставлена ATL-атрибутом module

14 // В конечном итоге управление попадет в CConsoleApp::WinMain