В свободное время я занимаюсь разработкой Snoop'a. Это отличное подспорье WPF разработчикам, которое не имеет хороших бесплатных аналогов в мире Silverlight. Snoop внедряет свою сборку в WPF процесс и раскладывает его по полочкам.Мне стало любопытно: можно ли внедрить произвольную сборку в исполняющийся сильверлайт процесс извне браузера?
Насколько мне известно, все существующие программы, внедряющие код в сильверлайт, делают это посредством перехвата и модификации .xap файла до загрузки приложения. Это неплохой способ, однако он обладает рядом недостатков:
1. Нельзя подключиться к уже запущенному приложению без перезагрузки страницы
2. Перехват .xap файла зачастую ограничивает среду выполнения плагина Internet Explorer'ом. Для универсального перехватчика нужно использовать/писать http-сниффер.
Я же хочу предложить способ, который позволяет подключиться к уже запущенному приложению и внедриться в него без перезагрузки страницы, без модификации .xap файла и без ограничений на браузер. Прежде чем мы начнем, позвольте представить сегодняшнего open-source гостя:
slinject
Эта консольная программа — плод программистского любопытства. Она позволяет выбрать исполняющийся Silverlight процесс и внедрить в него Silverlight сборку (тыкните для большего разрешения):

Бинарники можно скачать здесь, а исходный код на C# доступен на github'e. Прежде чем вы начнете внедряться, программ�� нужно установить, выполнив команду:
slinject --installЭто одноразовая операция, требующая администраторских прав. После установки инжектора внедряться можно под любым пользователем.
Подождите, не уходите! Или как это работает
Как это часто бывает, идея очень простая. Подключиться отладчиком к Silverlight процессу и выполнить команду Assembly.Load(). Это всегда было можно сделать из отладчика Visual Studio, но можно ли это сделать из своей программы?
Сильверлайт устанавливается с рядом интересных библиотек и инструментов, которые, к сожалению, имеют либо отвратительную документацию, либо никакой. Одна из таких библиотек — dbgshim.dll. Именно через эту дверь ходят отладчики в мир Silverlight'a. Visual Studio использует ее, так почему бы и нам не воспользоваться лазейкой? Правда, почему бы и нет? Отстутвие хорошей документации нельзя назвать хорошим аргументом. А вот хорошим вызовом для любопытных — можно.
Итак, после нескольких ночей блуждания по
Проблема
Нужно было найти метод в Silverlight, который бы гарантированно вызывался в любом Silverlight процессе, чтобы поставить брякпоинт в нем. В случае с WPF/WinForms приложениям — все просто. Берем обработчик очереди Windows сообщений и ставим точку останова в нем. Но в Сильверлайте нет очереди Windows сообщений. К счастью, в нем есть близкий аналог: метод JoltHelper.FireEvent(). Этот метод вызывается даже в пустом Silverlight приложении, достаточно лишь пошевелить мышку.
Помните, я сказал, что консольный отладчик умел делать expression evaluation… иногда? На самом деле, чтобы выполнить код во время точки останова инфраструктура отладки требует ряда условий. Если метод оптимизирован ни о каком funceval'e и речи быть не может. JoltHelper.FireEvent(), конечно, оптимизирован:

Решение
Оказывается Silverlight jitter, как и его большой брат из мира .NET, прислушивается к .ini рекомендациям по оптимизации генерируемого кода. Достаточно создать следующий .INI файл рядом со сборкой:
[.NET Framework Debugging Control]
GenerateTrackingInfo=1
AllowOptimize=0Сохранить его как «Имя_сборки_без_dll.ini», и в следующий раз во время JIT-компиляции генерируемый код будет более дружелюбен к отладчику. Самый большой подвох заключается в кодировке .ini файла. Если она отличается от UTF-16 Little Endian — файл будет просто проигнорирован.
Для улучшения производительности, Silverlight, во время установки, генерирует образы в машинных кодах для стандартных сборок. По сути, тот же самый ngen, только утилит�� называется coregen, и результат генерации кладется в папку Silverlight'a. Coregen также принимает во внимание наличие .ini рекомендаций, и может генерировать пригодный для отладки машинный код.
Когда вы говорите:
slinject --installПрограмма создает System.Windows.ini файл с нужными рекомендациями jitter'у. удаляет старый прекомпилированный образ System.Windows.ni.dll, и просит coregen создать отлаживабельный образ. Ну и конечно, чтобы отменить все эти изменения, достаточно выполнить
slinject --uninstall
В заключение
Итак, мы получили универсальный внедритель сборок в Silverlight? Увы, нет. Даже убийство потока выглядит более безобидно, чем прерывание его в случайном месте с просьбой выполнить случайный код. Mike Stall в своем блоге рассказывает почему FuncEval — зло, но полезное зло. В конце концов, именно так работают окна Watches/Locals/Immediate в Visual Studio. Вооружившись этими знаниями, используя slinject, будьте готовы к падениям Silverlight процессов, крешам браузеров и обманам политиков.
Более того, я не эксперт в COM'e/ICorDebug'e. Наверняка я наделал кучу ляпов в коде, но ведь это открытый код — буду рад если вы покажете/исправите ошибки.
Надеюсь, вам понравилась эта прогулка в мир отладчика Silverlight — буду рад отзывам :).
