ПЕРЕЛІК ДИСЦИПЛІН:
  • Адміністративне право
  • Арбітражний процес
  • Архітектура
  • Астрологія
  • Астрономія
  • Банківська справа
  • Безпека життєдіяльності
  • Біографії
  • Біологія
  • Біологія і хімія
  • Ботаніка та сільське гос-во
  • Бухгалтерський облік і аудит
  • Валютні відносини
  • Ветеринарія
  • Військова кафедра
  • Географія
  • Геодезія
  • Геологія
  • Етика
  • Держава і право
  • Цивільне право і процес
  • Діловодство
  • Гроші та кредит
  • Природничі науки
  • Журналістика
  • Екологія
  • Видавнича справа та поліграфія
  • Інвестиції
  • Іноземна мова
  • Інформатика
  • Інформатика, програмування
  • Юрист по наследству
  • Історичні особистості
  • Історія
  • Історія техніки
  • Кибернетика
  • Комунікації і зв'язок
  • Комп'ютерні науки
  • Косметологія
  • Короткий зміст творів
  • Криміналістика
  • Кримінологія
  • Криптология
  • Кулінарія
  • Культура і мистецтво
  • Культурологія
  • Російська література
  • Література і російська мова
  • Логіка
  • Логістика
  • Маркетинг
  • Математика
  • Медицина, здоров'я
  • Медичні науки
  • Міжнародне публічне право
  • Міжнародне приватне право
  • Міжнародні відносини
  • Менеджмент
  • Металургія
  • Москвоведение
  • Мовознавство
  • Музика
  • Муніципальне право
  • Податки, оподаткування
  •  
    Бесплатные рефераты
     

     

     

     

     

     

         
     
    Перехоплення методів інтерфейсу Iunknown
         

     

    Інформатика, програмування

    Перехоплення методів інтерфейсу IUnknown

    Олексій Остапенко

    Введення

    У цій статті розглядається технологія, що дозволяє перехоплювати виклики методів інтерфейсу IUnknown COM-об'єкта. Крім дослідницьких цілей, ця технологія може мати і практичне застосування. Вона дозволяє здійснювати такі корисні дії, як майже прозора підміна контексту користувача, "під яким" виробляються виклики методів віддаленого об'єкта, "агрегування" віддалених об'єктів, агрегування об'єктів, не підтримують агрегацію і т.п. З дослідницької точки зору перехоплення викликів IUnknown дозволяє заглянути у нутрощі взаємодії додатку і використовуваних їм COM-об'єктів. Наприклад, отлажівая наведений у статті приклад, я виявив, що виклик функції CreateObject скриптової рантайма призводить до запиту чотирьох (!) інтерфейсів замість одного у створюваного об'єкта. :)

    Трохи теорії

    Інтерфейс IUnknown є основоположним елементом COM. Він має 3 методи, які керують доступом до інших інтерфейсів об'єкту:

    QueryInterface.

    AddRef.

    Release.

    Перехопивши виклики методів 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 (& pDispatch ));     

    Цей виклик транслюється приблизно в такий:        

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

    reinterpret_cast (& 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)   

    (   

    (   

    void * tmp =   * reinterpret_cast (pItf);   

    CComCritSecLock   lock (m_csHooks);   

    HookMap:: iterator it = m_hooks.lower_bound (tmp);   

    if (it == m_hooks.end () | | (* it). first! = tmp)// а чи немає вже такого   хука?   

    (   

    // ставимо новий хук   

    // по-хорошому, тут варто було б ще перевіряти,   не повернув Чи new 0   

    HookPtr pNewHook (new HookEntry ());//   

    if (pNewHook-> Hook (pItf))   

    (   

    m_hooks.insert (it,   HookMap:: value_type (tmp, pNewHook ));   

    // запобігаємо передчасну вивантаження dll   

    if (+ + m_totalRefCount == 1)   

    _Module.Lock ();   

    )   

    else   

    return NULL;   

    )   

    else   

    (* it). second-> AddRef ();// хук вже є.   просто додаємо посилання   

    )// lock.Unlock ();      

    // додаємо або дістаємо credentials   

    CComCritSecLock   lock2 (m_csCredentials);   

    CredentialsMap:: iterator itc =   m_credentials.lower_bound (pItf);   

    // такого елемента ще ні - доведеться додати   

    if (itc == m_credentials.end () | |   (* itc). first! = pItf)   

    (   

    m_credentials.insert (itc,   CredentialsMap:: value_type (pItf, pCredentials ));   

    return   pCredentials-> GetCredentials ();   

    )   

    else   

    return   (* itc). second-> GetCredentials ();   

    )     

    Надалі цей же метод викликається з перехоплювача QueryInterface:

    Метод QueryInterfaceHook        

    // хук QueryInterface   

    STDMETHODIMP HookEntry:: QueryInterfaceHook (void * pItf, REFIID iid,   void ** ppvObject)   

    (   

    // добуваємо Хелпер-об'єкт   

    HookEntry * pHook =   HookFromItf (reinterpret_cast (pItf ));   

    if (pHook == NULL)   

    // хук вже хтось зняв: (   

    return ((IUnknown *) pItf) -> QueryInterface (iid,   ppvObject);      

    // IClientSecurity повинен проходити повз   

    if (:: InlineIsEqualGUID (iid,   IID_IClientSecurity))   

    return pHook-> m_oldQI (pItf,   iid, ppvObject);      

    // власне QueryInterface   

    CComPtr   spUnknown;   

    HRESULT hr =   pHook-> m_oldQI (pItf, iid, (void **) & spUnknown.p);   

    if (FAILED (hr))   

    return hr;      

    // добуваємо пари "логін-пароль"   

    CredentialsHolder:: CredentialsPtr *   pCredentials = CredentialsFromItf (reinterpret_cast (pItf ));   

    if (pCredentials! = NULL)   

    (   

    // встановлюємо хук на інтерфейс   

    RPC_AUTH_IDENTITY_HANDLE ident =   HookInterface (spUnknown.p, * pCredentials);   

    if (ident)   

    (   

    // і встановлюємо на інтерфейс proxy blanket   

    hr =:: CoSetProxyBlanket (spUnknown.p,   RPC_C_AUTHN_WINNT,   

    RPC_C_AUTHZ_NONE, NULL,   RPC_C_AUTHN_LEVEL_DEFAULT,   

    RPC_C_IMP_LEVEL_IMPERSONATE,   ident, EOAC_NONE);   

    if (FAILED (hr) & & hr! =   E_NOINTERFACE)   

    return hr;   

    )   

    )   

    * ppvObject =   reinterpret_cast (spUnknown.Detach ());   

    return S_OK;   

    )     

    Підрахунок посилань для підправлені VTBL реалізований безпосередньо в класі HookEntry.        

    ПОПЕРЕДЖЕННЯ   

    Перший дзвінок HookInterface повинен бути   проведений відразу після отримання першого покажчика на будь-який інтерфейс   COM-об'єкта. В іншому випадку в подальшому може виникнути ситуація, коли   хук буде знято, але об'єкт все ще буде жити.     

    Корисне навантаження

    В якості корисного навантаження розглянутий приклад здійснює підміну контексту користувача. Таким чином, приклад раcсчітан на роботу з віддаленими COM-об'єктами (CLSCTX_REMOTE_SERVER). Контекст користувача підміняється за допомогою виклику CoSetProxyBlanket із зазначенням нового RPC_AUTH_IDENTITY_HANDLE.        

    ПРИМІТКА   

    Досить цікавий момент - в MSDN   практично відсутня інформація про те, як правильно створювати RPC_AUTH_IDENTITY_HANDLE.   Написано, що за певних умов він є просто дороговказом на   структуру SEC_WINNT_AUTH_IDENTITY (_EX). Однак експерименти показали, що   просте створення такої структури і підсовування покажчика на неї в   CoSetProxyBlanket не призводить ні до чого, крім помилки (в той же час з   CoCreateInstanseEx такий фокус проходить). Досвідченим шляхом було встановлено, що   правильну структуру можна створити викликом DsMakePasswordCredentials. На жаль,   цей API специфічний для ОС Win2k і далі, і приклад не буде працювати під NT   4.     

    У результаті, для "боротьби" з RPC_AUTH_IDENTITY_HANDLE був створений окремий Хелпер-клас CredentialsHolder. Для зберігання відповідності credentials конкретним інтерфейсів використовується ще один контейнер std:: map. Варто окремо зауважити, що в контейнері зберігаються не екземпляри класів CredentialsHolder, а "розумні" (smart) покажчики shared_ptr, що реалізують підрахунок посилань. Це зроблено для запобігання необхідності копіювання примірників CredentialsHolder, для яких операція копіювання не тільки накладна, а й просто не очевидна.

    Використання

    У фото з проектом міститься демонстраційний скрипт test.js, що показує, як можна підміняти користувальницький контекст при створенні та використанні віддаленого об'єкта. Метод CreateObject дозволяє інстанцііровать віддалений об'єкт і повернути вказівник на його головний інтерфейс IDispatch. Метод HookObject дозволяє перехопити раніше отриманий інтерфейс IDispatch.        

    ПРИМІТКА   

    У теорії HookObject може давати не   зовсім ті результати, на які хочеться розраховувати. Це пов'язано з тим, що   в даному випадку неможливо точно відстежити момент звільнення інтерфейсу,   тому що на нього (а можливо, що й на інші інтерфейси об'єкта) вже є   посилання, не враховані до перехоплення. Однак, при практичному використанні   даного механізму всередині ASP-скриптів, передчасного зняття хуков НЕ   спостерігалося.     

    За допомогою описаного механізму перехоплення можна робити та інші кумедні трюки. Наприклад, агрегувала об'єкти, які не підтримують агрегацію. Справді, раз ми маємо повний контроль над викликами IUnknown, ніщо не заважає повністю замінити його реалізацію так, що вона буде в точності емулювати поведінка IUnknown агрегованого об'єкта.

    Список літератури

    MSDN

    Введення в COM

    Захист в DCOM/COM +

    Для підготовки даної роботи були використані матеріали з сайту http://www.rsdn.ru/

         
     
         
    Реферат Банк
     
    Рефераты
     
    Бесплатные рефераты
     

     

     

     

     

     

     

     
     
     
      Все права защищены. Reff.net.ua - українські реферати ! DMCA.com Protection Status