Форум: "Основная";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2013.03.22;
Скачать: [xml.tar.bz2];




Вниз

AV при использовании юнита Variants 


Сергей М. ©   (2010-03-09 11:15) [0]

Столкнулся с неожиданной засадой при использовании юнита Variants.

Целевая задача :

реализовать в D7EE DLL-модуль (использующий юнит Classes и собираемый без использования Run-Time пакетов), загружаемый в адр.пространства создаваемых системой процессов с использованием документированного механизма автозагрузки указанием модуля в списке
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs

Проблема:

при создании и инициализации процессов ряда приложений в ходе инициализации загружаемого в их АП моего DLL-модуля возбуждается AV-исключение с дословным сообщением постоянного содержания в появляющемся диалоговом окне:

Инструкция по адресу 0x7C918FEA обратилась по адресу 0x00000010. Память не может быть "written".

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

В ходе анализа причин и условий возникающей проблемы выявлены следующие факты:

1. Исключение возбуждается в коде модуля ntdll.dll в теле ф-ции RtlInitializeCriticalSection()

2. Исключение возбуждается как минимум при условии явного (прямым указанием в USES) или неявного (фигурирантом в USES иных юнитов в составе VCL и RTL, например, Classes) использования в проекте моего DLL-модуля юнита Variants

3. Исключение возбуждается только при условии сборки моего DLL-модуля без использования Run-Time пакетов.

4. Инициализация критической секции фигурирует в тексте юнита Variants в секции INITIALIZATION.

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

Текст проекта DLL-модуля для тестирования:


library TestDll;

uses
 Variants;

{$R *.res}

begin

end.


Пример списка приложений, запуск которых приводит к возникновению вышеописанной проблемы:

delphi7.exe
mmc.exe
paint.exe
skype.exe
virtualbox.exe
TeamViewer.exe (TeamViewer4)

Пример списка приложений, запуск которых НЕ приводит к возникновению вышеописанной проблемы:

iexplore.exe
firefox.exe
procexpr.exe
calc.exe
wordpad.exe
excel.exe
notepad.exe
TeamViewer.exe (TeamViewer3)
FAR.exe

Воспроизводима ли проблемная ситуация в других условиях (иная ОС, иная Delphi IDE) ?



Leonid Troyanovsky ©   (2010-03-09 12:25) [1]


> Сергей М. ©   (09.03.10 11:15)  

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

Самые общие рекомендации by MS:
http://www.microsoft.com/whdc/driver/kernel/DLL_bestprac.mspx

Сталкивался давно, еще в ту пору, когда variants не выделялся
в отдельный юнит. Проблема была та же, в неявной инициализации,
т.е., в момент загрузки твоей библиотеки, необходимые для
поддержки variant библиотеки еще не загружены.

Если поддержка variants необходима, то,  видимо, нужно
добиваться более поздней инициализации. Not tested.

--
Regards, LVT.



Сергей М. ©   (2010-03-09 12:49) [2]


> Leonid Troyanovsky ©   (09.03.10 12:25) [1]


Спасибо за отклик.


> в момент загрузки твоей библиотеки, необходимые для
> поддержки variant библиотеки еще не загружены


Есть ли соображения на тему о каких модулях может идти речь в этом случае ?

Если спотык действительно происходит в момент вызова юнитом Variants
функции
kernel32.InitializeCriticalSection(LVarTypeSync)
то это вроде бы не противоречит рекомендациям MS, к тому же kernel32+ntdll в этот момент уже гарантированно загружены и инициализированы

Согласно

http://support.microsoft.com/kb/q197571/

спотык происходит в момент выполнения инициализации user32.dll при ее загрузке целевым процессом

Я что-то не вижу пока явной связи между инициализацией КС и user32 ..



Сергей М. ©   (2010-03-09 13:04) [3]


> Leonid Troyanovsky


И вот еще какая любопытная картина - тот же, к примеру, calc.exe в таблице импорта имеет  зависимость от user32, однако я не наблюдаю в его АП  своего DLL-модуля с Variants ни при использовании ею ран-тайм пакетов ни без использования оных.
При этом , как я уже сказал, calc.exe грузится и инициализируется безо всяких проблем и в том и в другом случае.



Игорь Шевченко ©   (2010-03-09 13:07) [4]

Я не совсем уверен, что проблема в InitializeCriticalSection - нечему там вызывать AV, на первый взгляд.

Но следует учесть, что загрузка такой DLL тянет за собой загрузку oleaut32 с возможным вызовом функций из нее, а допустимо ли загружать эту DLL в момент инициализации user32 - это вопрос, на который я не могу ответить.



Leonid Troyanovsky ©   (2010-03-09 13:17) [5]


> Сергей М. ©   (09.03.10 12:49) [2]


Вот библиотеки екзешника (консоль) с uses variants:

770E0000-C:\WINDOWS\system32\oleaut32.dll
77160000-C:\WINDOWS\system32\ole32.dll
77BA0000-C:\WINDOWS\system32\msvcrt.dll
77C00000-C:\WINDOWS\system32\GDI32.dll
77C50000-C:\WINDOWS\system32\RPCRT4.dll
77D00000-C:\WINDOWS\system32\user32.dll
77DA0000-C:\WINDOWS\system32\advapi32.dll
77E40000-C:\WINDOWS\system32\kernel32.dll
77F40000-C:\WINDOWS\system32\ntdll.dll
--------------------------

А вот без оного:

77BA0000-C:\WINDOWS\system32\msvcrt.dll
77C00000-C:\WINDOWS\system32\GDI32.dll
77C50000-C:\WINDOWS\system32\RPCRT4.dll
77D00000-C:\WINDOWS\system32\user32.dll
77DA0000-C:\WINDOWS\system32\advapi32.dll
77E40000-C:\WINDOWS\system32\kernel32.dll
77F40000-C:\WINDOWS\system32\ntdll.dll

Видимо, то, что ole* не есть safe to use in the initialization of the AppInit DLLs.

Кто их там тянет через variants разобрать сложно.

--
Regards, LVT.



Smile   (2010-03-09 13:19) [6]

Google для строки поиска: "Инструкция по адресу 0x7C918FEA обратилась по адресу 0x00000010" находит достаточное количество ссылок, в общем то, на первый взляд, не связанных с юнитом Variants



Сергей М. ©   (2010-03-09 13:30) [7]


> Игорь Шевченко ©   (09.03.10 13:07) [4]


Спасибо за отклик.

> не совсем уверен, что проблема в InitializeCriticalSection

Да, ты прав, отказ в иной ф-ции, но тем не менее связанной с ожиданием некоей КС в ф-ции ntdll.RtlpWaitForCriticalSection

Иллюстрация картины:

Скриншот диал.окна с сообщением об AV (14 кб):
http://ifolder.ru/16756773

Скриншот соответствующего Delphi Debugger CPU Window (188 кб)
http://ifolder.ru/16756784

Посмотри, пож., вероятно это даст какой-то вектор для поиска кустов где притаилась засада..



Сергей М. ©   (2010-03-09 13:33) [8]


> Leonid Troyanovsky ©   (09.03.10 13:17) [5]


Да и я уже нутром чувствую, что следы ведут в ole ..
Но пока не могу нарисовать в голове четкую картинку происходящего при  этом ..



Leonid Troyanovsky ©   (2010-03-09 13:55) [9]


> Сергей М. ©   (09.03.10 13:33) [8]

> Но пока не могу нарисовать в голове четкую картинку происходящего
> при  этом ..

Для пищи добавлю выжимку из той ссылки

You should never perform the following tasks from within DllMain:
• Call LoadLibrary or LoadLibraryEx (either directly or indirectly). This can cause a deadlock or a crash.
• Synchronize with other threads. This can cause a deadlock.
• Acquire a synchronization object that is owned by code that is waiting to acquire the loader lock. This can cause a deadlock.
• Initialize COM threads by using CoInitializeEx. Under certain conditions, this function can call LoadLibraryEx.
• Call the registry functions. These functions are implemented in Advapi32.dll. If Advapi32.dll is not initialized before your DLL, the DLL can access uninitialized memory and cause the process to crash.
• Call CreateProces. Creating a process can load another DLL.
• Call ExitThread. Exiting a thread during DLL detach can cause the loader lock to be acquired again, causing a deadlock or a crash.
• Call CreateThread. Creating a thread can work if you do not synchronize with other threads, but it is risky.
• Create a named pipe or other named object (Windows 2000 only). In Windows 2000, named objects are provided by the Terminal Services DLL. If this DLL is not initialized, calls to the DLL can cause the process to crash.
• Use the memory management function from the dynamic C Run-Time (CRT). If the CRT DLL is not initialized, calls to these functions can cause the process to crash.
• Call functions in User32.dll or Gdi32.dll. Some functions load another DLL, which may not be initialized.
• Use managed code.

Т.е., при загрузке User32 кто-то требует ole.
Осталось найти, возможно, где-то в дебрях system.

Но, проще отказаться от uses variants, IMHO.

--
Regards, LVT.



Игорь Шевченко ©   (2010-03-09 13:58) [10]

Сергей М. ©   (09.03.10 13:30) [7]

Попробуй то же самое, без AppInit_DLL, это даст однозначный ответ - можно вызывать oleaut32 в момент инициализации user32 или нет.



Игорь Шевченко ©   (2010-03-09 14:01) [11]

Leonid Troyanovsky ©   (09.03.10 13:55) [9]


> Для пищи добавлю выжимку из той ссылки


Ну и я добавлю:

http://transl-gunsmoker.blogspot.com/2009/01/dllmain_04.html
http://transl-gunsmoker.blogspot.com/2009/01/dllmain.html
http://transl-gunsmoker.blogspot.com/2009/01/dllmain_7983.html



Сергей М. ©   (2010-03-09 14:03) [12]


> Leonid Troyanovsky ©   (09.03.10 13:55) [9]


Похоже что следы ведут в этот пункт:

• Call the registry functions. These functions are implemented in Advapi32.dll. If Advapi32.dll is not initialized before your DLL, the DLL can access uninitialized memory and cause the process to crash.

Сейчас проверю. не лезет ли процесс неявно в реестр при посредничестве ole32 (у нее в таблице импорта фигурируют ф-ции из состава advapi32 для доступа к реестру)



Сергей М. ©   (2010-03-09 14:11) [13]

Нет, предположения [12] не подтвердились ...



Игорь Шевченко ©   (2010-03-09 14:18) [14]

Сергей М. ©   (09.03.10 14:11) [13]

Казалось бы, чего проще - взять windbg, проверить, какие DLL загружены/инициализированы на этапе загрузки твоей DLL, пройти по вызовам (он это умеет делать, для подобных случаев самый подходящий отладчик).

к AdvApi32, кстати, обращается system.pas при определении, нужно ли загружать ресурсные DLL и инициализировать особым образом FPU



Сергей М. ©   (2010-03-09 14:31) [15]


> Игорь Шевченко ©   (09.03.10 14:18) [14]


> Казалось бы, чего проще


Да не хотелось бы возиться).. Хотя может и придется.
Впрочем можно и без Windbg - думаю что и привычный дельфийский встроенный отладчик справится не хуже

Но проще, видимо, отказаться от геморроя с AppInit_Dlls в пользу иных подходящих механизмов, ибо, по правде говоря, залезть мне нужно в АП конкретного GUI процесса, а не вовсе подряд.

Да и к тому же MS предупреждает о возможной неподдержке AppInit_Dlls в перспективных версиях своих ОС.

Кстати, в Win7 и Vista механизм AppInit_Dlls еще поддерживается или уже нет ? Под рукой их сейчас нет, так что поверю на слово)



