Дизассемблируем Windows Explorer — отключаем группировку на панели задач

от автора

Однажды, в 2009 году, вышла Windows 7. В то время я сидел на Висте, которая притормаживала на моем стареньком компьютере, и я решил пересесть на семерку сразу после ее выхода.

Первое, на что я обратил внимание после установки — новая панель задач. А конкретнее — тот факт, что она группирует кнопки по программе, к которой они принадлежат.

Сразу же полез в настройки, чтобы отключить это безобразие, и с удивлением обнаружил, что группировка не отключается. Наиболее близкий к желаемому вариант, Never combine, кнопки все же группирует.

Я думал, что как и мне, многим это не понравится, и был уверен, что спустя неделю-две в интернете всплывет решение этой проблемы. Но я так ничего путного и не нашел, и понял, что придется действовать самому.

Под катом:

  • Подробная демонстрация того, как можно самостоятельно подправить принцип работы такого системного процесса, как explorer, под себя, используя отладчик OllyDbg.
  • Готовое решение описанной выше проблемы.

Приступаем

Для понимания описанного ниже процесса, желательно иметь базовое знание ассемблера.

Для того, чтобы отучить панель задач от группировки, нам понадобится OllyDbgбесплатный проприетарный 32-битный отладчик уровня ассемблера для операционных систем Windows, предназначенный для анализа и модификации откомпилированных исполняемых файлов и библиотек, работающих в режиме пользователя (ring-3) © Wikipedia.

Повторить процесс можно на ОС Windows 7 или Windows 8. К сожалению, на данный момент имеется только 32-битная версия OllyDbg, так что если у вас 64-битная ОС, повторить следующие действия у вас не получится (в таком случае можно использовать виртуальную машину).

Инсталляция OllyDbg

OllyDbg — портативная программа, и в инсталляции не нуждается. Создайте папку с правами записи, и перепишите туда файлы с этого архива.

Настройка Microsoft Symbol Server

Microsoft Symbol Server — Майкрософтовский сервер отладочных символов, благодаря которым мы, кроме чистого ассемблера, будем видеть еще и названия функций и переменных. Это очень помогает при анализе кода.

Для этого нужно сделать следующее:

  • Скачайте этот архив, и поместите его содержание в папку программы OllyDbg.
    Что это за файлы?

    • symsrv.dll — DLL файл Майкрософта, предназначен для работы с их сервером отладочных символов. Подписан Майкрософтом.
    • symsrv.yes — пустой файл. Обозначает, что вы согласны с условиями предоставленных услуг.
    • symsrv.ini — исключает все файлы кроме тех, которые начинаются на exp — для нашего explorer.exe. Иначе, если будут качаться все символы, придется очень долго ждать, да и не нужно оно нам для данной демонстрации.
  • Включите OllyDbg, откройте настройки (Alt+O), и пометьте опцию Demangle symbolic names. Эта опция декодирует символических имена, делая их более читаемые для нас, людей.
    Скриншот

  • В настройках, откройте страницу Debugging data, и пометьте Allow access to Microsoft Symbol Server. Затем создайте папку для символов, и выберете ее в настройках, как на скриншоте.
    Скриншот

  • Перезапустите OllyDbg.

Присоединение процесса explorer

Как известно, панель задач — часть процесса Windows Explorer. Запускаем OllyDbg, и выбираем File -> Attach…. Затем, выбираем explorer и жмем на Attach.

Скриншоты


Attach, с английского — присоединять — то есть мы присоединяем наш отладчик к проводнику для его отладки.

Затем ждем, пока все модули загрузятся. Это может занять несколько минут, и в это время проводник не будет реагировать. После завершения загрузки в строке состояния справа будет написано Paused на желтом фоне — то есть процесс приостановлен. Жмем F9 чтобы возобновить работу процесса.

Внимание, исключения: если процесс приостанавливается, и в строке состояния появляется надпись Exception xxxxxxxx (как на скриншоте ниже), нажмите Shift+F9 чтобы передать исключение по назначению.

Скриншот

Обзор функций проводника

Теперь проводник работает под надзором нашего отладчика, OllyDbg. Если связь с сервером отладочных символов прошла как следует, мы можем взглянуть на функции проводника.

Сначала, откроем модуль процесса explorer в окошке CPU отладчика. Для этого откроем окошко модулей (буква E на голубом фоне), нажмем правой кнопкой на Explorer, и выберем View code in CPU.

Скриншот

Затем, нажмем правой кнопкой на код, и выберем Search for -> Names. Перед нами появится список функций, присутствующих в проводнике. Для более удобной работой со списком, отсортируем его по имени, скопируем и вставим в текст в любимый текстовой редактор.

Скриншоты


