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

     

     

     

     

     

         
     
    Хукі і DLL
         

     

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

    Хукі і DLL

    Dr. Joseph M. Newcomer

    Переклад: Олексій Остапенко

    Існує велика плутанина з приводу встановлення і використання глобальних хуков.        

    ПРИМІТКА   

    Можливо варто згадати, що камбали   (прим. перекладача: Flounder - псевдонім автора) в цілому не схвалюють   використання гачків (hooks), але такі гачки (хукі) здаються припустимими.     

    Зауважимо, що ні один із таких проблем не виникає, якщо ви просто відловлює операції у своєму власному процесі. Вони виникають тільки в тому випадку, коли ви хочете отримувати події на системному рівні.

    Основною проблемою тут є адресне простір. Коли глобальна DLL виконується, вона виконується в контексті того процесу, чиє подія перехоплюється. Це означає, що адреси, які бачить DLL, навіть для своїх власних змінних, що є адресами в контексті цільового процесу. Оскільки це DLL, вона має окрему копію своїх даних для кожного використовує її процесу. І це означає, що будь-які значення, які ви використовуєте для глобальних DLL змінних (таких, як оголошені на рівні файлу), є приватними і не будуть наслідувати нічого з початкового контексту бібліотеки. Вони будуть ініціалізувати заново, тобто, звичайно, вони будуть дорівнювати нулю.

    Недавнє повідомлення навіть пропонував концепцію збереження callback-адреси в DLL. Це неможливо. Ну, неможливо не зберегти його, а неможливо його використовувати. Те, що ви зберегли, - це пачка бітів. Навіть якщо ви пройдете викладеної нижче інструкції по створенню розділяється змінної, видимої в усіх примірниках DLL, набір бітів (який ви вважає адресою) насправді є адресою тільки в контексті процесу, зберіг цей набір. Для всіх інших процесів це всього лише набір бітів, і якщо ви спробуєте використовувати його в якості адреси, ви звернетеся з якого-то адресою в процесі, подія якого було перехоплено, що нікому не потрібне. У більшості випадків це просто призведе до падіння додатки.

    Концепція розділених адресних просторів важка для розуміння. Дозвольте мені продемонструвати її на зображенні.

    Тут ми маємо три процеси. Ваш Процес показаний зліва (Your Process). У DLL є сегменти коду (Code), даних (Data) і розділяється сегмент (Shared), як його створити ми обговоримо пізніше. Тепер, якщо перехоплюються DLL викликається для перехоплення події в Процесі A (Process A), вона відображається в адресний простір Процесу A, як зазначено. Код є розділяються, тому адреси в Процесі A посилаються на ті ж сторінки пам'яті, що і адреси у Вашому Процесі. За збігом сторінки пам'яті виявилися відображеними в Процес A по тим же самим віртуальним адресами, тобто адресами, які бачить Процес A. Процес A також отримує свою власну копію сегмента даних, тому все що бачить Процес A в секції "Data", повністю належить йому і не може вплинути на будь-який інший процес (або бути зміненим будь-яким іншим процесом!). Однак, фокус який змушує все це працювати полягає в поділюваному сегменті даних, показаному тут червоним кольором. Статті, адресовані Вашим Процесом в точності ті ж сторінки пам'яті, що і адресовані в Процесі A. Зауважимо, що за збігом ці сторінки опинилися в адресному просторі Процесу A в точності на тих же віртуальних адресах, що і у Вашому Процесі. Якби ви сиділи за налагодженням Вашого Процесу і Процесу A одночасно (що ви можете робити, запустивши дві копії VC + +!) і дивилися б на адресу & something, що знаходився у поділюваному сегменті даних, і дивилися б по ньому в Вашому Процесі і потім за тією ж адресою & something в Процесі A, ви б побачили в точності одні й ті ж дані і навіть по тому ж самому адресою. Якщо б ви використовували відладчик для зміни або відстежували зміни програмою значення something, то ви могли б перейти до іншого процесу, досліджувати його і побачити, що нове значення з'явилося також і тут.

    Але от облом: однакову адресу - це збіг. Це збіг абсолютно і однозначно не гарантується. Подивіться на Процес B. Коли подія перехоплено в Процесі B, в нього відображається DLL. Але адреси, займані нею у Вашому Процесі і Процесі A, не доступні в адресному просторі Процесу B. Тому відбувається переміщення коду на іншу адресу в Процесі B. Код в порядку, йому дійсно байдуже за якою адресою він виповнюється. Адреса даних підправлений так, щоб посилатися на нове положення даних, і навіть колективні дані відображені в інше безліч адрес, таким чином до них звертаються по-іншому. Якщо б ви використовували відладчик з Процесом B і подивилися б на адресу & something в розділяється області, ви б виявили, що адреса something був би іншим, але вміст something було б тим же самим; виконання зміни вмісту у Вашому Процесі або в Процесі A негайно зробило б це зміна видимим у Процесі B, хоча Процес B і бачить його за іншою адресою. Це те ж саме місце фізичної пам'яті. Віртуальна пам'ять - це відображення між адресами, видимими вами, як програмістом, і фізичними сторінками пам'яті, які в дійсності містить ваш комп'ютер.

    Хоча я і назвав однакове розташування збігом, "відповідність" частково навмисне; Windows намагається відображати бібліотеки в ті ж самі віртуальні області, що й в інших екземплярів однієї і тієї ж бібліотеки, кожного разу, коли це можливо. Вона намагається. Їй може не вдасться це зробити.        

    ПРИМІТКА   

    Якщо ви знаєте трохи більше   (достатньо, щоб це представляло небезпеку), ви можете сказати: "Ага!   Я можу перемістити (rebase) мою DLL так, що вона завантажується по не   конфліктуючими адресу, і я зможу проігнорувати цю особливість ". Це   відмінний приклад того, як малі знання можуть стати серйозною   небезпеку. Ви не можете гарантувати що така DLL буде працювати з будь-яким   можливим виконуваним модулем, який може бути будь-коли запущений на вашій   машині! Оскільки це DLL глобального хука, вона може бути викликана з Word,   Excel, Visio, VC + + і шести тисяч додатків, про які ви ніколи не чули,   але можете будь-коли запустити або може запустити ваш клієнт. Тому   забудьте про це. Не намагайтеся переміщувати. В кінці кінців, ви програєте.   Звичайно, в самий невідповідний час з найважливішим вашим клієнтом (наприклад, з   браузером вашого продукту з журналу чи з вашим дуже багатим   замовником, який вже стурбований іншими помилками, які у вас можуть   бути ...). Вважайте, що розділяється сегмент даних "переміщаємо".   Якщо ви не розумієте цей параграф, значить ви знаєте не досить багато,   щоб це представляло небезпеку. І ви можете спокійно його ігнорувати.     

    У переміщення є й інші наслідки. Якщо в DLL ви зберегли вказівник на callback-функцію в контексті Вашого Процесу, то для DLL безглуздо викликати її в Процесі A або Процесі B. Ця адреса призведе до передачу управління в вказує їм область, що нормально, але ця передача відбудеться в адресний простір Процесу A або Процесу B, що абсолютно марно, не кажучи вже про те, що майже напевно фатально.

    Це також означає, що ви не можете використовувати в своєї DLL нічого з MFC. Вона не може бути ні MFC DLL, ні MFC Extension DLL. Чому? Тому, що вона буде викликати функції MFC. А де вони? Ну, вони у вашому адресному просторі. А не в адресному просторі Процесу A, написаного на Visual Basic, або Процесу B, написаного на Java. Таким чином, ви повинні написати DLL на чистому C, і я б рекомендував зовсім не використовувати бібліотеку виконавчі C (CRT). Ви повинні використовувати тільки API. Використовуйте lstrcpy замість strcpy або tcscpy, lstrcmp замість strcmp або tcscmp, і т.д.

    Існує безліч рішень для організації взаємодії вашої DLL і її керуючого сервера. Одне з рішень полягає у використанні:: PostMessage або:: SendMessage (зауважимо, що тут я посилаються на виклики чистого API, а не виклики MFC!). Там, де можливо використовувати виклик :: PostMessage, краще використовуйте його, а не:: SendMessage, тому що інакше ви можете отримати небезпечні тупикові ситуації. Якщо Ваш Процес у результаті зупиняється, всі інші процеси в системі зупиняться, тому що всі заблоковані на виклик :: SendMessage, який ніколи не повернеться, і ви просто вивели всю систему з ладу з можливістю серйозної втрати даних у важливих для користувача додатках. Це Абсолютно Однозначно Чи не Гарна Ситуація.

    Ви також можете використовувати інформаційні черги в розділяється області пам'яті, але я буду вважати цю тему не включається в рамки даного огляду.

    Ви не можете повернути вказівник із викликів :: SendMessage і:: PostMessage (ми забудемо про можливість передавати назад відносні покажчики в поділювану область пам'яті; це також виходить за рамки цієї статті). Це з-за того, що будь-покажчик, який ви можете створити, буде посилатися або на адресу в DLL (переміщеною в перехоплений процес), або на адресу в перехопленому процесі (Процесі A або Процесі B), і, отже, він буде абсолютно не потрібний у Вашому Процесі. Ви можете повертати лише адресно-незалежну інформацію в WPARAM або LPARAM.

    Я сильно рекомендую використовувати для таких цілей Зареєстровані Віконні Повідомлення (дивіться мій огляд по Управлінню Повідомленнями). Ви можете використати макрос ON_REGISTERED_MESSAGE в MESSAGE_MAP вікна, якому ви надсилаєте повідомлення.

    Основною вимогою тепер є отримання HWND цього вікна. На щастя, це нескладно.

    Перше, що ви повинні зробити - це створити розділяється сегмент даних. Це робиться за допомогою оголошення # pragma data_seg. Виберіть будь-яке гарне мнемонічне ім'я для сегменту даних (воно має бути не довше 8 символів). Просто щоб підкреслити довільність імені, я використав тут своє власне ім'я. Під час викладання я виявив, що якщо я використовую імена виду. SHARE або. SHR, або . SHRDATA, то студенти вважають, що ім'я має значення. А воно не має значення.        

    # pragma   data_seg ( ". JOE")   

    HANDLE   hWnd = NULL;   

    # pragma   dta_seg ()   

    # pragma   comment (linker, "/ section:. JOE, rws")     

    Будь-які змінні, оголошені вами в області дії # pragma, що визначає сегмент даних, будуть розміщені в цьому сегменті даних, за умови, що вони ініціалізований. Якщо ви не вкажете ініціалізатор, змінні будуть розміщені в сегменті даних за замовчуванням, і # pragma не має сили.        

    ПРИМІТКА   

    У той же момент виявляється, що ця   особливість не дозволяє використовувати масиви об'єктів C + + в поділюваному   сегменті даних, тому що в C + + ви не можете ініціалізувати масив   призначених для користувача об'єктів (передбачається, що цим повинні займатися їх   конструктори за умовчанням). Це перетин формальних вимог C + + і   розширень Microsoft, що вимагають наявності ініціалізаторов, виявляється   фундаментальним обмеженням.     

    Директива # pragma comment викликає додавання зазначеного ключа до командного рядка компонувальника на етапі зв'язування. Ви могли б використовувати Project | Settings в VC + + і змінити командний рядок компонувальника, однак важко пам'ятати про необхідність такої дії, коли ви переміщаєте код з місця на місце (і звичайна помилка - забути вибрати All Configurations при зміні установок і, таким чином, успішно налагоджувати, але отримати збій в конфігурації Release). Отже, я виявив, що краще за все розміщувати команду безпосередньо у вихідному файлі. Зауважимо, що використовуваний текст повинен відповідати синтаксису командного ключа Компоновнику. Це означає, що ви не повинні включати в зазначений текст прогалини, інакше компонувальник НЕ опрацює його належним чином.

    Зазвичай ви надаєте певний механізм для установки дескриптора вікна. Наприклад,        

    void SetWindow (HWND w)   

    (   

    hWnd = w;   

    )     

    хоча ця операція, як я покажу далі, часто поєднана з власне установкою хука.

    Приклад: Мишачий Хук

    заголовки (myhook.h)

    Тут повинні бути оголошені функції setMyHook і clearMyHook, але ця вимога роз'яснено в моєму нарисі The Ultimate DLL Header File.        

    # define UWM_MOUSEHOOK_MSG   

    _T ( "UMW_MOUSEHOOK-"   

    "(B30856F0-D3DD-11d4-A00B-006067718D04 }")     

    вихідний файл (myhook.cpp)        

    # include "stdafx.h"   

    # include "myhook.h"      

    # pragma data_seg ( ". JOE")   

    HWND hWndServer = NULL;   

    # pragma data_seg ()   

    # pragma comment ( "linker,/section:. JOE, rws")      

    HINSTANCE hInstance;   

    UINT HWM_MOUSEHOOK;   

    HHOOK hook;      

    // випереджаючий оголошення   

    static LRESULT CALLBACK msghook (int nCode, WPARAM wParam, LPARAM   lParam);      

    /********************************************** ******************   

    * DllMain   

    * Вхід:   

    * HINSTANCE hInst: Дескриптор примірника DLL   

    * DWORD Reason: причина виклику   

    * LPVOID reserved: зарезервовано   

    * Вихід: BOOL   

    * TRUE при успішному   завершення   

    * FALSE при наявності помилок   (не повертається ніколи)   

    * Дія:   

    * ініціалізація DLL.   

    *********************************************** *****************/      

    BOOL DllMain (HINSTANCE hInst, DWORD Reason, LPVOID reserved)   

    (   

    switch (Reason)   

    (/ * причина */   

    //********************************************* *   

    // PROCESS_ATTACH   

    //********************************************* *   

    case DLL_PROCESS_ATTACH:   

    // Збережемо дескриптор екземпляра, тому що він   знадобиться нам пізніше для встановлення хука   

    hInstance = hInst;   

    // Цей код ініціалізує повідомлення   повідомлення хука   

    UWM_MOUSEHOOK =   RegisterWindowMessage (UWM_MOUSEHOOK_MSG);   

    return TRUE;      

    //********************************************* *   

    // PROCESS_DETACH   

    //********************************************* *   

    case DLL_PROCESS_DETACH:   

    // Якщо сервер не зняв хук, знімемо його, тому що ми вивантажується   

    if (hWndServer! = NULL)   

    clearMyHook (hWndServer);   

    return TRUE;   

    )/* причина */   

    )      

    /********************************************** ******************   

    * setMyHook   

    * Вхід:   

    * HWND hWnd: Вікно, чий хук   належить поставити   

    * Вихід: BOOL   

    * TRUE якщо хук успішно   поставлений   

    * FALSE якщо відбулася   помилка, наприклад, якщо хук   

    * вже був встановлений   

    * Дія:   

    * Встановлює хук для   вказаного вікна   

    * Спочатку встановлює хук   перехоплює повідомлення (WH_GETMESSAGE)   

    * Якщо установка пройшла   успішно, hWnd встановлюється як   

    * вікна сервера.   

    *********************************************** *****************/      

    __declspec (dllexport) BOOL WINAPI setMyHook (HWND hWnd)   

    (   

    if (hWndServer! = NULL)   

    return FALSE;   

    hook = SetWindowsHookEx (   

    WH_GETMESSAGE,   

    (HOOKPROC) msghook,   

    hInstance,   

    0);   

    if (hook! = NULL)   

    (/ * удача */   

    hWndServer = hWnd;   

    return TRUE;   

    )/* удача */   

    return FALSE;   

    )// SetMyHook      

    /********************************************** ******************   

    * clearMyHook   

    * Вхід:   

    * HWND hWnd: Вікно, чий хук   повинен бути знятий   

    * Вихід: BOOL   

    * TRUE якщо хук успішно   знятий   

    * FALSE якщо ви передали   невірний параметр   

    * Дія:   

    * Знімає встановлений   хук.   

    *********************************************** *****************/   

    __declspec (dllexport) BOOL   clearMyHook (HWND hWnd)   

    (   

    if (hWnd! = hWndServer)   

    return FALSE;   

    BOOL unhooked =   UnhookWindowsHookEx (hook);   

    if (unhooked)   

    hWndServer = NULL;   

    return unhooked;   

    )      

    /********************************************** ******************   

    * msghook   

    * Вхід:   

    * int nCode: Значення коду   

    * WPARAM wParam: параметр   

    * LPARAM lParam: параметр   

    * Вихід: LRESULT   

    *   

    * Дія:   

    * Якщо повідомлення є   повідомленням про переміщення миші, відправляє його   

    * вікна сервера з   координатами миші   

    * Зауваження:   

    * Функція повинна бути   CALLBACK-функцією, або вона не буде працювати!   

    *********************************************** *****************/      

    static LRESULT CALLBACK msghook (int nCode, WPARAM wParam, LPARAM   lParam)   

    (   

    // If the value of nCode is   <0, just pass it on and return 0   

    // this is required by the   specification of hook handlers   

    // Якщо значення nCode <0, просто передаємо його далі і повертаємо   0   

    // цього вимагає специфікація обробників   хуков   

    if (nCode <0)   

    (/ * передаємо далі */   

    CallNextHookEx (hook, nCode,   

    wParam, lParam);   

    return 0;   

    )/* передаємо далі */      

    // Прочитайте документацію, щоб з'ясувати   сенс параметрів WPARAM і LPARAM   

    // Для хука WH_MESSAGE, LPARAM визначається   як покажчик на структуру MSG,   

    // таким чином наступний код робить цю   структуру доступною      

    LPMSG msg = (LPMSG) lParam;      

    // Якщо це повідомлення про переміщення миші,   або в клієнтської (client), або   

    // в не клієнтської (non-client) області, ми   хочемо повідомити батька про його   

    // виникненні. Зауважимо, що замість   SendMessage використовується PostMessage   

    if (msg-> message == WM_MOUSEMOVE | |   

    msg-> message ==   WM_NCMOUSEMOVE)   

    PostMessage (hWndServer,   

    UWM_MOUSEMOVE,   

    0, 0);      

    // Передаємо повідомлення наступного Хуку   

    return CallNextHookEx (hook, nCode,   

    wParam, lParam);   

    )// msghook     

    Додаток сервера

    У заголовки додайте наступне в секцію protected класу:        

    afx_msg LRESULT OnMyMouseMove (WPARAM, LPARAM);     

    У Фалєєв програми додайте це десь на початку файла:        

    UINT   UWM_MOUSEMOVE =:: RegisterWindowMessage (UWM_MOUSEMOVE_MSG);     

    Додайте наступне у MESSAGE_MAP поза спеціальними коментарів// (AFX_MSG:        

    ON_REGISTERED_MESSAGE (UWM_MOUSEMOVE,   OnMyMouseMove)     

    У файл програми додайте наступну функцію:        

    LRESULT   CMyClass:: OnMyMouseMove (WPARAM, LPARAM)   

    (   

    //   ... тут щось робимо   

    return   0;   

    )     

    Я написав невеликий приклад для демонстрації, але оскільки я втомився створювати функцію глобального хука в n +1 раз, я зробив йому відмінний інтерфейс користувача. Кіт дивиться з вікна і стежить за мишею. Але будьте обережні! Підійдіть достатньо близько до кота, і він схопить миша!

    Ви можете завантажити цей проект і зібрати його. Ключове значення має підпроект DLL; інше - це що використовує її декоративна мішура.

    У цьому прикладі показано кілька інших прийомів, включаючи різні прийоми малювання, використання ClipCursor і SetCapture, вибір регіону, оновлення екрану, і т.д. Таким чином, крім демонстрації використання перехоплює функції, для початківців програмістів цей приклад має цінність у різних аспектах програмування під Windows.

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

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

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

     

     

     

     

     

     

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