Как стать автором
Обновить

Внедрение кода в Silverlight

Время на прочтение 4 мин
Количество просмотров 2.4K
imageВ свободное время я занимаюсь разработкой Snoop'a. Это отличное подспорье WPF разработчикам, которое не имеет хороших бесплатных аналогов в мире Silverlight. Snoop внедряет свою сборку в WPF процесс и раскладывает его по полочкам.

Мне стало любопытно: можно ли внедрить произвольную сборку в исполняющийся сильверлайт процесс извне браузера? (можно) Об этом и пойдет речь под катом.



Насколько мне известно, все существующие программы, внедряющие код в сильверлайт, делают это посредством перехвата и модификации .xap файла до загрузки приложения. Это неплохой способ, однако он обладает рядом недостатков:

1. Нельзя подключиться к уже запущенному приложению без перезагрузки страницы
2. Перехват .xap файла зачастую ограничивает среду выполнения плагина Internet Explorer'ом. Для универсального перехватчика нужно использовать/писать http-сниффер.

Я же хочу предложить способ, который позволяет подключиться к уже запущенному приложению и внедриться в него без перезагрузки страницы, без модификации .xap файла и без ограничений на браузер. Прежде чем мы начнем, позвольте представить сегодняшнего open-source гостя:

slinject

Эта консольная программа — плод программистского любопытства. Она позволяет выбрать исполняющийся Silverlight процесс и внедрить в него Silverlight сборку (тыкните для большего разрешения):

Assembly injection example


Бинарники можно скачать здесь, а исходный код на C# доступен на github'e. Прежде чем вы начнете внедряться, программу нужно установить, выполнив команду:

slinject --install

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

Подождите, не уходите! Или как это работает

Как это часто бывает, идея очень простая. Подключиться отладчиком к Silverlight процессу и выполнить команду Assembly.Load(). Это всегда было можно сделать из отладчика Visual Studio, но можно ли это сделать из своей программы?

Сильверлайт устанавливается с рядом интересных библиотек и инструментов, которые, к сожалению, имеют либо отвратительную документацию, либо никакой. Одна из таких библиотек — dbgshim.dll. Именно через эту дверь ходят отладчики в мир Silverlight'a. Visual Studio использует ее, так почему бы и нам не воспользоваться лазейкой? Правда, почему бы и нет? Отстутвие хорошей документации нельзя назвать хорошим аргументом. А вот хорошим вызовом для любопытных — можно.

Итак, после нескольких ночей блуждания по царству Морфея пространству имен ICorDebug мне удалось написать консольную программу, которая умела быть простым отладчиком Silverlight процессов. Она умела реагировать на основные события отладчика, находить нужные функции, устанавливать брякпоинты и останавливаться на них. Иногда она даже умела делать expression evaluation (ака function evaluation, FuncEval), исполнять код то бишь. Увы, готовить кофе она так и не научилась.

Проблема

Нужно было найти метод в Silverlight, который бы гарантированно вызывался в любом Silverlight процессе, чтобы поставить брякпоинт в нем. В случае с WPF/WinForms приложениям — все просто. Берем обработчик очереди Windows сообщений и ставим точку останова в нем. Но в Сильверлайте нет очереди Windows сообщений. К счастью, в нем есть близкий аналог: метод JoltHelper.FireEvent(). Этот метод вызывается даже в пустом Silverlight приложении, достаточно лишь пошевелить мышку.

Помните, я сказал, что консольный отладчик умел делать expression evaluation… иногда? На самом деле, чтобы выполнить код во время точки останова инфраструктура отладки требует ряда условий. Если метод оптимизирован ни о каком funceval'e и речи быть не может. JoltHelper.FireEvent(), конечно, оптимизирован:

Optimized code disables FuncEval


Решение

Оказывается 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

So what?


В заключение

Итак, мы получили универсальный внедритель сборок в Silverlight? Увы, нет. Даже убийство потока выглядит более безобидно, чем прерывание его в случайном месте с просьбой выполнить случайный код. Mike Stall в своем блоге рассказывает почему FuncEval — зло, но полезное зло. В конце концов, именно так работают окна Watches/Locals/Immediate в Visual Studio. Вооружившись этими знаниями, используя slinject, будьте готовы к падениям Silverlight процессов, крешам браузеров и обманам политиков.

Более того, я не эксперт в COM'e/ICorDebug'e. Наверняка я наделал кучу ляпов в коде, но ведь это открытый код — буду рад если вы покажете/исправите ошибки.

Надеюсь, вам понравилась эта прогулка в мир отладчика Silverlight — буду рад отзывам :).
Теги:
Хабы:
+14
Комментарии 6
Комментарии Комментарии 6

Публикации

Истории

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн