Реферат: Перехват методов интерфейса Iunknown

В этой статье рассматривается технология, позволяющая перехватывать вызовы методов интерфейса IUnknown COM-объекта. Кроме исследовательских целей, эта технология может иметь и практическое применение. Она позволяет осуществлять такие полезные действия, как почти прозрачная подмена контекста пользователя, “под которым” производятся вызовы методов удаленного объекта, “агрегирование” удаленных объектов, агрегирование объектов, не поддерживающих агрегацию и т.п. С исследовательской точки зрения перехват вызовов IUnknown позволяет заглянуть во внутренности взаимодействия приложения и используемых им COM-объектов. Например, отлаживая приведенный в статье пример, я обнаружил, что вызов функции CreateObject скриптового рантайма приводит к запросу четырех (!) интерфейсов вместо одного у создаваемого объекта. :)

Немного теории

Интерфейс IUnknown является основополагающим элементом COM. Он имеет 3 метода, управляющих доступом к другим интерфейсам объекта:

QueryInterface.

AddRef.

Release.

Возможно вы искали - Доклад: Wiki как движок обычного сайта

Перехватив вызовы методов IUnknown, можно управлять набором интерфейсов, предоставляемых объектом “наружу” (например, можно спрятать некоторые из них или добавить свои интерфейсы, сделав вид, что они тоже предоставляются объектом), а также управлять некоторыми параметрами вызова методов интерфейсов (например, proxy blanket’ом). Любой другой интерфейс, наследуемый от IUnknown, соответственно, наследует и эти три метода.

Работа с любым интерфейсом осуществляется через указатель на этот интерфейс. Физически указатель на интерфейс – это указатель на переменную, которая, в свою очередь, указывает на таблицу указателей на методы этого интерфейса (VTBL, см. рисунок 1).

Рисунок 1.

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

Похожий материал - Реферат: Информационные технологии

Интерфейс может использоваться клиентом напрямую, если COM-объект создавался внутри процесса (in-proc) клиента и в том же апартаменте (apartment), из которого происходит вызов. В противном случае (внепроцессный (out-of-proc) объект или другой апартамент) клиент вместо реального интерфейса будет использовать его прокси. Однако в обоих случаях указатель на интерфейс ссылается на указатель на VTBL, содержащую указатели на методы. Таким образом, в обоих случаях VTBL интерфейса (или его прокси) напрямую доступна в процессе клиента для чтения, и может быть сделана доступной для записи.

ПРИМЕЧАНИЕ

Компилятор C++ может разместить VTBL в константном сегменте данных, который может быть загружен в страницу памяти с атрибутами защиты “только для чтения” (PAGE_READONLY) (за это замечание отдельное спасибо Николаю Меркину и Алексею Ширшову). В этом случае просто так писать в VTBL не получится. Но, поскольку VTBL находится в адресном пространстве процесса, можно поменять атрибуты защиты страницы на “для чтения и записи” (PAGE_READWRITE) с помощью функции VirtualProtect.

Кроме этих кратких сведений об устройстве указателей на интерфейс нам понадобится понимание того, как именно происходит вызов и передача параметров в метод интерфейса, реализованного как метод C++ класса, унаследованного от интерфейса:

Клиент вызывает метод QueryInterface:

pUnknown->QueryInterface(IID_IDispatch, reinterpret_cast<void**>(&pDispatch));

Очень интересно - Реферат: Концепция управления доходами для операторов связи

Этот вызов транслируется примерно в такой:

((VTBL*)pUnknown)->vtbl->QueryInterface(pUnknown, IID_IDispatch,

reinterpret_cast<void**>(&pDispatch));

т.е. указатель на интерфейс передается первым параметром для метода. Формат вызова фактически соответствует формату вызова нестатического метода класса в конвенции _stdcall.

В начале метода указатель на интерфейс заменяется указателем на this (на самом деле через VTBL вызывается не сам метод, а сгенерированный компилятором переходник, который корректирует указатель на интерфейс и передает управление собственно методу).

Вам будет интересно - Реферат: Радиорелейная связь: организация дальней связи

Принцип перехвата

Для перехвата IUnknown потребуется подменить указатели на методы QueryInterface, AddRef и Release в VTBL всех интерфейсов COM-объекта указателями на нашу реализацию этих методов. Другими словами, необходимо установить хук на вызовы этих методов. Методы-перехватчики не могут быть нестатическими методами класса, так как передаваемый в метод указатель на интерфейс никак не связан с this объекта-перехватчика. Кроме того, по полученному указателю на интерфейс нужно уметь определять указатели на оригинальные методы QueryInterface, AddRef и Release перехваченного интерфейса. Эту задачу можно легко решить с помощью статического экземпляра контейнера std::map (или hash_map), позволяющего по указателю на VTBL получить структуру (или класс), содержащую указатели на оригинальные реализации QueryInterface и т.п.

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

Реализация перехвата

Большая часть действий по перехвату возложена на вспомогательный класс HookEntry. Изначальный перехват интерфейса осуществляется в статическом методе HookInteface:

Похожий материал - Реферат: Телекоммуникационные технологии для дорожных интегрированных систем связи

Метод HookInterface

RPC_AUTH_IDENTITY_HANDLE HookEntry::HookInterface(IUnknown* pItf, const CredentialsHolder::CredentialsPtr& pCredentials)

{

{