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

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

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

 

 

 

 

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

-xch

 

n

e

 

 

 

 

 

a

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

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

 

to

 

 

 

 

 

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Рис. 13.4. Процесс загрузки программы

15_tWinMain(hInstance, NULL, lpCmdLine, si.wShowWindow);

16}

Анализ

В строках 4 и 5 мы сохраняем переданные в командной строке аргументы для последующего анализа.

Далее мы просто готовим аргументы для вставленной ATL функции _tWinMain:

в строке 7 мы получаем описатель модуля и сохраняем его в переменной hInstance. Позже он станет первым аргументом _tWinMain;

в строках 9 и 10 извлекается информация о начальном состоянии приложения. Значение поля wShowWindows из структуры STARTUPINFO передается в _tWinMain в качестве последнего аргумента;

в строке 11 мы получаем командную строку приложения и передаем ее

_tWinMain третьим аргументом.

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

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

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

 

 

 

 

 

e

 

 

 

 

df

 

 

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

 

 

 

 

Речь идет о процедурах, которые вызывает исходная утилита для передачи существенных данных. Например, сканер портов мог бы сообщить, открыт некий порт или нет. Сканер срочных исправлений сообщил бы, установлено исправление или нет. Ну а утилита RPCDump извещает о доступных RPCинтерфейсах. Чтобы написать процедуры интеграции с приложением, надо представлять себе, как исходная программа управляет данными. Если, как в случае RPCDump, управление сводится к записи на стандартный вывод, то перед записью процедура интеграции должна получить доступ к данным и сохранить их у себя. Именно это и делается в примерах 13.3. и 13.4.

Пример 13.3. Структура данных для процедуры интеграции с приложением (из файла COMSupport.h)

1 typedef struct _IFACE_DATA_ENTRY {

2CComBSTR m_bstrInterfaceID;

3CComBSTR m_bstrVersionID;

4CComBSTR m_bstrUUID;

5CComBSTR m_bstrBinding;

6 } IFACE_DATA_ENTRY, *PIFACE_DATA_ENTRY;

7

8 typedef struct _TOOL_CALL_CONTEXT {

9std::vector<IFACE_DATA_ENTRY> *pIfaceVector;

10IFACE_DATA_ENTRY CurrentRecord;

11} TOOL_CALL_CONTEXT, *PTOOL_CALL_CONTEXT;

Пример 13.4. Процедура интеграции с приложением (из файла COMSupport.cpp)

1 extern "C" {

// компоновка с учетом правил языка C

2 void

SetInterfaceID(char *pIFaceID)

3 {

 

 

 

4

if

(!g_IsCOM) return;

5

 

 

 

6PTOOL_CALL_CONTEXT pCtx =

7

(PTOOL_CALL_CONTEXT)TlsGetValue(g_dwCOMCallTls);

8

 

9

pCtx->CurrentRecord.m_bstrInterfaceID = CComBSTR(pIFaceID);

10}

11void SetVersion(char *pVersion)

12{

13if (!g_IsCOM) return;

14

15PTOOL_CALL_CONTEXT pCtx =

16(PTOOL_CALL_CONTEXT)TlsGetValue(g_dwCOMCallTls);

17pCtx->CurrentRecord.m_bstrVersionID = CComBSTR(pVersion);

18}

 

 

 

 

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-xch19an

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

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

 

to

 

 

 

 

 

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

20 void SetUUID(char *pUuid)

21{

22if (!g_IsCOM) return;

24PTOOL_CALL_CONTEXT pCtx =

25(PTOOL_CALL_CONTEXT)TlsGetValue(g_dwCOMCallTls);

26pCtx->CurrentRecord.m_bstrUUID = CComBSTR(pUuid);

27}

28

29 void SetBinding(char *pBinding)

30{

31if (!g_IsCOM) return;

33PTOOL_CALL_CONTEXT pCtx =

34(PTOOL_CALL_CONTEXT)TlsGetValue(g_dwCOMCallTls);

35pCtx->CurrentRecord.m_bstrBinding = CComBSTR(pBinding);

36}

37

38 void NextRecord()

39{

40if (!g_IsCOM) return;

42PTOOL_CALL_CONTEXT pCtx =

43(PTOOL_CALL_CONTEXT)TlsGetValue(g_dwCOMCallTls);

45// Теперь нужно сохранить запись. Мы воспользуемся для этого

46// стандартным классом vector, ассоциированным с созданным

47// COM-объектом.

48pCtx->pIfaceVector->push_back(pCtx->CurrentRecord);

49}

50}