Игорь Шевченко ©   (2010-03-09 14:51) [16]

Сергей М. ©   (09.03.10 14:31) [15]


> Кстати, в Win7 и Vista механизм AppInit_Dlls еще поддерживается
> или уже нет ? Под рукой их сейчас нет, так что поверю на
> слово)


Гугля у тебя тоже под рукой нет ?

http://www.microsoft.com/whdc/driver/install/AppInit-Win7.mspx

там в документе, предлагаемом к скачиванию, есть фраза, что появился ключ
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT \CurrentVersion\Windows\LoadAppInit_DLLs типа DWORD, который определяет поведение.



Сергей М. ©   (2010-03-09 15:20) [17]


> Игорь Шевченко ©   (09.03.10 14:51) [16]


Спасибо, понятно.



Сергей М. ©   (2010-03-09 16:25) [18]


> Игорь Шевченко


Игорь, я еще немного понаглею, с твоего позволения ?)

Структура параметра ApplicationGoo ключа в разделе
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
документирована где-либо ?



Игорь Шевченко ©   (2010-03-09 16:36) [19]

Сергей М. ©   (09.03.10 16:25) [18]

Попробуй, начав отсюда, поискать:

http://www.alex-ionescu.com/?p=39



Сергей М. ©   (2010-03-09 16:41) [20]


> Игорь Шевченко ©   (09.03.10 16:36) [19]


Благодарю.



brother ©   (2010-03-10 04:35) [21]

Сергей, если разберешься с проблеммой до конца и найдешь решение: буду благодарен за публикацию "отчета" в этом топике...



Сергей М. ©   (2010-03-10 13:29) [22]


> brother ©   (10.03.10 04:35) [21]


Ну проблема тут налицо - недопустимая попытка загрузки oleaut32.dll и ole32.dll в момент выполнения загрузчиком инициализации user32.dll

Изменить порядок загрузки и инициализации документированными способами при использовании AppInit_Dlls , наверно, не удастся.

Так что, если функциональность этих модулей не нужна, придется тащить в свой проект копии как минимум пяти юнитов: system.pas, sysutils.pas, Variants.pas, VarUtils.pas и Classes.pas и править их, выкорчевывая везде и всюду все упоминания об ole32 и oleaut32, начиная со статического импорта, что довольно муторно, но вполне осуществимо.



brother ©   (2010-03-10 13:46) [23]

а если перехватить загрузку oleaut32.dll и ole32.dll и подсовывать их "пустышки" не проще?



Сергей М. ©   (2010-03-10 13:57) [24]

Во-первых, как ты себе это мыслишь при использовании механизма AppInit_Dlls ?

Во-вторых, что значит "пустышки" ?



brother ©   (2010-03-10 14:05) [25]

чет я протупил... пора видимо спать...



Leonid Troyanovsky ©   (2010-03-10 14:43) [26]


> Игорь Шевченко ©   (09.03.10 16:36) [19]

> http://www.alex-ionescu.com/?p=39

Любопытно, что автор также столкивался с похожей на тему проблемой.

http://groups.google.com/group/microsoft.public.platformsdk.shell/browse_thread/thread/e4fd80b723e961fe/b75d6f0a55b180ea

--
Regards, LVT.



Игорь Шевченко ©   (2010-03-10 14:47) [27]

Leonid Troyanovsky ©   (10.03.10 14:43) [26]


> Любопытно, что автор также столкивался с похожей на тему
> проблемой.


6 лет назад :)

Сергей М. ©   (10.03.10 13:29) [22]

Проще отказаться от вариантов в подобной DLL



Anatoly Podgoretsky ©   (2010-03-10 15:11) [28]

Правильнее, но если сел на иглу, то что делать?
Варианты это чужое, из Бейсика.



Сергей М. ©   (2010-03-10 16:23) [29]


> Игорь Шевченко ©   (10.03.10 14:47) [27]


> Проще отказаться от вариантов в подобной DLL


