Blog. Just Blog

Перехват буфера обмена на Ассемблере

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Перехват буфера обмена на Ассемблере
Перехват буфера обмена на Ассемблере

Сегодня разберем интересную тему - перехват буфера обмена. Применений этому перехвату можно найти много: менеджеры буфера обмена, хранящие последние несколько скопированных текстов; программы, выполняющие заданные действия, если в буфере обмена появилось кодовое слово; кейлоггеры, перехватывающие пароли и тексты пользователя; одно время были популярны трояны, подменяющие в буфере обмена номера электронных кошельков на свои собственные. Область применения любой технологии, как обычно, ограничивается только вашей фантазией. Но это все лирика, пора переходить к программированию.

Для того, чтобы ваше приложение узнавало об изменении содержимого буфера обмена, оно должно встроиться в цепочку обработчиков буфера обмена ("наблюдателей"). Существует два основных способа это сделать. Первый способ поддерживается операционными системами от Windows 2000 и выше, он основан на использовании функции SetClipboardViewer.
  1. wm_init:
  2.         ; Добавить наше окно в список наблюдателей
  3.         invoke  SetClipboardViewer, [hwnddlg]
  4.         mov     [hNextW], eax
  5.         ...
  6.  
  7. wm_close:
  8.         ; Убрать наше окно из списка наблюдателей
  9.         invoke  ChangeClipboardChain, [hwnddlg], [hNextW]
  10.         ...
При инициализации диалогового окна-перехватчика вызывается функция SetClipboardViewer, которая добавляет его в цепочку наблюдателей за буфером обмена. После этого при любом изменении буфера обмена, окну будет посылаться сообщение WM_DRAWCLIPBOARD. Но тут есть один важный момент. Сразу после того, как наше окно было встроено в цепочку наблюдателей, возвращается хэндл окна, следующего в цепочке. Когда наше приложение заканчивает работу с буфером обмена, оно обязано послать сообщение WM_DRAWCLIPBOARD следующему в цепочке окну.
  1.         ; Отправить сообщение следующему окну
  2.         invoke  SendMessage, [hNextW], WM_DRAWCLIPBOARD, 0, 0
А при закрытии окна-перехватчика, надо обязательно уведомить об этом систему, сообщив ей через функцию ChangeClipboardChain хэндл закрываемого окна и хэндл окна, следующего по цепочке наблюдателей. Точно так же должны поступать и остальные перехватчики. При любом изменении цепочки, всем ее членам системой отправляется сообщение WM_CHANGECBCHAIN. Наше приложение также должно его обработать примерно следующим образом:
  1.         ; Удаляется известное нам окно?
  2.         mov     eax,[wparam]
  3.         cmp     eax,[hNextW]
  4.         jne     unknown_handle
  5.  
  6.         ; Запомнить новый хэндл следующего обработчика
  7.         mov     eax,[lparam]
  8.         mov     [hNextW],eax
Если удаляется обработчик, который мы знали в качестве следующего в цепочке, то нам надо запомнить у себя новый хэндл окна, которое теперь будет являться следующим. Если удаляемое окно нам неизвестно, то просто оповещаем об изменении цепочки следующее окно, отправив ему сообщение WM_CHANGECBCHAIN с теми же параметрами, что были получены.
  1. unknown_handle:
  2.         ; Передать сообщение следующему окну
  3.         invoke  SendMessage, [hNextW], WM_CHANGECBCHAIN, [wparam], [lparam]
Этот способ универсальный, но несколько сложен в реализации. Также слабым звеном тут является предположение, что все программы-перехватчики, встроенные в цепочку наблюдения за буфером обмена, всегда работают корректно и всегда отсылают другим участникам правильные сообщения. На деле это может быть совсем не так. Операционная система, конечно, пытается корректировать такие ситуации, но это не всегда получается. Если в исходнике из приложения к статье закомментировать строчку с отправкой сообщения WM_DRAWCLIPBOARD и запустить две копии программы, то перехватывать буфер обмена сможет только одна из них.

Второй, более современный способ, доступен в операционных системах, начиная с Windows Vista. Здесь используется функция AddClipboardFormatListener.
  1. wm_init:
  2.         ; Добавить наше окно в список наблюдателей
  3.         invoke  AddClipboardFormatListener,[hwnddlg]
  4.         ...
  5.  
  6. wm_close:
  7.         ; Убрать наше окно из списка наблюдателей
  8.         invoke  RemoveClipboardFormatListener,[hwnddlg]
  9.         ...
Немного похоже на первый способ, но гораздо проще в реализации. После добавления окна в список наблюдателей, при каждом изменении буфера обмена, окну будет приходить сообщение WM_CLIPBOARDUPDATE. Все просто, никаких цепочек, никого не надо ни о чем уведомлять, достаточно только убрать окно из списка наблюдателей при его закрытии при помощи функции RemoveClipboardFormatListener.

При написании статьи я попробовал одновременно запустить два приложения, одно из которых использует первый (старый) способ перехвата, а другой второй способ (новый). Как ни странно, в этом случае победил первый способ перехвата. Второму приложению не доставалось ничего до того момента, как было закрыто первое. Дальше я запустил параллельно две копии первого перехватчика, они оба отрабатывали перехват без каких-либо проблем. А вот при запуске двух копии приложения с новым перехватом, данные доставались только тому, которое было запущено позднее. Такие дела.

Осталось выяснить еще один вопрос. Как узнать, что именно содержится в буфере обмена? Например, нас интересуют только текстовые строчки, а картинки и всякие эксельные таблицы должны пролетать мимо. Для этого есть функция IsClipboardFormatAvailable.
  1.         ; В буфере обмена находится текст?
  2.         invoke  IsClipboardFormatAvailable,CF_TEXT
  3.         or      eax,eax
  4.         jz      not_text
В приложении пример двух программ, реализующих оба метода перехвата буфера обмена. Если в буфер обмена копируется текст, то он сразу же появится в окне перехватчика.

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

Clipboard.Hook.Demo.zip (4,644 bytes)


Поделиться ссылкой ВКонтакте Поделиться ссылкой на Facebook Поделиться ссылкой на LiveJournal Поделиться ссылкой в Мой Круг Добавить в Мой мир Добавить на ЛиРу (Liveinternet) Добавить в закладки Memori Добавить в закладки Google
Просмотров: 4997 | Комментариев: 4

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
Марат (24.08.2013 в 12:52):
...Спасибо Вам...
brute (23.08.2013 в 20:04):
да стебусь я по случаю пятницы) Давно хотел на любимом PB переводчик в стиле QDictionary запилить(самодельные словари есть), поэтому статья весьма полезна!
ManHunter (23.08.2013 в 19:46):
А зачем копировать текст из окна самой программы? Можно добавить проверку владельца окна, но это уже выходит за рамки примера.
brute (23.08.2013 в 19:01):
первый пример не работает, если копировать текст из самого окна примера. Если запустить две копии первой программы и копировать текст в одной из них, то корректно отображает его только одна.. жаль, что второй пример на XP не запускается..:)

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2017
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.06 сек. / MySQL: 2 (0.0033 сек.) / Память: 4.5 Mb
Наверх