Анализ

Имена всех показанных выше процедур выбраны в соответствии с данными, которые предоставляет утилита RPCDump. Эти данные взаимосвязаны (привязка, UUID, номер версии и идентификатор интерфейса), но не так легко доступны, как структура в RPCDump.c, поэтому из процедуры интеграции доступ к ним производится последовательно. Когда все данные об интерфейсе получены, вызывается функция NextRecord, которая помещает их в одну запись и переустанавливает указатель для следующей итерации.

Таким образом, последовательность вызовов выглядит следующим образом:

1.RPCDump->SetInterfaceID(...)

2.RPCDump->SetVersion(...)

3.RPCDump->SetUUID(...)

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

X

 

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

F

 

 

 

 

 

 

 

t

 

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

to

 

 

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

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

 

w

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

 

.

 

 

 

 

g

.c

 

 

 

 

p

 

-xcha

 

 

 

4. RPCDump->SetBinding(...)

 

 

 

df

 

e

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

 

 

 

 

5. RPCDump->NextRecord(...)

 

 

 

 

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 находится директива extern «C» {. Она говорит, что все функции

âпоследующем блоке должны компоноваться с учетом правил языка C. Это необходимо потому, что обращения к процедурам интеграции производятся из программы RPCDump, которая написана на C, а не на C++. Блок внешней компоновки заканчивается в строке 50.

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

if (!g_IsCom) return;

Если программа запущена не как EXE-сервер COM, то глобальная переменная g_IsCOM равна FALSE, и в этом случае процедура интеграции сразу же возвращает управление. Это разумно, так как нет смысла сохранять данные – ведь никаких COM-клиентов не существует.

Далее идет такая строка:

PTOOL_CALL_CONTEXT pCtx =

(PTOOL_CALL_CONTEXT)TlsGetValue(g_dwCOMCallTls);

В ней переменной pCtx присваивается значение, сохраненное ранее в локальной памяти потока. Стоит отметить несколько моментов:

в структуре, описывающей контекст вызова, есть указатель на вектор,

âкотором хранятся все записи об интерфейсах, и структура, предназна- ченная для хранения информации из текущей записи;

память для структуры, описывающей контекст вызова, выделяется, когда COM-клиент обращается к методу scan COM-объекта, находящегося

âисполняемом файле RPCDump;

элемент в локальной памяти потока вызывает метод CConsoleApp::WinMain после того, как выяснит, что программа запущена как EXE-сер- вер COM.

Если мы дошли до этой точки, значит, программа была запущена как EXEсервер COM, и процедура интеграции вызывается в том потоке, в котором работает COM-клиент. Данные, принадлежащие этому потоку (указатель на структуру TOOL_CALL_CONTEXT), и сохраняются в его локальной памяти.

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

pCtx->CurrentRecord.m_bstrBinding = CComBSTR(pBinding);

 

 

 

 

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 685

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w Click

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

-xcha

 

 

 

 

 

p

 

-x cha

 

 

 

 

 

 

 

 

 

Поскольку в исходном файле RPCDump.C используются только строки

 

 

e

 

 

 

 

df

 

 

n

e

 

 

 

 

df

 

 

n

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

в кодировке ANSI, то процедуры интеграции принимают строковые параметры как char *. Но наш COM-объект должен поддерживать также и обращения из программ на интерпретируемых языках (VBScript, JScript и т.д.), значит, нужны строки типа BSTR. Поэтому мы должны преобразовать переданную строку в другой формат и кодировку Unicode. Это легко сделать, воспользовавшись объектом класса CComBSTR и передав его конструктору полученную строку. Возвращенное значение сохраняется в текущей записи.

Определение интерфейсов COM3объектов

COM-расширение программы RPCDump включает три COM-объекта, у каждого из которых определено по одному интерфейсу (помимо IUnknown и IDispatch):

IrpcEnum;

IendPointCollection;

Iendpoint.

Определения всех этих интерфейсов показаны в примере 13.5.

Пример 13.5. Определения интерфейсов (из файла COMSupport.cpp)

1

[

 

2

object,

// COM-объект

3

dual,

// поддержка IDispatch и vtable

4

uuid("2F55A03C-9513-4CF1-9939-E0BD72E968E8")

5

]

 

6

__interface IEndpoint : IDispatch

7

{

 

8[propget] HRESULT InterfaceID([out, retval] BSTR *bstrVal);

9 [propget] HRESULT Version([out, retval] BSTR *bstrVal);

10[propget] HRESULT Uuid([out, retval] BSTR *bstrVal);

11[propget] HRESULT Binding([out, retval] BSTR *bstrVal);

12};

13

14 [

15

object,

//

COM-объект

16

dual,

//

поддержка IDispatch и vtable

17uuid("7C7487E9-7F08-462C-85CF-CF23C08498AC")

18]

19__interface IEndpointCollection : IDispatch

20{

21[id(DISPID_NEWENUM), propget]

22HRESULT _NewEnum([out, retval] IUnknown** ppUnk);

24[id(DISPID_VALUE), propget]

25HRESULT Item(

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

X

 

 

 

 

 

 

 

 

-

 

 

 

 

d

 

 

 

F

 

 

 

 

 

 

t

 

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

to

 

 

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

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

m

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

 

.

 

 

 

 

 

.c

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

 

 

 

 

 

e

 

[in] long Index,

 

 

 

df-xchan

26

 

 

 

 

 

 

 

 

27

[out, retval] IEndpoint **ppVal);

 

 

 

 

 

 

 

 

28

 

29[id(0x00000001), propget]

30HRESULT Count([out, retval] long* pVal);

31};

32

33 [

34

object,

//

COM-объект

35

dual,

//

поддержка IDispatch и vtable

36uuid("22AD386A-59D0-4d35-90C5-3089E207D73E")

37]

38__interface IRpcEnum : IDispatch

39{

40HRESULT Execute(

41[in] BSTR bstrTarget,

42[out, retval] IEndpointCollection **ppResult

43);

44};

 

 

 

 

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

 

 

 

 

Интерфейс IRpcEnum

Для начала мы рассмотрим последний из представленных в примере 13.5 интерфейсов: IRpcEnum. Это подразумеваемый по умолчанию интерфейс COMобъекта CRPCDump, он и предоставляет методы и свойства, необходимые для исполнения RPCDump.

В строках 33–37 задается ATL-атрибут object, который понуждает ATL вставить код, необходимый данному COM-интерфейсу. В атрибуте object присутствуют два параметра: dual и uuid. Параметр dual говорит, что этот интерфейс должен быть доступен как через IDispatch (поддержка позднего связывания), так и через таблицу виртуальных функций (vtable). Параметр uuid задает идентификатор интерфейс IID.

Интерфейс IRpcEnum определен в строке 38. В нем есть только один метод Execute. Он принимает те же аргументы, которые передаются исходной утилите в командной строке. При обращении к этому методу программа находит все оконечные точки RPC на удаленном хосте и возвращает результат в выходной (помеченной атрибутом [out]) переменной ppResult. В случае успеха ppResult указывает на объект-набор, содержащий информацию обо всех оконечных точках.

Интерфейс IEndPointCollection

Объект, представляющий набор, который возвращает метод IRpcEnum::Execute, определен в строках 14–31. Как и любой другой набор, он содержит указатели на интерфейсы. Но для полноты картины рассмотрим его подробно.

 

 

 

 

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 687

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w Click

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

-xcha

 

 

 

 

 

p

 

-x cha

 

 

 

 

 

 

 

 

 

Атрибуты в строках 14–27 такие же, как для всех остальных интерфейсов:

 

 

e

 

 

 

 

df

 

 

n

e

 

 

 

 

df

 

 

n

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

интерфейс объявляется дуальным и задается его IID.

Вы, наверное, обратили внимание на атрибут id в строках 21 и 24. Чтобы понять его назначение, надо кое-что знать об интерфейсе IDispatch. Этот интерфейс используется COM-клиентами, которые осуществляют позднее связывание. Такой механизм задействуется в ситуации, когда клиент не имеет доступа к информации о типе во время компиляции и должен обращаться к методам интерфейса косвенно (через IDispatch), а не напрямую через таблицу виртуальных функций. Это становится возможным из-за наличия в интерфейсе IDispatch двух методов: GetIDsOFNames и Invoke. Ниже приведены их прототипы:

HRESULT GetIDsOFNames( REFIID riid,

OLECHAR FAR* FAR* rgszNames, unsigned int cNames,

LCID lcid,

DISPID FAR* rgDispId

};

HRESULT Invoke( DISPID dispIdMember, REFIID riid,

LCID lcid, WORD wFlags,

DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo, unsigned int FAR* puArgErr

};

Метод Invoke служит для обращения к конкретному методу, определяемому параметром dispIdMember. Его значение задается в библиотеке типов и, следовательно, должно быть частью описания метода. Именно это мы и видим в описаниях методов _NewEnum и Item. Если COM-клиент не имеет информации из библиотеки типов, он может запросить DISPID у метода GetIDsOFNames интерфейса IDispatch.

Метод _NewEnum возращает указатель на интерфейс IUnknown объектаэнумератора для набора. Обычно он вызывается из языков сценариев для реализации конструкций типа foreach.

Метод Item почти не нуждается в комментариях, он получает числовой индекс и возвращает соответствующий ему указатель на интерфейс IEndPoint. Если задан индекс вне допустимого диапазона, метод возвращает S_FALSE.

Метод Count возвращает число объектов в наборе.

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

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

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

-xcha

 

 

.c

 

 

 

p

 

 

 

Интерфейс IEndPoint

 

 

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

 

 

 

 

Интерфейс IEndPoint определен в строках 1–12, именно указатели на такие интерфейсы, хранятся в наборе IEndPointCollection. Он предоставляет доступ к информации об одном RPC-интерфейсе. Получить ее можно, опросив свойства IntefaceID, Version, Uuid и Binding. Все свойства имеют тип BSTR. Стоит отметить, что информация берется непосредственно из структуры IFACE_DATA_ENTRY.

Классы компонентов

Поскольку определено три интерфейса, существует и три кокласса, а именно:

CEndpoint, CEndpointCollection è CRPCDump. Их код рассматривается ниже.

Пример 13.6. Реализация кокласса CEndpoint (из файла COMSupport.cpp)

1 [coclass, uuid("598E69E2-19E4-9E5C-D180C2FAE6F2", noncreatable]

2 class ATL_NO_VTABLE CEndpoint : public IDispatchIpml<IEndpoint>

3 {

4 public:

5void Initialize(IFACE_DATA_ENTRY *pEntry) {

6// Сохранить информацию о том, на что должен указывать объект

7 m_data = *pEntry;

8 }

9

10 HRESULT get_InterfaceID(BSTR *bstrVal) {

11*bstrVal = m_data.m_bstrInterfaceID.Copy());

12return S_OK;

13}

14

15HRESULT get_Version(BSTR *bstrVal) {

16*bstrVal = m_data.m_bstrVersionID.Copy());

17return S_OK;

18}

19

20HRESULT get_Uuid(BSTR *bstrVal) {

21*bstrVal = m_data.m_bstrUUID.Copy());

22return S_OK;

23}