Ну почему же ?
Выковырять загрузку oleaut32 и ole32 из дельфийских юнитов не так уж сложно, хоть и муторно.
Конечно после этого об "универсальности" решения можно забыть, но .. было бы ради чего)
Вариантов-то документированного инжекта кроме  AppInit_Dlls с гулькин нос - hooks да remote threads.

Хуки - это, конечно, просто и универсально, но и недостатки у этого решения нельзя не учитывать.

Удал.треды - это нерадужная перспектива борьбы с софтом антивирусного фронта, да и не триггерный это метод, что ощутимо ограничивает область его применения.



Leonid Troyanovsky ©   (2010-03-10 17:45) [30]


> Сергей М. ©   (10.03.10 16:23) [29]

> Хуки - это, конечно, просто и универсально, но и недостатки
> у этого решения нельзя не учитывать.

Недостатков у AppInit_Dlls видится гораздо больше :)

--
Regards, LVT.



Сергей М. ©   (2010-03-11 10:10) [31]


> Leonid Troyanovsky ©   (10.03.10 17:45) [30]


Не возражаю.

Глоб.хуки в триггерном режиме (hInst=HookLib.dll, ThreadId=0) всем хороши, кроме одного - экземпляры HookLib торчат "мертвым грузом" в АП не интересующих меня процессов, и выгрузить это "мертвый груз" еще на этапе инициализации HookLib , вернув системе из DllMain ненулевой результат, насколько мне известно, не представляется возможным.

Или я ошибаюсь ?



Leonid Troyanovsky ©   (2010-03-11 12:22) [32]


> Сергей М. ©   (11.03.10 10:10) [31]

> Глоб.хуки в триггерном режиме (hInst=HookLib.dll, ThreadId=0)
> всем хороши, кроме одного - экземпляры HookLib торчат "мертвым
> грузом" в АП не интересующих меня процессов, и выгрузить
> это "мертвый груз" еще на этапе инициализации HookLib ,
> вернув системе из DllMain ненулевой результат, насколько
> мне известно, не представляется возможным.

Если интересуют конкретные процессы/потоки, то именно
за них и стоит цепляться.

Мне нравится комбинация WH_GETMESSAGE & PostThreadMessage.
Иногда достаточно установить хук, расчитанный на обработку
единственного сообщения, после чего он снимается.

Я как-то приводил тут пример.
http://delphimaster.net/view/4-1259064819/

--
Regards, LVT.



Сергей М. ©   (2010-03-11 14:22) [33]


> Leonid Troyanovsky ©   (11.03.10 12:22) [32]



> Если интересуют конкретные процессы/потоки, то именно
> за них и стоит цепляться.


Это да.
Но если требуется получить в своем хук-модуле управление в целевом потоке целевого процесса во время самого первого вызова им GetMessage, то эта метода не годится.
Ибо на момент когда мне становится известен ThreadId нужного мне потока, он уже за время с начала своего старта успел выполнить некие операции, ДО выполнения которых мне собственно и требовалось получить управление.



Leonid Troyanovsky ©   (2010-03-11 14:44) [34]


> Сергей М. ©   (11.03.10 14:22) [33]

> Ибо на момент когда мне становится известен ThreadId нужного
> мне потока, он уже за время с начала своего старта успел
> выполнить некие операции, ДО выполнения которых мне собственно
> и требовалось получить управление.

Ну, и делай некие операции только в нужных потоках.
Для "закрепления" длл - LoadLibrary (нек. проблемы с LoadLibrary
из хуковой процедуры были лишь в 95).
В примере это есть.

По окончанию же обработки - сними глобальный хук.
И/или, можно разбить на 2 библиотеки: одна небольшая, хуковая,
загружена постоянно, а вторая рабочая - для избранных.

--
Regards, LVT.



Сергей М. ©   (2010-03-11 15:21) [35]


> Leonid Troyanovsky ©   (11.03.10 14:44) [34]


> делай некие операции только в нужных потоках


