Отгадка

В комментариях к загадке ответ был дан почти сразу. Действительно, обработчик отложенной загрузки DLL попытается загрузить «version.dll» по имени, а не по полному пути. В результате загрузчик будет искать библиотеку, пользуясь правилами по умолчанию. Номер два в этом списке поиск DLL в system32, что при отключенном перенаправлении файловой системы, равноценно попытке загрузить 64-х битную DLL в 32-х битный процесс. Бум.

Но проблема на самом деле не так уж и проста, как может показаться с самого начала. Первая сложность здесь в том, что загрузка DLL происходит неявно для программиста. Из кода никак не понять, что библиотека выбрана для отложенной загрузки. В достаточном большом проекте это означает, что «любой залетный программист» из соседней группы может запретить перенаправление, исправляя какой-то свой баг. Причем какое-то время всё может работать, так как нужная библиотека могла загрузиться ранее. А потом, по закону подлости, это вылезет во время презентации продукта заказчику.

Причем это не единственный способ скрытой загрузки DLL. Таких способов масса: создание COM объекта, вызов CreateProcess или ShellExecute, установка хука на оконные сообщения и т.д. и т.п.

Вторая проблема заключается в том, что реализовать работающее/альтернативное решение либо сложно, либо неочевидно. Показательно, что из двух предложенных решений проблемы из предыдущего топика только одно решало проблему полностью, а второе - только маскировало проблему. С другой стороны, даже правильное решение не работает однообразно для 32-х разрядного и 64-х разрядного кода. 32-х битное приложение должно использовать «sysnative» для доступа к настоящему каталогу «system32». 64-х разрядные приложения ничего не знают о «sysnative» и подобное перенаправление для них не работает. Да, я знаю, что Microsoft - зло. В данном конкретном случае я полностью согласен. :-)

Ещё один пример, на форуме разработчиков Far Manager обсуждался плагин, который должен был запрещать перенаправление файловой системы раз и навсегда при старте приложения. Именно с целью попасть в настоящий «system32». Не знаю, чем это закончилась, но помню аргументацию в стиле «попробовал - работает». Действительно работает, пока не сломается.

Третья сложность заключается в неочевидных взаимоотношениях LoadLibrary и перенаправления файловой системы. Если запустить 32-х разрядную версию fileverison.exe, чтобы получить версию ядра (файл «%windir%\system32\ntoskrnl.exe»), то случится странное. Не смотря на перенаправление файловой системы, 32-х разрядный процесс успешно загрузит ресурсы из 64-х битного «ntoskrnl.exe». Хотя никакого «ntoskrnl.exe» в «%windir%\syswow64», куда перенаправляется процесс, нет.

Как так получается? Очень просто. GetFileVersionInfo вызывает LoadLibraryEx с флагом LOAD_LIBRARY_AS_IMAGE_RESOURCE. Этот флаг указывает, что файл загружается только для извлечения ресурсов. Если указан этот флаг и первая попытка найти файл с заданным именем окончилась неудачей, LoadLibraryEx попробует отключить перенаправление файловой системы и повторит попытку. В приеме выше, 64-х битный «ntoskrnl.exe» будет загружен во время второй попытки. Добавлю, что этот механизм работает только для модулей «system32» и своему появлению он обязан проблемам с совместимостью к каким-то из приложений.

Вот такие вот ужасы.

comments powered by Disqus