24

25HRESULT get_Binding(BSTR *bstrVal) {

26*bstrVal = m_data.m_bstrBinding.Copy());

27return S_OK;

28}

29

30protected:

31IFACE_DATA_ENTRY m_data;

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

 

-xchАнализa

 

 

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

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

 

to

 

 

 

 

 

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Класс CEndpoint совсем простой. Его единственное назначение – предоставить COM-клиентам доступ к информации из записи о конкретном RPCинтерфейсе.

Âстроке 1 мы видим ATL-атрибут coclass, который заставляет провайдера атрибутов вставить нужный для работоспособности компонента код. Атрибут noncreatable говорит, что объекты этого класса не могут быть созданы клиентами непосредственно.

Âстроке 2 начинается объявление класса CEndpoint. Обратите внимание на макрос ATL_NO_VTABLE. Он расширяется в __declspec(novtable) и позволяет оптимизировать создание класса за счет устранения процедур инициализации указателя на таблицу виртуальных функций из конструктора и деструктора. Однако это безопасно только для классов, которые не создаются непосредственно, как в данном случае (вспомните, что классы компонентов реально создает CComObject).

Âстроке 2 также сказано, что кокласс наследует классу IDispatchImpl, конкретизированному параметром IEndpoint. Тем самым достигается две цели: а) кокласс наделяется функциональностью, необходимой для поддержки интерфейса IDispatch, и б) неявно кокласс становится производным от интерфейса IEndpoint, который он и призван реализовать.

