Всё сдал! - помощь студентам онлайн Всё сдал! - помощь студентам онлайн

Реальная база готовых
студенческих работ

Узнайте стоимость индивидуальной работы!

Вы нашли то, что искали?

Вы нашли то, что искали?

Да, спасибо!

0%

Нет, пока не нашел

0%

Узнайте стоимость индивидуальной работы

это быстро и бесплатно

Получите скидку

Оформите заказ сейчас и получите скидку 100 руб.!


Хуки и DLL

Тип Реферат
Предмет Информатика и программирование
Просмотров
411
Размер файла
31 б
Поделиться

Ознакомительный фрагмент работы:

Хуки и 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.


Нет нужной работы в каталоге?

Сделайте индивидуальный заказ на нашем сервисе. Там эксперты помогают с учебой без посредников Разместите задание – сайт бесплатно отправит его исполнителя, и они предложат цены.

Цены ниже, чем в агентствах и у конкурентов

Вы работаете с экспертами напрямую. Поэтому стоимость работ приятно вас удивит

Бесплатные доработки и консультации

Исполнитель внесет нужные правки в работу по вашему требованию без доплат. Корректировки в максимально короткие сроки

Гарантируем возврат

Если работа вас не устроит – мы вернем 100% суммы заказа

Техподдержка 7 дней в неделю

Наши менеджеры всегда на связи и оперативно решат любую проблему

Строгий отбор экспертов

К работе допускаются только проверенные специалисты с высшим образованием. Проверяем диплом на оценки «хорошо» и «отлично»

1 000 +
Новых работ ежедневно
computer

Требуются доработки?
Они включены в стоимость работы

Работы выполняют эксперты в своём деле. Они ценят свою репутацию, поэтому результат выполненной работы гарантирован

avatar
Математика
История
Экономика
icon
159599
рейтинг
icon
3275
работ сдано
icon
1404
отзывов
avatar
Математика
Физика
История
icon
156450
рейтинг
icon
6068
работ сдано
icon
2737
отзывов
avatar
Химия
Экономика
Биология
icon
105734
рейтинг
icon
2110
работ сдано
icon
1318
отзывов
avatar
Высшая математика
Информатика
Геодезия
icon
62710
рейтинг
icon
1046
работ сдано
icon
598
отзывов
Отзывы студентов о нашей работе
63 457 оценок star star star star star
среднее 4.9 из 5
ИжГТУ имени М.Т.Калашникова
Сделала все очень грамотно и быстро,автора советую!!!!Умничка😊..Спасибо огромное.
star star star star star
РГСУ
Самый придирчивый преподаватель за эту работу поставил 40 из 40. Спасибо большое!!
star star star star star
СПбГУТ
Оформил заказ 14 мая с сроком до 16 мая, сделано было уже через пару часов. Качественно и ...
star star star star star

Последние размещённые задания

Ежедневно эксперты готовы работать над 1000 заданиями. Контролируйте процесс написания работы в режиме онлайн

Решить задачи по математике

Решение задач, Математика

Срок сдачи к 14 дек.

только что

Чертеж в компасе

Чертеж, Инженерная графика

Срок сдачи к 5 дек.

только что

Выполнить курсовой по Транспортной логистике. С-07082

Курсовая, Транспортная логистика

Срок сдачи к 14 дек.

1 минуту назад

Сократить документ в 3 раза

Другое, Информатика и программирование

Срок сдачи к 7 дек.

2 минуты назад

Сделать задание

Доклад, Стратегическое планирование

Срок сдачи к 11 дек.

2 минуты назад

Понятия и виды пенсии в РФ

Диплом, -

Срок сдачи к 20 янв.

3 минуты назад

Сделать презентацию

Презентация, ОМЗ

Срок сдачи к 12 дек.

3 минуты назад

Некоторые вопросы к экзамену

Ответы на билеты, Школа Здоровья

Срок сдачи к 8 дек.

5 минут назад

Приложения AVA для людей с наступающим слуха

Доклад, ИКТ

Срок сдачи к 7 дек.

5 минут назад

Роль волонтеров в мероприятиях туристской направленности

Курсовая, Координация работы служб туризма и гостеприимства

Срок сдачи к 13 дек.

5 минут назад

Контрольная работа

Контрольная, Технологическое оборудование автоматизированного производства, теория автоматического управления

Срок сдачи к 30 дек.

5 минут назад
6 минут назад

Линейная алгебра

Контрольная, Математика

Срок сдачи к 15 дек.

6 минут назад

Решить 5 кейсов бизнес-задач

Отчет по практике, Предпринимательство

Срок сдачи к 11 дек.

7 минут назад

Решить одну задачу

Решение задач, Начертательная геометрия

Срок сдачи к 7 дек.

9 минут назад

Решить 1 задачу

Решение задач, Начертательная геометрия

Срок сдачи к 7 дек.

10 минут назад

Выполнить научную статью. Юриспруденция. С-07083

Статья, Юриспруденция

Срок сдачи к 11 дек.

11 минут назад

написать доклад на тему: Процесс планирования персонала проекта.

Доклад, Управение проектами

Срок сдачи к 13 дек.

11 минут назад
planes planes
Закажи индивидуальную работу за 1 минуту!

Размещенные на сайт контрольные, курсовые и иные категории работ (далее — Работы) и их содержимое предназначены исключительно для ознакомления, без целей коммерческого использования. Все права в отношении Работ и их содержимого принадлежат их законным правообладателям. Любое их использование возможно лишь с согласия законных правообладателей. Администрация сайта не несет ответственности за возможный вред и/или убытки, возникшие в связи с использованием Работ и их содержимого.

«Всё сдал!» — безопасный онлайн-сервис с проверенными экспертами

Используя «Свежую базу РГСР», вы принимаете пользовательское соглашение
и политику обработки персональных данных
Сайт работает по московскому времени:

Вход
Регистрация или
Не нашли, что искали?

Заполните форму и узнайте цену на индивидуальную работу!

Файлы (при наличии)

    это быстро и бесплатно