Итак, нам нужно отключить группировку. Логично начать поиск со слова group. В глаза сразу бросаются два класса: CTaskBtnGroup и CTaskGroup. Вместе у этих двух классов 131 функций — не очень много для беглого просмотра названий.

Мне сложно сказать, можно ли найти нужную нам функцию, всего лишь изучая названия, так как я уже неплохо с многими из них знаком. В любом случае, нужная нам функция — CTaskGroup::DoesWindowMatch, название которой переводится как совпадает ли окошко, и делает она именно это — отвечает, подходит ли окошко к определенной группе.

Скриншот

Просмотр выбранной функции

Мы нашли функцию с интересным названием, теперь давайте посмотрим, чего она из себя представляет. Вернемся в OllyDbg, нажмем на Ctrl+G и перейдем на адрес функции (тот восьмизначный номер в начале строки, в нашем случае 00973629).

P.S. Советую включить подсветку Jumps and calls, благодаря которой четко видны вызовы функций и условные/безусловные прыжки.

Скриншот

Итак, вот наша функция:

Столбцы, слева на право: адрес, байты, команды (ассемблер), комментарии.
Голубым цветом помечены функции, желтым — переходы. На них мы и сосредоточимся.

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

Дальше мы видим три перехода. Первый перепрыгивает через второго и третьего. Второй и третий переходят куда-то подальше — давайте посмотрим, куда…

Первый
В глаза сразу бросается WinAPI функция ILIsEqual, которая сравнивает две структуры. После некоторого анализа становится ясно, что сравниваемые структуры связаны с группами. Наша цель — отменить любую группировку, так что пропатчим код так, чтобы проводник думал, что структуры не ровны.

Для этого, пропишем безусловный прыжок JMP, как на скриншоте сверху.

Второй
Как и в первом разе, сразу видим WinAPI функцию для сравнения, на этот раз текста — CompareStringOrdinal. Опять же, после экспериментирования становится ясно, что и это сравнение связано с группами. На этот раз сравниваются так называемые Application ID — идентификатор аппликации, по которым панель задач группирует кнопки.

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

Пробуем

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

Попробуем открыть несколько копий блокнота, и увидим, что они не группируются:

Получилось 🙂

Так как мы изменили код только в памяти запущенного процесса, наша модификация продержится только до завершения процесса.

Бонус — декомпилированная версия функции CTaskGroup::DoesWindowMatch

Несмотря на то, что оригинальный язык данного кода — C++, ниже превидена самописная декомпиляция на чистом C.

/* return: S_OK if there's a match, E_FAIL otherwise  *pnMatch: 1: AppId matches, ITEMIDLIST is missing 2: AppId matches, ITEMIDLIST doesn't match 3: ITEMIDLIST matches 4: hWnd already exists (pTaskItem recieves the CTaskItem) */  HRESULT CTaskGroup_DoesWindowMatch(void /*CTaskGroup*/ *this,  	/*IN*/ HWND hCompareWnd, /*IN*/ ITEMIDLIST *pCompareItemIdList, /*IN*/ WCHAR *pCompareAppId,  	/*OUT*/ int *pnMatch, /*OUT*/ void /*CTaskItem*/ **pTaskItem) { 	void /*CTaskItem*/ *CompareWndTaskItem; 	int nMatch; 	HRESULT hr;  	nMatch = 0;  	hr = this->lpVtbl->GetItemFromWindow(this, hCompareWnd, &CompareWndTaskItem); 	if(SUCCEEDED(hr)) // if an item for this window already exists 	{ 		if(pTaskItem) 		{ 			*pTaskItem = NULL; 			IUnknown_Set(pTaskItem, CompareWndTaskItem); 		}  		nMatch = 4; 		CompareWndTaskItem->lpVtbl->Release(CompareWndTaskItem); 	} 	else 	{ 		if(!(this->SomeFlags & 0x80000000)) 		{ 			if(pCompareItemIdList && this->pItemIdList && ILIsEqual(pCompareItemIdList, this->pItemIdList) != 0) 			{ 				nMatch = 3; 				hr = S_OK; 			} 			else if(pCompareAppId && CompareStringOrdinal(this->pAppId, -1, pCompareAppId, -1, TRUE) == CSTR_EQUAL) 			{ 				hr = S_OK;  				if(pCompareItemIdList && this->pItemIdList) 					nMatch = 2; 				else 					nMatch = 1; 			} 		} 	}  	*pnMatch = nMatch;  	return hr; } 

Готовое решение

Программа 7+ Taskbar Tweaker, автор которой являюсь я, умеет отключать группировку как глобально, так и выборочно по Application ID. Кроме этого, имеются в наличие еще несколько интересных настроек панели задач.

Скриншот

Заключение

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

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

ссылка на оригинал статьи http://habrahabr.ru/post/174593/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *