Запуск просмотрщика картинок из Windows XP на современных Windows

Есть у меня папка со старыми картинками, которые я собирал в нулевых. Переношу с компьютера на компьютер вместе со всеми моими файлами при каждом апгрейде, изредка захожу поностальгировать. Но каждый раз меня немного смущало то, что стандартный просмотрщик Windows 7 не показывал GIF-анимацию, хотя память мне подсказывала, что во времена XP анимация показывалась без проблем. При очередном приступе ностальгии я всё же решил запустить просмотрщик из XP на Windows 7. После преодоления ряда препятствий, я теперь уверен — GIF-анимация там действительно поддерживалась! А главное — теперь я могу смотреть свою старую папку с картинками в аутентичном интерфейсе просмотрщика картинок Windows XP, что создаёт более подходящую атмосферу =)

Скачать: shimgvw_xp32.7z (включает бинарник и исходный код лаунчера, плюс shimgvw.dll из англоязычной Windows XP SP3).

Как это было сделано?

Стандартный просмотрщик картинок в Windows XP — это не обычное приложение. Он находится в библиотеке shimgvw.dll, и запустить его напрямую нельзя — нужен как минимум rundll32 (указание пути к существующему файлу картинки обязательно):

rundll32 c:\windows\system32\shimgvw.dll,ImageView_Fullscreen c:\test.gif

Но этот трюк не срабатывает при попытке запуска shimgvw.dll из Windows XP на Windows 7 и новее — shimgvw.dll не может загрузиться, ему для корректной работы нужен режим совместимости с Windows XP. Этого можно добиться, установив соответствующий режим совместимости для копии rundll32, но совместимость с XP подразумевает под собой повышение прав приложения до максимальных, что вызывает появление диалога UAC при каждом запуске, чего хотелось бы избежать.

Небольшой танец с отладчиком позволил выяснить причину — в процессе загрузки shimgvw.dll впоследствии пытается импортировать несколько функций из shunimpl.dll, где хранятся устаревшие функции оболочки, и последняя по умолчанию отказывается загружаться, если ATOM «FailObsoleteShellAPIs» отсутствует (наличие этого ATOM разрешает загрузку библиотеки, но тогда все устаревшие функции просто возвращают код с ошибкой). Режим совместимости с XP, помимо прочего, устанавливает этот ATOM, поэтому просмотрщик и запускается.

Было решено написать небольшой лоадер, который добавляет ATOM «FailObsoleteShellAPIs», спрашивает какую картинку открыть (если в параметрах не был передан путь), и дальше передаёт управление в shimgvw.dll. Просмотрщик работает нормально (т.е. изначальная задача была выполнена), так что я не стал исследовать, что же за устаревшие функции импортируются из shunimpl.dll и в каких случаях они используются — видимо, ничего критичного для работы просмотрщика они не делают.

Поскольку для реализации лоадера не нужно ничего кроме небольшого числа функций WinAPI, я решил в качестве эксперимента собрать проект при помощи Clang без использования рантайма (ранее я всегда использовал MSVC для таких танцев). Таким образом, получился исполняемый файл размером 14 килобайт, из которых 9 килобайт ушло на иконку. Если кому-то также нравится делать мини-приложения без зависимостей от нестандартных библиотек — данный проект может послужить небольшим примером, как это делается при помощи Clang.

  1. #1
    Argo

    Очень интересно, Евгений. Всегда с интересом читаю ваши статьи на тему реверсинга и здесь и на "хабре". Даже сам стал немного приобщаться к этому делу. Некоторое время я просто компилировал на СИ, СИ++ свои простенькие примеры и изучал как они устроены в дизассемблере. А тут думаю, а не попробовать ли мне усложнить задачу. Захотелось в каком-нибудь чужом коде найти какую-нибудь интересующую меня функцию и проанализировать её. Хочу взять обычный калькулятор из Win7-32bit и найти там интересующую меня функцию. Но вот пока не могу сообразить как мне это пограмотнее или правилнее сделать. Может посоветуете что-нибудь, Жень? Мы же когда нажимаем кнопку функции, то запускаем определенный участок кода, который по окончании работы записывает в память данные. Как можно опираясь на эти события (запись в память и нажатие кнопки) найти нужный участок кода. Или в таких случаях долго и упорно анализируют дизассемблированый код и только после этого уже находят нужный участок. Как тут можно облегчить себе задачу, чтобы не делать двойную работу? Спасибо вам заранее за ответ.

  2. #2
    VEG Автор

    Argo, стандартный калькулятор вероятно написан с использованием достаточно низкоуровневого кода, так что нужно искать типичный код работы с окнами на голом Win32 API. Найдите код, который создаёт окно, посмотрите какая там указана процедура для обработки сообщений от окна, изучите её код... Если нужно — можно на помощь призвать отладчик, посмотреть как выполняется тот или иной код в реальных условиях. Как правило, изучать вообще весь код не нужно. Но чтобы двигаться в правильном направлении, нужно представлять, как то приложение, которое вы исследуете, могло бы быть написано.

  3. #3
    shikulja

    Блиин… а проперло все таки..
    Заметил только что подсказки на английском. с английской версии выдрали?)
    И вопрос а ассоциировать его можно?

  4. #4
    shikulja

    Хочу свою голубую мечту рассказать.. как то я попробывал вин8.1 и мне она не особо понравилась.. 7ка была и стабильней и привычней.. но диспетчер задач не давал мне спокойно спать.. очень уж мне он понравился. Сейчас уже сижу на 10ке.. и приятно представлять что все таки кто нибудь таки сможет портировать диспетчер из 10ки на 7ку.
    Почитал вашу статью, понял что это нереальная задача) Видимо пришло время проститься с 7кой)

  5. #5
    VEG Автор

    shikulja, в статье указано, что файл shimgvw.dll взят из англоязычной Windows XP SP3. Если вам нужен русский язык — можете сами взять этот же файл из русскоязычной версии XP, и запускать его при помощи shimgvw.exe из статьи.

    Что касается диспетчера задач из Windows 10, то в нём нет ничего такого, чего нельзя было бы сделать на Windows 7. Но Process Explorer, который можно скачать с сайта Microsoft, всё равно лучше.

  6. #6
    Argo

    Найдите код, который создаёт окно, посмотрите какая там указана процедура для обработки сообщений от окна, изучите её код...

    VEG, т.е. нужно сначала мысленно в голове на основе знаний, опыта и логики локализовать примерный участок программы. Затем для выяснения каких-нибудь сомнений, просмотреть это место под отладчиком. Только так? А если не получается вычислить в голове примерное место в программе - можно ли воспользоваться какими-то косвенными ориентирами? Как я уже писал выше - по записи данных в память или по нажатию на кнопки интерфейса? Затем локализовать примерное место, а потом попробовать разобраться на этом участке кода. Ведь есть же программы, которые мониторят обращения к API-функциям. Может и с памятью и обработчиками событий(кнопками) можно такой-же финт провернуть? Хотя интуиция мне подсказывает, что VEG ответит что-то наподобие: ну вы и размечтались... :).

  7. #7
    VEG Автор

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