Âстроке 5 вы видите единственную функцию-член, которая не была определенав интерфейсе: Initialize. Как следует из названия, онапредназначена для инициализации кокласса данными, которые позже будут доступны через его интерфейсы.

Âстроках 10–28 определены методы класса, соответствующие определению интерфейса IEndpoint. Их структура повторяется: скопировать в переданную строку типа BSTR * содержимое соответствующего поля внутренней структуры, например:

HRESULT get_InterfaceID(BSTR *bstrVal) { *bstrVal = m_data.m_bstrInterfaceID.Copy()); return S_OK;

}

Здесь в аргумент bstrVal копируется идентификатор интерфейса из поля m_data.m_bstrInterfaceID. Поскольку поле m_bstrInterfaceID имеет тип CComBSTR, то у него есть метод Copy, который создает копию строки и возвращает ее в качестве значения. Отметим, что в этом коде краткости ради не проверяется, корректно ли значение аргумента bstrVal. В реальной программе всегда нужно предполагать наихудший сценарий, и особенно это относится к 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

 

 

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

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

 

 

 

 

 

 

 

m

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

-xcha

 

 

 

 

 

 

p

 

 

 

 

 

 

 

 

 

 

 

 

e

 

Далее в примере 13.7 показан код энумератора набора. Это типичная для-x cha

 

