Пикник на обочине или не ходите, дети, в DllMain гулять, а то ноги оторвёт.

January 30th, 2008

Точка входа в DLL, так же как и точка входа в программу, - это очень специальное место. Зона. В Зоне действуют свои правила касательно того, что можно делать, а что делать нельзя. В Зоне можно инициализировать локальные данные DLL, можно создавать критические секции. В Зоне нельзя динамически загружать другие Модули или создавать потоки. Любой Сталкер знает и следует правилам Зоны. Все остальные рано или поздно нарушают правила и расплачиваются за это.

Что делает Зону особенной? Иные утверждают, что во всем виноват Загрузчик. Загрузчик единственный, кто понимает язык зависимостей между модулями. Он говорит с модулями, загружает их и вызывает из точки входа. Но Загрузчик слаб. Он не в силах совладать с Модулями, нарушающими правила Зоны.

Модули коварны и злы. Они стремятся окружить себя другими Модулями, от которых они зависят. Они любят создавать циклические зависимости между собой. Они загружают другие DLL в ответ на DLL_PROCESS_ATTACH и вызывают функции из непроинициализированных Модулей. Модули пытаются замаскировать и приумножить свое коварство. Они прикрываются статусом “Delay-Loaded DLL” и расставляют ловушки в конструкторах и деструкторах статических объектов.

Это люди сделали их такими. Люди нарушили правила Зоны. И теперь они расплачиваются за это каждый раз, когда идут в Зону.

На прошлой неделе произошел очередной несчастный случай. Access Violation прошил насквозь lodctr.exe:

А всё из-за того, что loadperf решил, что ему всё можно и попытался зарегистрировать источник событий для Event Log прямо из DllMain. Этот фокус ему удавался до поры до времени, пока advapi32 не стала “Delay-Loaded” . Как только это произошло, вызов RegisterEventSourceW стал приводить к её загрузке, что поломало граф зависимостей Загрузчика и вылилось в попытку использования неинициализированной критической секции в rpcrt4 до того, как была вызвана точка входа rpcrt4!DllMain.

Кто будет следующей жертвой?

P.S. Сслылка по теме: http://msdn2.microsoft.com/en-us/library/ms682583.aspx

, ,

  1. Alexey
    January 30th, 2008 at 23:43 | #1

    Здравсвуйте, Алексей.
    А у MS есть какие-нибудь анализаторы (как с уязвимостями) которые бы отслеживали подобные моменты?
    >пока advapi32 не стала “Delay-Loaded”
    а почему она такой стала?

    • January 31st, 2008 at 09:46 | #2

      А у MS есть какие-нибудь анализаторы (как с уязвимостями) которые бы отслеживали подобные моменты?

      Есть, но от них мало пользы. Чтобы от них был толк, нужно сначала разорвать все явные кольцевые зависимости (статически связанные DLL), а затем вручную найти все случаи когда из DllMain явно или опосредованно вызывается LoadLibrary. Только после этого такой инструмент будет полезен, так как с его помощью можно будет отлавливать “нарушителей”.

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

      а почему она такой стала?

      Эксперименты с черной магией.

  2. February 2nd, 2008 at 11:58 | #3

    Душещипательно.

    Вообще все эти игрища с бубном вокруг Disable/EnableThreadLibraryCalls и проч. в свое время много крови попортили. и seh_translator, который если не путаю ничего иначе чем в DLL_PROCESS_ATTACH не работает.

    Не люблю Winapi - слишком много trade-off в нем видимо наделали, теперь все это наружу торчит….

    • February 2nd, 2008 at 18:17 | #4

      _set_se_translator это совсем другой коленкор. Совать его в DLL не очень разумно. Как по мне так им вообще лучше не пользоваться.

      • February 4th, 2008 at 09:54 | #5

        а как жить без него? __finally{} и try… finally{} вместе не живут, вроде бы

        • February 4th, 2008 at 20:43 | #6

          Живут, просто в разных функциях. _set_se_translator просто устанавливает фильтр, который будет вызван из самого верхнего (считаем, что стек растет вниз) __try/__except. Никто не мешает обернуть свой поток в __try/__except и воспользоватться там аналогичным фильтром.

          Причина по которой не стоит использовать _set_se_translator в DLL - это то, что он устанавливает глобальный фильтр для всех потоков, а DLL , по определению, всему потоками не владеет.

          • February 4th, 2008 at 22:25 | #7

            Причина по которой не стоит использовать _set_se_translator в DLL - это то, что он устанавливает глобальный фильтр для всех потоков, а DLL , по определению, всему потоками не владеет.

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

            • February 5th, 2008 at 02:51 | #8

              Я так понимаю, что веселье начинается, когда выгружаешь dll, а в потоке где-то затем произойдет SEHовское исключение?

              • February 5th, 2008 at 08:35 | #9

                Ага. Еще могут быть всякие нехорошие эффекты, когда код главного модуля рассчитывает поймать SEH исключение, а тут мы на белом коне ему подсовываем C++ исключение.

  3. .net lover
    February 3rd, 2008 at 14:38 | #10

    Оффтопик.

    А вы на какой позиции в MS? Senior или Junior?

    Это к тому, что довольно периодично проскакивают сообщения о наборах программистов в Фирму (src — blogs.gotdotnet.ru ). Но после вашего блога, начинаешь сомневаться в проф. пригодности :)

    И какой у вас опыт вообще? CV где-то можно посмотреть? :)

    • February 3rd, 2008 at 15:29 | #11

      Не приходите к нам, пожалуйста. У нас всё плохо.

      P.S. (c) privet.com

  4. Shrike
    February 4th, 2008 at 13:42 | #12

    Описанное как-то касается .net кода?

    • February 4th, 2008 at 20:37 | #13

      Напрямую - нет, так как .NET все эти тонкости от программиста скрывает.

  1. January 30th, 2008 at 21:07 | #1
Comments are closed.