Ты, видимо, меня не понял.

"Нужный" поток обнаруживается по факту обнаружения "нужного" процесса. Согласен ?

Документированных методологий обнаружения старта "нужного" процесса (с целью  инжекта в его АП моего модуля и соотв.получения управления в нужном мне его потоке)  всего две:
- периодическое сканирование снэпшотов с целью обнаружения "нужного" процесса и и т.д.;
- использование в kernel-mode-драйвере соответствующей PsNotify-функции, извещающей о рождении "нужного" процесса;

Обе методологии мне хорошо известны и вопрос по ним не стоит.

Вопрос же, как понимаешь, стоит о максимально возможно раннем получении управления в одном из потоков (как правило - в основном) процесса "нужного" приложения.

AppInit_Dlls-метод для этого подходит как нельзя лучше, но .. вот с таким вот геморроем в вышеозначенном случае) ..



Leonid Troyanovsky ©   (2010-03-11 18:05) [36]


> Сергей М. ©   (11.03.10 15:21) [35]

> получении управления в одном из потоков (как правило - в
> основном) процесса "нужного" приложения.

Года 3 назад здесь пару раз обсуждался вопрос о получении управления
над первичным потоком до входа в WinMain.

Вот одно из обсуждений:
http://delphimaster.net/view/1-1179920037
Еще одно, к сожалению, обрезалось.

Если я ничего не запамятовал, то после полной инициализации процесса
его первичный поток попадает в alertаble state, в котором опустошает
очередь APC перед входом в WinMain.
Т.е., если после обнаружение стартовавшего потока послать ему
QueueUserAPC, то она выполнится первичным потоком в нужное время.

Вот простой пример:

// Включить звуковую схему. Not Windows.MessageBeep!

procedure TForm1.Button1Click(Sender: TObject);
var
 pi: TProcessInformation;
 si: TStartupInfo;
 p : Pointer;
 d:DWord;
begin
  Fillchar(si, SizeOf(si),0);
  CreateProcess(
       nil,
       "notepad.exe",
       nil,
       nil,
       False,
       CREATE_SUSPENDED,
       nil,
       nil,
       si,
       pi
   );
   p := GetProcAddress( GetModuleHandle( "user32.dll" ), "MessageBeep");
   QueueUserAPC(p,  pi.hThread, MB_OK);
   ResumeThread(pi.hThread);
   CloseHandle(pi.hThread);
   CloseHandle(pi.hProcess);
end;

Насчет AppInit_Dlls, т.е. посылки APC в момент инициализации user32.dll
ничего не скажу, не знаю (да, и пробовать не хочу :)
Но, IMHO, уважаемая Riply рассказывала об успешных опытах в
том числе и с оным.

--
Regards, LVT.



Leonid Troyanovsky ©   (2010-03-11 18:18) [37]


> Leonid Troyanovsky ©   (11.03.10 18:05) [36]

> Т.е., если после обнаружение стартовавшего потока послать

В смысле: при своевременном обнаружении.
Sorry.

--
Regards, LVT.



xayam ©   (2011-05-04 20:54) [38]


> delphimaster.net/view/1-1179920037
> Еще одно, к сожалению, обрезалось.

Исправил. 13 ответов, как и в самом архиве...




Форум: "Основная";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2013.03.22;
Скачать: [xml.tar.bz2];




Наверх





Память: 0.85 MB
Время: 0.242 c
3-1284795538      vhm                   2010-09-18 11:38  2013.03.22  
Подключение к MySQL


15-1353529805     Юрий                  2012-11-22 00:30  2013.03.22  
С днем рождения ! 22 ноября 2012 четверг


15-1343645986     Es                    2012-07-30 14:59  2013.03.22  
TcxComboBox, уведомление о Delete?


15-1342015306     Дмитрий С             2012-07-11 18:01  2013.03.22  
Apache mod_rewrite


9-1193831870      savyhinst             2007-10-31 14:57  2013.03.22  
Gravity