Программирование на C++ глазами хакера

         

Работа с чужыми окнами


Я регулярно получаю письма с вопросами типа: "Как уничтожить чужое окно или изменить что-то в нем?" В принципе, эта задача легко решается с помощью уже знакомой нам функции FindWindow. Но если необходимо изменить множество окон (или даже все), то нужно использовать другой метод поиска, который мы сейчас рассмотрим. Для начала напишем программу, которая будет искать все окна на Рабочем столе и изменять их заголовки.

3.1. Результат работы программы I SeeYou

На 3.1 показан вид нескольких окон после запуска программы, которую нам сейчас предстоит написать. Как видите, все заголовки изменились на "I See You".

Создайте в Visual C++ новый проект Win32 Project и в нем какой-нибудь пункт меню, при выборе которого будет запускаться программа, реализующая нашу задачу.

В функции WndProc добавьте следующий код обработки пункта меню:

case ID_MYCOMMANDS_ISEEYOU: while (TRUE) { EnumWindows(EnumWindowsWnd, 0); }

В приведенном коде ID_MYCOMMANDS_ISEEYOU — это идентификатор пункта меню. Цикл while будет выполняться бесконечно (TRUE никогда не станет равным FALSE). Внутри цикла вызывается функция EnumWindows. Это WinAPI -функция, которая используется для перечисления всех открытых окон.

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

В качестве функции обратного вызова будет использоваться функция EnumWindowsWnd. Так что, каждый раз, когда EnumWindows найдет окно, будет выполняться код, написанный в EnumWindowsWnd. Этот код выглядит следующим образом:

BOOL CALLBACK EnumWindowsWnd( HWND hwnd, // handle to parent window // (Указатель на главное окно) LPARAM lParam // application-defined value // (значение, определенное приложением) ) { SendMessage(hwnd, WM_SETTEXT, 0, LPARAM(LPCTSTR("I See You"))); return TRUE; }

Количество параметров, их тип и тип возвращаемого значения должны быть именно такими:


идентификатор найденного окна типа HWND;

значение типа LPARAM, которое вы можете использовать в своих целях.

Если что-то изменить, то функция станет несовместимой с EnumWindows. В таких случаях, чтобы не ошибиться, я беру имя функции с параметрами прямо из файла помощи по WinAPI и вставляю в свой код. Лучше лишний раз проверить, чем потом долго искать опечатку или случайную ошибку. То же самое я советую делать и вам. В данном случае нужно открыть файл помощи в разделе Enumwindows и перейти по ссылке, указывающей на формат функции обратного вызова.



Итак, у нас есть идентификатор найденного окна. Такой параметр мы уже использовали много раз, когда прятали или перемещали окно, теперь научимся изменять его заголовок. Для этого используем уже знакомую функцию SendMessage, которая посылает сообщения Windows. Вот ее параметры:

идентификатор окна, которому надо отослать сообщение, — передан в качестве параметра функции-ловушки EnumWindowsWnd, и он равен идентификатору найденного окна;

тип сообщения — WM_SETTEXT, заставляет окно сменить текст заголовка;

параметр для данного сообщения должен быть 0;

новое имя окна.

Чтобы программа продолжила поиск следующего окна, ей надо вернуть значение TRUE.

Примечание
Исходный код этого примера вы можете найти на компакт-диске в каталоге \Demo\Chapter3\ISeeYou.
Давайте немного усложним пример. Для начала изменим функцию EnumWindowsWnd до следующего вида:

BOOL CALLBACK EnumWindowsWnd( HWND hwnd, // handle to parent window LPARAM lParam // application-defined value ) { SendMessage(hwnd, WM_SETTEXT, 0,LPARAM(LPCTSTR("I See You"))); EnumChildWindows(hwnd,EnumChildWnd,0); return TRUE; }

Здесь после отправки сообщения вызывается функция EnumChildWindows, которая определяет все окна, принадлежащие главному окну. У нее три параметра:

идентификатор окна, дочерние элементы которого нужно искать, — указываем окно, которое уже нашли;

адрес функции обратного вызова, которая будет запускаться каждый раз, когда найдено дочернее окно;



просто число, которое может быть передано в функцию обратного вызова.

Как вы можете заметить, работа функции EnumChildWnd похожа на EnumWindowsWnd, только если вторая ищет окна во всей системе, то первая — внутри указанного окна. Образец такого окна можно увидеть на 3.2.

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

В принципе, программу можно считать законченной, но у нее есть один недостаток, о котором нельзя умолчать. Допустим, что программа нашла окно и начала перечисление в нем дочерних окон, а в этот момент окно закрывают. Программа пытается послать сообщение найденному окну об изменении текста, а его уже не существует, и происходит ошибка выполнения. Чтобы этого избежать, в самом начале функций обратного вызова нужно поставить проверку на правильность полученного идентификатора окна:

if (h==0) return TRUE;



З.2. Результат работы программы lSeeYou2

Вот теперь приложение можно считать завершенным и абсолютно рабочим.

Помните, что лишних проверок не бывает. Если хотите, чтобы ваш код был надежным, то нужно проверять все, что может вызвать проблемы. В данной книге я иногда буду пренебрегать этим правилом, чтобы не усложнять и не запутывать программу, но постараюсь указывать на те участки кода, которые нуждаются в пристальном внимании.

Примечание
Исходный код этого примера вы можете найти на компакт-диске в каталоге \Demo\Chapter3\ISeeYou2.
В примерах главы 2 я старался писать весь код в цикле обработки сообщений. Таким образом, программа сначала выполняла предписанные действия, а потом обрабатывала сообщения системы. Для завершения такого приложения программу было достаточно просто закрыть. В данном же случае запускается бесконечный цикл, который выполняется вне обработчика сообщений системы. Таким образом, наш цикл будет постоянно работать, а сообщения системы обрабатываться не будут, т. к. на них не хватит процессорного времени. Такую программу будет сложно закрыть. Пользователь поймет суть проблемы, но избавиться от нее сможет только через снятие задачи, потому что окно приложения не будет реагировать на любые действия пользователя.



Этот побочный эффект может оказаться полезным для невидимых окон. А если код отображения главного окна программы просто удален, тогда обработчик сообщений окна останется "без работы", его тоже можно будет убрать.

Теперь добавим еще один простой, но очень интересный эффект. Будем перебирать все окна и сворачивать их. Тогда функция EnumWindowsWnd (вызывается, когда найдено очередное окно) будет выглядеть следующим образом:

BOOL CALLBACK EnumWindowsWnd( HWND hwnd, // handle to parent window LPARAM lParam // application-defined value ) { ShowWindow(hwnd, SW_MINIMIZE); return TRUE; }

Здесь в вызываемой функции ShowWindow в качестве второго параметра указывается флаг SW_MINIMIZE, который и заставляет найденное окно свернуться. Будьте осторожны при запуске программы. Функция FindWindow ищет все окна, в том числе и невидимые.

Примечание
Исходный код этого примера вы можете найти на компакт - диске в каталоге \Demo\Chapter3\RandMinimize.

Содержание раздела