e

 

 

 

 

df

 

 

n

 

 

 

 

 

df

 

 

n

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ATL-проектов реализация.

Пример 13.7. Реализация кокласса CEndpointCollection (из файла COMSupport.cpp)

1 typedef CComEnumOnSTL<

2IEnumVARIANT,

3&__uuidof(IEnumVARIANT),

4VARIANT,

5_CopyEndpointIFToVariant,

6std::vector<IFACE_DATA_ENTRY>

7 > EnumType;

8

9 typedef ICollectionOnSTLImpl<

10IEndpointCollection,

11std::vector<IFACE_DATA_ENTRY>

12IEndpoint*,

13_CopyInformationToEndpointInterface,

14EnumType

15> EndpointCollectionType;

16

17

18[

19coclass,

20threading=apartment,

21uuid("2C793CBF-51FA-4146-022902FBDDCF"),

22noncreatable

23]

24class ATL_NO_VTABLE CEndpointCollection :

25public IDispatchImpl< EndpointCollectionType,

26

&__uuidof(IEndpointCollection) >

27{

28public:

29// Для этого набора не нужны никакие методы

30}

Анализ

В строках 1–7 определяется синоним EnumType для типа объекта-энумератора набора. Напомним, что энумератор возвращается методом _NewEnum. Рассмотрим этот код внимательнее. Тип EnumType реализован с помощью шаблонного класса CComEnumOnSTL, который определен так:

template <class Base, const IID* piid, class T, class Copy,

class CollType, class ThreadModel = CComObjectThreadModel > class ATL_NO_VTABLE CComEnumOnSTL :

public IEnumOnSTLImpl<Base, piid, T, Copy, CollType>, public CComObjectRootEx< ThreadModel >