Даже и не думайте пользоваться функцией Wow64DisableWow64FsRedirection!
Просто удивительно, насколько опасной может быть функция Wow64DisableWow64FsRedirection. Эта функция позволяет временно отключить перенаправление файловой системы в Wow64. Еще более удивительно, что лишь малая доля разработчиков соглашается менять свой код, даже после подробного объяснения, в чем, собственно, проблема.
В чем состоит опасность? Во-первых, при отключенном перенаправлении файловой системы не работает загрузка 32-х битных системных библиотек. Они, как правило, загружаются из system32 и только перенаправление ввода/вывода спасает ситуацию. Во-вторых, и это самое главное, разработчик очень редко полностью контролирует весь ввод-вывод на участке между Wow64DisableWow64FsRedirection и Wow64RevertWow64FsRedirection.
«Как это возможно?» – спросите вы, «ведь все три строчки кода – вот они, как на ладони». Очень просто. Вот неполный список случаев, когда может происходить неявная загрузка кода:
- Неявный вызов LoadLibrary. Многие функции Win32 вызывают LoadLibrary. Один из примеров – Multimedia API. То же самое делают и другие библиотеки, особенно те, что поддерживают плагины.
- Отложенная загрузка библиотек (Delayed Loading) – это хороший способ ускорить загрузку приложения. Проблема только в том, что загрузка может случиться в любой, в том числе самый неподходящий момент. Все приложения, так или иначе, используют отложенную загрузку, так как ею пользуются ключевые системные библиотеки.
- Так называемые «DLL import forwarders» позволяют сказать загрузчику, что функция «Foo», экспортируемая из «Foo.dll», на самом деле реализована в «Bar.dll». В результате при попытке получить адрес функции «Foo», загрузчик попытается загрузить «Bar.dll». Например:
link /dump /exports c:\Windows\System32\kernel32.dll | findstr forwarded 1 0 AcquireSRWLockExclusive (forwarded to NTDLL.RtlAcquireSRWLockExclusive) 2 1 AcquireSRWLockShared (forwarded to NTDLL.RtlAcquireSRWLockShared) 14 D AddVectoredContinueHandler (forwarded to NTDLL.RtlAddVectoredContinueHandler) 15 E AddVectoredExceptionHandler (forwarded to NTDLL.RtlAddVectoredExceptionHandler) 70 45 CancelThreadpoolIo (forwarded to NTDLL.TpCancelAsyncIoOperation) 86 55 CloseThreadpool (forwarded to NTDLL.TpReleasePool) 87 56 CloseThreadpoolCleanupGroup (forwarded to NTDLL.TpReleaseCleanupGroup) 88 57 CloseThreadpoolCleanupGroupMembers (forwarded to NTDLL.TpReleaseCleanupGroupMembers) 89 58 CloseThreadpoolIo (forwarded to NTDLL.TpReleaseIoCompletion) 90 59 CloseThreadpoolTimer (forwarded to NTDLL.TpReleaseTimer) 91 5A CloseThreadpoolWait (forwarded to NTDLL.TpReleaseWait) 92 5B CloseThreadpoolWork (forwarded to NTDLL.TpReleaseWork) - Взаимодействие с COM объектами очень часто приводит к загрузке дополнительных библиотек. К примеру, это быть вызов QueryInterface или любой другой вызов возвращающий указатель на COM интерфейс.
- Внедрение кода в другой процесс – обычное дело в Windows. Внедренный код может вызвать LoadLibrary в самый неподходящий момент. В этом случае виноват не ваш код, но с точки зрения пользователя упадет именно ваше приложение.
- и т.д. и т.п.
Проблема еще и в том, что код, использующий Wow64DisableWow64FsRedirection, в общем-то, работает в большинстве случаев. Так что разработчик не видит проблемы до тех пор, пока код не будет запущен в незнакомом окружении на машине заказчика. Или пока не выйдет новая версия ОС, используемой библиотеки или клавиатурного шпиона, которая вдруг начала загружать код в том месте, где раньше ничего такого не происходило.
PS. А когда можно использовать Wow64DisableWow64FsRedirection? Единственный поддерживаемый сценарий – вызов CreateFile, обернутый в Wow64DisableWow64FsRedirection и Wow64RevertWow64FsRedirection.
Не лучше ли тогда было бы использовать флаг FILE_FLAG_NO_WOW64_FS_REDIRECTION в вызове CreateFile? А то метания
1. используйте Wow64EnableWow64FsRedirection
2. нет, используйте Wow64DisableWow64FsRedirection/Wow64RevertWow64FsRedirection
3. нет, лучше вообще это все не используйте
выглядят как минимум странно. Типа “росло, росло и выросло” а не как вдумчиво и дальновидно спроектированный механизм.
Лучше. С точки зрения сложившейся ситуации еще лучше было, если бы перенаправление в Wow64 не существовало с самого начала.
Это так и есть. Вдумчиво спроектированная часть – это возможность просто пересобрать 32-х разрядное приложение под 64-е бита и оно, с большой вероятностью, заработает. А вот Wow64DisableWow64FsRedirection – это не очень продуманные попытки обойти побочные эффекты перенаправления файлового ввода вывода.
Другая причина почему не стали использовать флаг для CreateFile заключается в том что по слухам свободных значений флагов уже не осталось.
Бред а не статья.
Во первых, возникает вопрос – а чем же тогда пользоваться? По другому wow никак не обойти, а это иногда ТРЕБУЕТСЯ.
Во вторых Wow64DisableWow64FsRedirection работает только для потока, который ее вызвал.
что мешает вызвать это в другом потоке?
Ну и в третьих, у меня никогда не было проблем с этой функцией за все 5 лет, иначе пришли бы отчеты от пользователей, я бы это заметил.
Также я часто вращаюсь на форумах программистов, там я не встречал подобных отчетов или вопросов.
Поэтому не понятно почему автор так категоричен.
Ну написал бы заметку, но зачем делать такой ахтунг.
1. Иногда за таким “ТРЕБУЕТСЯ” скрывается баг в дизайне. Если это исправить, то “ТРЕБУЕТСЯ” не возникает.
2. Используйте 64-х битный код для доступа к system32.
3. Используйте sysnative
Вызов этой функции в другом потоке ничего радикально не решает. Другой поток точно также может попытаться загрузить DLL в самый неподходящий момент. Можно, конечно создать поток специально для вызова этой функции, но по уровню неудобства это тоже самое, что и не пользоваться ей вовсе.
А вот ко мне последние три года регулярно приходят люди с печальной миной на лице, спрашивая что случилось и как им теперь быть. Как правило, докопаться до того, что причина сбоя – некорректное использование Wow64DisableWow64FsRedirection, у них занимает много времени и сил. Даже докопавшись до причины, они частенько просят исправить Wow64DisableWow64FsRedirection, все еще не понимая, что настоящая причина в том, что они не в курсе неявных зависимостей в их коде.
Ахтунг внимание привлекает. Может задумается кто-нибудь, да глядишь на эти грабли не наступит. Проблема-то, не в самой функции. Проблема в том, что мало кто понимает какие зависимости скрываются на строчками их кода.
>>1. Иногда за таким “ТРЕБУЕТСЯ” скрывается баг >>в дизайне. Если это исправить, то “ТРЕБУЕТСЯ” >>не возникает.
Глупость. прежде чем делать подобные заявления, подумайте.
простая ситуация:
Мне нужно показать список драйверов, показать их размер, версию, дескрипшн, паблишера итп.
Как мне сделать это по другому? Никак.
>> Используйте 64-х битный код для доступа к system32.
>> Используйте sysnative
пишу на Delphi, мне проще написать одну строчку, чем делать те танцы с бубном, которые отнимут массу времени (ведь нужно писать еще и x32 версию а для x64 менять компилятор на Turbo Pascal), и создадут еще больше ошибок, которые сразу трудно заметить.
Для C# придется писать две версии и их же поддерживать.
Так что глупые советы, как и статья.
>>Проблема в том, что мало кто понимает какие >>зависимости скрываются на строчками их кода.
Не знаю среди каких людей вы вращаетесь, но квалифицированный программист, всегда читает документацию, в которой все это расписано, и корректно представляет ближайшие события, которые могут возникнуть.
Иначе, – он начинающий.
Если эта статья для начинающих, тогда можно понять ее категоричность, мол “не играй со спичками, ты еще ребенок”. Но правда тогда не понятна категоричность, где высказывается что ее нельзя вообще использовать, кроме одного случая.
Ну вот это и есть пример бага в дизайне. Такие вещи нужно делать из 64-х битного кода, а не из виртуальной песочницы Wow64. Вы же не будете пытаться показать список драйверов хоста, если ваша программа крутится в гостевой виртуальной машине под VM Ware?
Я прекрасно понимаю что Вам так проще. Однако “проще” не значит “надёжно и корректно”.
Для C# как раз нужна будет одна версия cобранная с /anycpu. А вот для C/C++ – две. Или три если считать Itanium.
Ну да, конечно. Он еще, поди, и ошибок в коде никогда не делает.
не turbo Pascal – Free Pascal конечно.
>>Ну вот это и есть пример бага в дизайне. Такие вещи нужно делать из 64-х битного кода, а не из виртуальной песочницы Wow64. Вы же не будете пытаться показать список драйверов хоста, если ваша программа крутится в гостевой виртуальной машине под VM Ware?
Коммерчески это не выгодно поддерживать 2 версии.
Пусть если бы там действительно были множественные изменения, а из за подобной мелочи в одну строку, никто не будет делать подобное.
+ Здесь нужно отходить от Delphi компилятора, и переходить на FPC. Что создаст массу трудностей и ошибок.
Тот кто понимает к чему это может привести, просто сделает так, что бы исключить такую возможность – это правильный и логичный путь, а не компиляция в x64 – тем более в моем случае.
>>Я прекрасно понимаю что Вам так проще. Однако >>“проще” не значит “надёжно и корректно”.
повторюсь, то что предлагаете вы, создаcт еще больше ошибок и проблем, особенно у тех, кто пользуется Delphi. да и не будет ни кто это делать, о чем вы и говорите в статьте (а чему вы удивляетесь?). Не выгодно это, и обойти это можно проще простого, хотя бы потоком (в большинстве случаев пускается допольнительный поток для обработки данных).
>>Ну да, конечно. Он еще, поди, и ошибок в коде никогда не делает.
все зависит от опыта. Ошибки конечно будут, но не критичные.
К чему я это все говорю, – к тому что это не так критично, как вы говорите.
Из тех 3 вариантов, которые вы предложили, подходит только один для большинства (первый тот вообще убрать следует – т.к. не вариант):
Это второй – Используйте 64-х битный код для доступа к system32.
Проще обойти и сделать безопасную работу, чем писать и поддерживать второй экземпляр программы.
А ведь программа в моем случае поддерживает все Win от 98 в одном экзепляре.
поэтому это не так категорически критично для большинства людей, как вы заявляете.
вариант с sysnative отходит по понятной причине.
А никто и не говорил, что будет легко.
Исправление ошибок дизайна – это практически всегда дорого.
Причем здесь не одна ошибка, а целый мешок. Во-первых сам факт того, что Wow64 виртуализирует system32. Это ошибка с точки зрения автора 32-х битного приложения, составляющего список драйверов.
Во-вторых, альтернативный вариант реальности, в котором Wow64 никогда бы не виртуализировала system32 – это тоже ошибка в дизайне с точки зрения тех, кто был бы вынужден исправлять множество программ.
В-третьих, нежелание и невозможноть разработчиков перейти на x64 – это тоже баг в дизайне, который они закладывают в свои приложения. Он может добавить проблем, а может и пронесет. Я могу только порекомендовать не закладывать мину в дизайн.
С каждым разом я удивляюсь все меньше и меньше. Такого насмотрелся…
Вашими бы устами, да мед пить. Разное бывает. Кроме того, для обработки данных код тоже нужен.
Я вполне подробно расписал когда именно случаются неприятности. Вы полагаете, что такие ситуации возникают крайне редко? Ну так разработчики, у которых таки возникли проблемы тоже так считали. Более того, в некоторых случаях все вообще печально, так как код, запрещающий перенаправление, они не контролируют. Приходится изворачиваться.